1 /*
   2  * Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.net.www.protocol.http;
  27 
  28 import java.util.Arrays;
  29 import java.net.URL;
  30 import java.net.URLConnection;
  31 import java.net.ProtocolException;
  32 import java.net.HttpRetryException;
  33 import java.net.PasswordAuthentication;
  34 import java.net.Authenticator;
  35 import java.net.HttpCookie;
  36 import java.net.InetAddress;
  37 import java.net.UnknownHostException;
  38 import java.net.SocketTimeoutException;
  39 import java.net.SocketPermission;
  40 import java.net.Proxy;
  41 import java.net.ProxySelector;
  42 import java.net.URI;
  43 import java.net.InetSocketAddress;
  44 import java.net.CookieHandler;
  45 import java.net.ResponseCache;
  46 import java.net.CacheResponse;
  47 import java.net.SecureCacheResponse;
  48 import java.net.CacheRequest;
  49 import java.net.URLPermission;
  50 import java.net.Authenticator.RequestorType;
  51 import java.security.AccessController;
  52 import java.security.PrivilegedExceptionAction;
  53 import java.security.PrivilegedActionException;
  54 import java.io.*;
  55 import java.util.ArrayList;
  56 import java.util.Collections;
  57 import java.util.Date;
  58 import java.util.Map;
  59 import java.util.List;
  60 import java.util.Locale;
  61 import java.util.StringTokenizer;
  62 import java.util.Iterator;
  63 import java.util.HashSet;
  64 import java.util.HashMap;
  65 import java.util.Set;
  66 import java.util.StringJoiner;
  67 import jdk.internal.misc.JavaNetHttpCookieAccess;
  68 import jdk.internal.misc.SharedSecrets;
  69 import sun.net.*;
  70 import sun.net.www.*;
  71 import sun.net.www.http.HttpClient;
  72 import sun.net.www.http.PosterOutputStream;
  73 import sun.net.www.http.ChunkedInputStream;
  74 import sun.net.www.http.ChunkedOutputStream;
  75 import sun.util.logging.PlatformLogger;
  76 import java.text.SimpleDateFormat;
  77 import java.util.TimeZone;
  78 import java.net.MalformedURLException;
  79 import java.nio.ByteBuffer;
  80 import java.util.Properties;
  81 import static sun.net.www.protocol.http.AuthScheme.BASIC;
  82 import static sun.net.www.protocol.http.AuthScheme.DIGEST;
  83 import static sun.net.www.protocol.http.AuthScheme.NTLM;
  84 import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;
  85 import static sun.net.www.protocol.http.AuthScheme.KERBEROS;
  86 import static sun.net.www.protocol.http.AuthScheme.UNKNOWN;
  87 import sun.security.action.GetIntegerAction;
  88 import sun.security.action.GetPropertyAction;
  89 
  90 /**
  91  * A class to represent an HTTP connection to a remote object.
  92  */
  93 
  94 
  95 public class HttpURLConnection extends java.net.HttpURLConnection {
  96 
  97     static String HTTP_CONNECT = "CONNECT";
  98 
  99     static final String version;
 100     public static final String userAgent;
 101 
 102     /* max # of allowed re-directs */
 103     static final int defaultmaxRedirects = 20;
 104     static final int maxRedirects;
 105 
 106     /* Not all servers support the (Proxy)-Authentication-Info headers.
 107      * By default, we don't require them to be sent
 108      */
 109     static final boolean validateProxy;
 110     static final boolean validateServer;
 111 
 112     private StreamingOutputStream strOutputStream;
 113     private static final String RETRY_MSG1 =
 114         "cannot retry due to proxy authentication, in streaming mode";
 115     private static final String RETRY_MSG2 =
 116         "cannot retry due to server authentication, in streaming mode";
 117     private static final String RETRY_MSG3 =
 118         "cannot retry due to redirection, in streaming mode";
 119 
 120     /*
 121      * System properties related to error stream handling:
 122      *
 123      * sun.net.http.errorstream.enableBuffering = <boolean>
 124      *
 125      * With the above system property set to true (default is false),
 126      * when the response code is >=400, the HTTP handler will try to
 127      * buffer the response body (up to a certain amount and within a
 128      * time limit). Thus freeing up the underlying socket connection
 129      * for reuse. The rationale behind this is that usually when the
 130      * server responds with a >=400 error (client error or server
 131      * error, such as 404 file not found), the server will send a
 132      * small response body to explain who to contact and what to do to
 133      * recover. With this property set to true, even if the
 134      * application doesn't call getErrorStream(), read the response
 135      * body, and then call close(), the underlying socket connection
 136      * can still be kept-alive and reused. The following two system
 137      * properties provide further control to the error stream
 138      * buffering behaviour.
 139      *
 140      * sun.net.http.errorstream.timeout = <int>
 141      *     the timeout (in millisec) waiting the error stream
 142      *     to be buffered; default is 300 ms
 143      *
 144      * sun.net.http.errorstream.bufferSize = <int>
 145      *     the size (in bytes) to use for the buffering the error stream;
 146      *     default is 4k
 147      */
 148 
 149 
 150     /* Should we enable buffering of error streams? */
 151     private static boolean enableESBuffer = false;
 152 
 153     /* timeout waiting for read for buffered error stream;
 154      */
 155     private static int timeout4ESBuffer = 0;
 156 
 157     /* buffer size for buffered error stream;
 158     */
 159     private static int bufSize4ES = 0;
 160 
 161     /*
 162      * Restrict setting of request headers through the public api
 163      * consistent with JavaScript XMLHttpRequest2 with a few
 164      * exceptions. Disallowed headers are silently ignored for
 165      * backwards compatibility reasons rather than throwing a
 166      * SecurityException. For example, some applets set the
 167      * Host header since old JREs did not implement HTTP 1.1.
 168      * Additionally, any header starting with Sec- is
 169      * disallowed.
 170      *
 171      * The following headers are allowed for historical reasons:
 172      *
 173      * Accept-Charset, Accept-Encoding, Cookie, Cookie2, Date,
 174      * Referer, TE, User-Agent, headers beginning with Proxy-.
 175      *
 176      * The following headers are allowed in a limited form:
 177      *
 178      * Connection: close
 179      *
 180      * See http://www.w3.org/TR/XMLHttpRequest2.
 181      */
 182     private static final boolean allowRestrictedHeaders;
 183     private static final Set<String> restrictedHeaderSet;
 184     private static final String[] restrictedHeaders = {
 185         /* Restricted by XMLHttpRequest2 */
 186         //"Accept-Charset",
 187         //"Accept-Encoding",
 188         "Access-Control-Request-Headers",
 189         "Access-Control-Request-Method",
 190         "Connection", /* close is allowed */
 191         "Content-Length",
 192         //"Cookie",
 193         //"Cookie2",
 194         "Content-Transfer-Encoding",
 195         //"Date",
 196         //"Expect",
 197         "Host",
 198         "Keep-Alive",
 199         "Origin",
 200         // "Referer",
 201         // "TE",
 202         "Trailer",
 203         "Transfer-Encoding",
 204         "Upgrade",
 205         //"User-Agent",
 206         "Via"
 207     };
 208 
 209     static {
 210         Properties props = GetPropertyAction.getProperties();
 211         maxRedirects = GetIntegerAction.getProperty("http.maxRedirects",
 212                         defaultmaxRedirects);
 213         version = props.getProperty("java.version");
 214         String agent = props.getProperty("http.agent");
 215         if (agent == null) {
 216             agent = "Java/"+version;
 217         } else {
 218             agent = agent + " Java/"+version;
 219         }
 220         userAgent = agent;
 221         validateProxy = Boolean.parseBoolean(
 222                 props.getProperty("http.auth.digest.validateProxy"));
 223         validateServer = Boolean.parseBoolean(
 224                 props.getProperty("http.auth.digest.validateServer"));
 225 
 226         enableESBuffer = Boolean.parseBoolean(
 227                 props.getProperty("sun.net.http.errorstream.enableBuffering"));
 228         timeout4ESBuffer = GetIntegerAction
 229                 .getProperty("sun.net.http.errorstream.timeout", 300);
 230         if (timeout4ESBuffer <= 0) {
 231             timeout4ESBuffer = 300; // use the default
 232         }
 233 
 234         bufSize4ES = GetIntegerAction
 235                 .getProperty("sun.net.http.errorstream.bufferSize", 4096);
 236         if (bufSize4ES <= 0) {
 237             bufSize4ES = 4096; // use the default
 238         }
 239 
 240         allowRestrictedHeaders = Boolean.parseBoolean(
 241                 props.getProperty("sun.net.http.allowRestrictedHeaders"));
 242         if (!allowRestrictedHeaders) {
 243             restrictedHeaderSet = new HashSet<>(restrictedHeaders.length);
 244             for (int i=0; i < restrictedHeaders.length; i++) {
 245                 restrictedHeaderSet.add(restrictedHeaders[i].toLowerCase());
 246             }
 247         } else {
 248             restrictedHeaderSet = null;
 249         }
 250     }
 251 
 252     static final String httpVersion = "HTTP/1.1";
 253     static final String acceptString =
 254         "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";
 255 
 256     // the following http request headers should NOT have their values
 257     // returned for security reasons.
 258     private static final String[] EXCLUDE_HEADERS = {
 259             "Proxy-Authorization",
 260             "Authorization"
 261     };
 262 
 263     // also exclude system cookies when any might be set
 264     private static final String[] EXCLUDE_HEADERS2= {
 265             "Proxy-Authorization",
 266             "Authorization",
 267             "Cookie",
 268             "Cookie2"
 269     };
 270 
 271     protected HttpClient http;
 272     protected Handler handler;
 273     protected Proxy instProxy;
 274 
 275     private CookieHandler cookieHandler;
 276     private final ResponseCache cacheHandler;
 277 
 278     // the cached response, and cached response headers and body
 279     protected CacheResponse cachedResponse;
 280     private MessageHeader cachedHeaders;
 281     private InputStream cachedInputStream;
 282 
 283     /* output stream to server */
 284     protected PrintStream ps = null;
 285 
 286 
 287     /* buffered error stream */
 288     private InputStream errorStream = null;
 289 
 290     /* User set Cookies */
 291     private boolean setUserCookies = true;
 292     private String userCookies = null;
 293     private String userCookies2 = null;
 294 
 295     /* We only have a single static authenticator for now.
 296      * REMIND:  backwards compatibility with JDK 1.1.  Should be
 297      * eliminated for JDK 2.0.
 298      */
 299     @Deprecated
 300     private static HttpAuthenticator defaultAuth;
 301 
 302     /* all the headers we send
 303      * NOTE: do *NOT* dump out the content of 'requests' in the
 304      * output or stacktrace since it may contain security-sensitive
 305      * headers such as those defined in EXCLUDE_HEADERS.
 306      */
 307     private MessageHeader requests;
 308 
 309     /* The headers actually set by the user are recorded here also
 310      */
 311     private MessageHeader userHeaders;
 312 
 313     /* Headers and request method cannot be changed
 314      * once this flag is set in :-
 315      *     - getOutputStream()
 316      *     - getInputStream())
 317      *     - connect()
 318      * Access synchronized on this.
 319      */
 320     private boolean connecting = false;
 321 
 322     /* The following two fields are only used with Digest Authentication */
 323     String domain;      /* The list of authentication domains */
 324     DigestAuthentication.Parameters digestparams;
 325 
 326     /* Current credentials in use */
 327     AuthenticationInfo  currentProxyCredentials = null;
 328     AuthenticationInfo  currentServerCredentials = null;
 329     boolean             needToCheck = true;
 330     private boolean doingNTLM2ndStage = false; /* doing the 2nd stage of an NTLM server authentication */
 331     private boolean doingNTLMp2ndStage = false; /* doing the 2nd stage of an NTLM proxy authentication */
 332 
 333     /* try auth without calling Authenticator. Used for transparent NTLM authentication */
 334     private boolean tryTransparentNTLMServer = true;
 335     private boolean tryTransparentNTLMProxy = true;
 336     private boolean useProxyResponseCode = false;
 337 
 338     /* Used by Windows specific code */
 339     private Object authObj;
 340 
 341     /* Set if the user is manually setting the Authorization or Proxy-Authorization headers */
 342     boolean isUserServerAuth;
 343     boolean isUserProxyAuth;
 344 
 345     String serverAuthKey, proxyAuthKey;
 346 
 347     /* Progress source */
 348     protected ProgressSource pi;
 349 
 350     /* all the response headers we get back */
 351     private MessageHeader responses;
 352     /* the stream _from_ the server */
 353     private InputStream inputStream = null;
 354     /* post stream _to_ the server, if any */
 355     private PosterOutputStream poster = null;
 356 
 357     /* Indicates if the std. request headers have been set in requests. */
 358     private boolean setRequests=false;
 359 
 360     /* Indicates whether a request has already failed or not */
 361     private boolean failedOnce=false;
 362 
 363     /* Remembered Exception, we will throw it again if somebody
 364        calls getInputStream after disconnect */
 365     private Exception rememberedException = null;
 366 
 367     /* If we decide we want to reuse a client, we put it here */
 368     private HttpClient reuseClient = null;
 369 
 370     /* Tunnel states */
 371     public enum TunnelState {
 372         /* No tunnel */
 373         NONE,
 374 
 375         /* Setting up a tunnel */
 376         SETUP,
 377 
 378         /* Tunnel has been successfully setup */
 379         TUNNELING
 380     }
 381 
 382     private TunnelState tunnelState = TunnelState.NONE;
 383 
 384     /* Redefine timeouts from java.net.URLConnection as we need -1 to mean
 385      * not set. This is to ensure backward compatibility.
 386      */
 387     private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT;
 388     private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT;
 389 
 390     /* A permission converted from a URLPermission */
 391     private SocketPermission socketPermission;
 392 
 393     /* Logging support */
 394     private static final PlatformLogger logger =
 395             PlatformLogger.getLogger("sun.net.www.protocol.http.HttpURLConnection");
 396 
 397     /*
 398      * privileged request password authentication
 399      *
 400      */
 401     private static PasswordAuthentication
 402     privilegedRequestPasswordAuthentication(
 403                             final String host,
 404                             final InetAddress addr,
 405                             final int port,
 406                             final String protocol,
 407                             final String prompt,
 408                             final String scheme,
 409                             final URL url,
 410                             final RequestorType authType) {
 411         return java.security.AccessController.doPrivileged(
 412             new java.security.PrivilegedAction<>() {
 413                 public PasswordAuthentication run() {
 414                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
 415                         logger.finest("Requesting Authentication: host =" + host + " url = " + url);
 416                     }
 417                     PasswordAuthentication pass = Authenticator.requestPasswordAuthentication(
 418                         host, addr, port, protocol,
 419                         prompt, scheme, url, authType);
 420                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
 421                         logger.finest("Authentication returned: " + (pass != null ? pass.toString() : "null"));
 422                     }
 423                     return pass;
 424                 }
 425             });
 426     }
 427 
 428     private boolean isRestrictedHeader(String key, String value) {
 429         if (allowRestrictedHeaders) {
 430             return false;
 431         }
 432 
 433         key = key.toLowerCase();
 434         if (restrictedHeaderSet.contains(key)) {
 435             /*
 436              * Exceptions to restricted headers:
 437              *
 438              * Allow "Connection: close".
 439              */
 440             if (key.equals("connection") && value.equalsIgnoreCase("close")) {
 441                 return false;
 442             }
 443             return true;
 444         } else if (key.startsWith("sec-")) {
 445             return true;
 446         }
 447         return false;
 448     }
 449 
 450     /*
 451      * Checks the validity of http message header and whether the header
 452      * is restricted and throws IllegalArgumentException if invalid or
 453      * restricted.
 454      */
 455     private boolean isExternalMessageHeaderAllowed(String key, String value) {
 456         checkMessageHeader(key, value);
 457         if (!isRestrictedHeader(key, value)) {
 458             return true;
 459         }
 460         return false;
 461     }
 462 
 463     /* Logging support */
 464     public static PlatformLogger getHttpLogger() {
 465         return logger;
 466     }
 467 
 468     /* Used for Windows NTLM implementation */
 469     public Object authObj() {
 470         return authObj;
 471     }
 472 
 473     public void authObj(Object authObj) {
 474         this.authObj = authObj;
 475     }
 476 
 477     /*
 478      * checks the validity of http message header and throws
 479      * IllegalArgumentException if invalid.
 480      */
 481     private void checkMessageHeader(String key, String value) {
 482         char LF = '\n';
 483         int index = key.indexOf(LF);
 484         int index1 = key.indexOf(':');
 485         if (index != -1 || index1 != -1) {
 486             throw new IllegalArgumentException(
 487                 "Illegal character(s) in message header field: " + key);
 488         }
 489         else {
 490             if (value == null) {
 491                 return;
 492             }
 493 
 494             index = value.indexOf(LF);
 495             while (index != -1) {
 496                 index++;
 497                 if (index < value.length()) {
 498                     char c = value.charAt(index);
 499                     if ((c==' ') || (c=='\t')) {
 500                         // ok, check the next occurrence
 501                         index = value.indexOf(LF, index);
 502                         continue;
 503                     }
 504                 }
 505                 throw new IllegalArgumentException(
 506                     "Illegal character(s) in message header value: " + value);
 507             }
 508         }
 509     }
 510 
 511     public synchronized void setRequestMethod(String method)
 512                         throws ProtocolException {
 513         if (connecting) {
 514             throw new IllegalStateException("connect in progress");
 515         }
 516         super.setRequestMethod(method);
 517     }
 518 
 519     /* adds the standard key/val pairs to reqests if necessary & write to
 520      * given PrintStream
 521      */
 522     private void writeRequests() throws IOException {
 523         /* print all message headers in the MessageHeader
 524          * onto the wire - all the ones we've set and any
 525          * others that have been set
 526          */
 527         // send any pre-emptive authentication
 528         if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) {
 529             setPreemptiveProxyAuthentication(requests);
 530         }
 531         if (!setRequests) {
 532 
 533             /* We're very particular about the order in which we
 534              * set the request headers here.  The order should not
 535              * matter, but some careless CGI programs have been
 536              * written to expect a very particular order of the
 537              * standard headers.  To name names, the order in which
 538              * Navigator3.0 sends them.  In particular, we make *sure*
 539              * to send Content-type: <> and Content-length:<> second
 540              * to last and last, respectively, in the case of a POST
 541              * request.
 542              */
 543             if (!failedOnce) {
 544                 checkURLFile();
 545                 requests.prepend(method + " " + getRequestURI()+" "  +
 546                                  httpVersion, null);
 547             }
 548             if (!getUseCaches()) {
 549                 requests.setIfNotSet ("Cache-Control", "no-cache");
 550                 requests.setIfNotSet ("Pragma", "no-cache");
 551             }
 552             requests.setIfNotSet("User-Agent", userAgent);
 553             int port = url.getPort();
 554             String host = stripIPv6ZoneId(url.getHost());
 555             if (port != -1 && port != url.getDefaultPort()) {
 556                 host += ":" + String.valueOf(port);
 557             }
 558             String reqHost = requests.findValue("Host");
 559             if (reqHost == null ||
 560                 (!reqHost.equalsIgnoreCase(host) && !checkSetHost()))
 561             {
 562                 requests.set("Host", host);
 563             }
 564             requests.setIfNotSet("Accept", acceptString);
 565 
 566             /*
 567              * For HTTP/1.1 the default behavior is to keep connections alive.
 568              * However, we may be talking to a 1.0 server so we should set
 569              * keep-alive just in case, except if we have encountered an error
 570              * or if keep alive is disabled via a system property
 571              */
 572 
 573             // Try keep-alive only on first attempt
 574             if (!failedOnce && http.getHttpKeepAliveSet()) {
 575                 if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) {
 576                     requests.setIfNotSet("Proxy-Connection", "keep-alive");
 577                 } else {
 578                     requests.setIfNotSet("Connection", "keep-alive");
 579                 }
 580             } else {
 581                 /*
 582                  * RFC 2616 HTTP/1.1 section 14.10 says:
 583                  * HTTP/1.1 applications that do not support persistent
 584                  * connections MUST include the "close" connection option
 585                  * in every message
 586                  */
 587                 requests.setIfNotSet("Connection", "close");
 588             }
 589             // Set modified since if necessary
 590             long modTime = getIfModifiedSince();
 591             if (modTime != 0 ) {
 592                 Date date = new Date(modTime);
 593                 //use the preferred date format according to RFC 2068(HTTP1.1),
 594                 // RFC 822 and RFC 1123
 595                 SimpleDateFormat fo =
 596                   new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
 597                 fo.setTimeZone(TimeZone.getTimeZone("GMT"));
 598                 requests.setIfNotSet("If-Modified-Since", fo.format(date));
 599             }
 600             // check for preemptive authorization
 601             AuthenticationInfo sauth = AuthenticationInfo.getServerAuth(url);
 602             if (sauth != null && sauth.supportsPreemptiveAuthorization() ) {
 603                 // Sets "Authorization"
 604                 requests.setIfNotSet(sauth.getHeaderName(), sauth.getHeaderValue(url,method));
 605                 currentServerCredentials = sauth;
 606             }
 607 
 608             if (!method.equals("PUT") && (poster != null || streaming())) {
 609                 requests.setIfNotSet ("Content-type",
 610                         "application/x-www-form-urlencoded");
 611             }
 612 
 613             boolean chunked = false;
 614 
 615             if (streaming()) {
 616                 if (chunkLength != -1) {
 617                     requests.set ("Transfer-Encoding", "chunked");
 618                     chunked = true;
 619                 } else { /* fixed content length */
 620                     if (fixedContentLengthLong != -1) {
 621                         requests.set ("Content-Length",
 622                                       String.valueOf(fixedContentLengthLong));
 623                     } else if (fixedContentLength != -1) {
 624                         requests.set ("Content-Length",
 625                                       String.valueOf(fixedContentLength));
 626                     }
 627                 }
 628             } else if (poster != null) {
 629                 /* add Content-Length & POST/PUT data */
 630                 synchronized (poster) {
 631                     /* close it, so no more data can be added */
 632                     poster.close();
 633                     requests.set("Content-Length",
 634                                  String.valueOf(poster.size()));
 635                 }
 636             }
 637 
 638             if (!chunked) {
 639                 if (requests.findValue("Transfer-Encoding") != null) {
 640                     requests.remove("Transfer-Encoding");
 641                     if (logger.isLoggable(PlatformLogger.Level.WARNING)) {
 642                         logger.warning(
 643                             "use streaming mode for chunked encoding");
 644                     }
 645                 }
 646             }
 647 
 648             // get applicable cookies based on the uri and request headers
 649             // add them to the existing request headers
 650             setCookieHeader();
 651 
 652             setRequests=true;
 653         }
 654         if (logger.isLoggable(PlatformLogger.Level.FINE)) {
 655             logger.fine(requests.toString());
 656         }
 657         http.writeRequests(requests, poster, streaming());
 658         if (ps.checkError()) {
 659             String proxyHost = http.getProxyHostUsed();
 660             int proxyPort = http.getProxyPortUsed();
 661             disconnectInternal();
 662             if (failedOnce) {
 663                 throw new IOException("Error writing to server");
 664             } else { // try once more
 665                 failedOnce=true;
 666                 if (proxyHost != null) {
 667                     setProxiedClient(url, proxyHost, proxyPort);
 668                 } else {
 669                     setNewClient (url);
 670                 }
 671                 ps = (PrintStream) http.getOutputStream();
 672                 connected=true;
 673                 responses = new MessageHeader();
 674                 setRequests=false;
 675                 writeRequests();
 676             }
 677         }
 678     }
 679 
 680     private boolean checkSetHost() {
 681         SecurityManager s = System.getSecurityManager();
 682         if (s != null) {
 683             String name = s.getClass().getName();
 684             if (name.equals("sun.plugin2.applet.AWTAppletSecurityManager") ||
 685                 name.equals("sun.plugin2.applet.FXAppletSecurityManager") ||
 686                 name.equals("com.sun.javaws.security.JavaWebStartSecurity") ||
 687                 name.equals("sun.plugin.security.ActivatorSecurityManager"))
 688             {
 689                 int CHECK_SET_HOST = -2;
 690                 try {
 691                     s.checkConnect(url.toExternalForm(), CHECK_SET_HOST);
 692                 } catch (SecurityException ex) {
 693                     return false;
 694                 }
 695             }
 696         }
 697         return true;
 698     }
 699 
 700     private void checkURLFile() {
 701         SecurityManager s = System.getSecurityManager();
 702         if (s != null) {
 703             String name = s.getClass().getName();
 704             if (name.equals("sun.plugin2.applet.AWTAppletSecurityManager") ||
 705                 name.equals("sun.plugin2.applet.FXAppletSecurityManager") ||
 706                 name.equals("com.sun.javaws.security.JavaWebStartSecurity") ||
 707                 name.equals("sun.plugin.security.ActivatorSecurityManager"))
 708             {
 709                 int CHECK_SUBPATH = -3;
 710                 try {
 711                     s.checkConnect(url.toExternalForm(), CHECK_SUBPATH);
 712                 } catch (SecurityException ex) {
 713                     throw new SecurityException("denied access outside a permitted URL subpath", ex);
 714                 }
 715             }
 716         }
 717     }
 718 
 719     /**
 720      * Create a new HttpClient object, bypassing the cache of
 721      * HTTP client objects/connections.
 722      *
 723      * @param url       the URL being accessed
 724      */
 725     protected void setNewClient (URL url)
 726     throws IOException {
 727         setNewClient(url, false);
 728     }
 729 
 730     /**
 731      * Obtain a HttpsClient object. Use the cached copy if specified.
 732      *
 733      * @param url       the URL being accessed
 734      * @param useCache  whether the cached connection should be used
 735      *        if present
 736      */
 737     protected void setNewClient (URL url, boolean useCache)
 738         throws IOException {
 739         http = HttpClient.New(url, null, -1, useCache, connectTimeout, this);
 740         http.setReadTimeout(readTimeout);
 741     }
 742 
 743 
 744     /**
 745      * Create a new HttpClient object, set up so that it uses
 746      * per-instance proxying to the given HTTP proxy.  This
 747      * bypasses the cache of HTTP client objects/connections.
 748      *
 749      * @param url       the URL being accessed
 750      * @param proxyHost the proxy host to use
 751      * @param proxyPort the proxy port to use
 752      */
 753     protected void setProxiedClient (URL url, String proxyHost, int proxyPort)
 754     throws IOException {
 755         setProxiedClient(url, proxyHost, proxyPort, false);
 756     }
 757 
 758     /**
 759      * Obtain a HttpClient object, set up so that it uses per-instance
 760      * proxying to the given HTTP proxy. Use the cached copy of HTTP
 761      * client objects/connections if specified.
 762      *
 763      * @param url       the URL being accessed
 764      * @param proxyHost the proxy host to use
 765      * @param proxyPort the proxy port to use
 766      * @param useCache  whether the cached connection should be used
 767      *        if present
 768      */
 769     protected void setProxiedClient (URL url,
 770                                            String proxyHost, int proxyPort,
 771                                            boolean useCache)
 772         throws IOException {
 773         proxiedConnect(url, proxyHost, proxyPort, useCache);
 774     }
 775 
 776     protected void proxiedConnect(URL url,
 777                                            String proxyHost, int proxyPort,
 778                                            boolean useCache)
 779         throws IOException {
 780         http = HttpClient.New (url, proxyHost, proxyPort, useCache,
 781             connectTimeout, this);
 782         http.setReadTimeout(readTimeout);
 783     }
 784 
 785     protected HttpURLConnection(URL u, Handler handler)
 786     throws IOException {
 787         // we set proxy == null to distinguish this case with the case
 788         // when per connection proxy is set
 789         this(u, null, handler);
 790     }
 791 
 792     public HttpURLConnection(URL u, String host, int port) {
 793         this(u, new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(host, port)));
 794     }
 795 
 796     /** this constructor is used by other protocol handlers such as ftp
 797         that want to use http to fetch urls on their behalf.*/
 798     public HttpURLConnection(URL u, Proxy p) {
 799         this(u, p, new Handler());
 800     }
 801 
 802     protected HttpURLConnection(URL u, Proxy p, Handler handler) {
 803         super(u);
 804         requests = new MessageHeader();
 805         responses = new MessageHeader();
 806         userHeaders = new MessageHeader();
 807         this.handler = handler;
 808         instProxy = p;
 809         if (instProxy instanceof sun.net.ApplicationProxy) {
 810             /* Application set Proxies should not have access to cookies
 811              * in a secure environment unless explicitly allowed. */
 812             try {
 813                 cookieHandler = CookieHandler.getDefault();
 814             } catch (SecurityException se) { /* swallow exception */ }
 815         } else {
 816             cookieHandler = java.security.AccessController.doPrivileged(
 817                 new java.security.PrivilegedAction<>() {
 818                 public CookieHandler run() {
 819                     return CookieHandler.getDefault();
 820                 }
 821             });
 822         }
 823         cacheHandler = java.security.AccessController.doPrivileged(
 824             new java.security.PrivilegedAction<>() {
 825                 public ResponseCache run() {
 826                 return ResponseCache.getDefault();
 827             }
 828         });
 829     }
 830 
 831     /**
 832      * @deprecated.  Use java.net.Authenticator.setDefault() instead.
 833      */
 834     @Deprecated
 835     public static void setDefaultAuthenticator(HttpAuthenticator a) {
 836         defaultAuth = a;
 837     }
 838 
 839     /**
 840      * opens a stream allowing redirects only to the same host.
 841      */
 842     public static InputStream openConnectionCheckRedirects(URLConnection c)
 843         throws IOException
 844     {
 845         boolean redir;
 846         int redirects = 0;
 847         InputStream in;
 848 
 849         do {
 850             if (c instanceof HttpURLConnection) {
 851                 ((HttpURLConnection) c).setInstanceFollowRedirects(false);
 852             }
 853 
 854             // We want to open the input stream before
 855             // getting headers, because getHeaderField()
 856             // et al swallow IOExceptions.
 857             in = c.getInputStream();
 858             redir = false;
 859 
 860             if (c instanceof HttpURLConnection) {
 861                 HttpURLConnection http = (HttpURLConnection) c;
 862                 int stat = http.getResponseCode();
 863                 if (stat >= 300 && stat <= 307 && stat != 306 &&
 864                         stat != HttpURLConnection.HTTP_NOT_MODIFIED) {
 865                     URL base = http.getURL();
 866                     String loc = http.getHeaderField("Location");
 867                     URL target = null;
 868                     if (loc != null) {
 869                         target = new URL(base, loc);
 870                     }
 871                     http.disconnect();
 872                     if (target == null
 873                         || !base.getProtocol().equals(target.getProtocol())
 874                         || base.getPort() != target.getPort()
 875                         || !hostsEqual(base, target)
 876                         || redirects >= 5)
 877                     {
 878                         throw new SecurityException("illegal URL redirect");
 879                     }
 880                     redir = true;
 881                     c = target.openConnection();
 882                     redirects++;
 883                 }
 884             }
 885         } while (redir);
 886         return in;
 887     }
 888 
 889 
 890     //
 891     // Same as java.net.URL.hostsEqual
 892     //
 893     private static boolean hostsEqual(URL u1, URL u2) {
 894         final String h1 = u1.getHost();
 895         final String h2 = u2.getHost();
 896 
 897         if (h1 == null) {
 898             return h2 == null;
 899         } else if (h2 == null) {
 900             return false;
 901         } else if (h1.equalsIgnoreCase(h2)) {
 902             return true;
 903         }
 904         // Have to resolve addresses before comparing, otherwise
 905         // names like tachyon and tachyon.eng would compare different
 906         final boolean result[] = {false};
 907 
 908         java.security.AccessController.doPrivileged(
 909             new java.security.PrivilegedAction<>() {
 910                 public Void run() {
 911                 try {
 912                     InetAddress a1 = InetAddress.getByName(h1);
 913                     InetAddress a2 = InetAddress.getByName(h2);
 914                     result[0] = a1.equals(a2);
 915                 } catch(UnknownHostException | SecurityException e) {
 916                 }
 917                 return null;
 918             }
 919         });
 920 
 921         return result[0];
 922     }
 923 
 924     // overridden in HTTPS subclass
 925 
 926     public void connect() throws IOException {
 927         synchronized (this) {
 928             connecting = true;
 929         }
 930         plainConnect();
 931     }
 932 
 933     private boolean checkReuseConnection () {
 934         if (connected) {
 935             return true;
 936         }
 937         if (reuseClient != null) {
 938             http = reuseClient;
 939             http.setReadTimeout(getReadTimeout());
 940             http.reuse = false;
 941             reuseClient = null;
 942             connected = true;
 943             return true;
 944         }
 945         return false;
 946     }
 947 
 948     private String getHostAndPort(URL url) {
 949         String host = url.getHost();
 950         final String hostarg = host;
 951         try {
 952             // lookup hostname and use IP address if available
 953             host = AccessController.doPrivileged(
 954                 new PrivilegedExceptionAction<>() {
 955                     public String run() throws IOException {
 956                             InetAddress addr = InetAddress.getByName(hostarg);
 957                             return addr.getHostAddress();
 958                     }
 959                 }
 960             );
 961         } catch (PrivilegedActionException e) {}
 962         int port = url.getPort();
 963         if (port == -1) {
 964             String scheme = url.getProtocol();
 965             if ("http".equals(scheme)) {
 966                 return host + ":80";
 967             } else { // scheme must be https
 968                 return host + ":443";
 969             }
 970         }
 971         return host + ":" + Integer.toString(port);
 972     }
 973 
 974     protected void plainConnect()  throws IOException {
 975         synchronized (this) {
 976             if (connected) {
 977                 return;
 978             }
 979         }
 980         SocketPermission p = URLtoSocketPermission(this.url);
 981         if (p != null) {
 982             try {
 983                 AccessController.doPrivilegedWithCombiner(
 984                     new PrivilegedExceptionAction<>() {
 985                         public Void run() throws IOException {
 986                             plainConnect0();
 987                             return null;
 988                         }
 989                     }, null, p
 990                 );
 991             } catch (PrivilegedActionException e) {
 992                     throw (IOException) e.getException();
 993             }
 994         } else {
 995             // run without additional permission
 996             plainConnect0();
 997         }
 998     }
 999 
1000     /**
1001      *  if the caller has a URLPermission for connecting to the
1002      *  given URL, then return a SocketPermission which permits
1003      *  access to that destination. Return null otherwise. The permission
1004      *  is cached in a field (which can only be changed by redirects)
1005      */
1006     SocketPermission URLtoSocketPermission(URL url) throws IOException {
1007 
1008         if (socketPermission != null) {
1009             return socketPermission;
1010         }
1011 
1012         SecurityManager sm = System.getSecurityManager();
1013 
1014         if (sm == null) {
1015             return null;
1016         }
1017 
1018         // the permission, which we might grant
1019 
1020         SocketPermission newPerm = new SocketPermission(
1021             getHostAndPort(url), "connect"
1022         );
1023 
1024         String actions = getRequestMethod()+":" +
1025                 getUserSetHeaders().getHeaderNamesInList();
1026 
1027         String urlstring = url.getProtocol() + "://" + url.getAuthority()
1028                 + url.getPath();
1029 
1030         URLPermission p = new URLPermission(urlstring, actions);
1031         try {
1032             sm.checkPermission(p);
1033             socketPermission = newPerm;
1034             return socketPermission;
1035         } catch (SecurityException e) {
1036             // fall thru
1037         }
1038         return null;
1039     }
1040 
1041     protected void plainConnect0()  throws IOException {
1042         // try to see if request can be served from local cache
1043         if (cacheHandler != null && getUseCaches()) {
1044             try {
1045                 URI uri = ParseUtil.toURI(url);
1046                 if (uri != null) {
1047                     cachedResponse = cacheHandler.get(uri, getRequestMethod(), getUserSetHeaders().getHeaders());
1048                     if ("https".equalsIgnoreCase(uri.getScheme())
1049                         && !(cachedResponse instanceof SecureCacheResponse)) {
1050                         cachedResponse = null;
1051                     }
1052                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1053                         logger.finest("Cache Request for " + uri + " / " + getRequestMethod());
1054                         logger.finest("From cache: " + (cachedResponse != null ? cachedResponse.toString() : "null"));
1055                     }
1056                     if (cachedResponse != null) {
1057                         cachedHeaders = mapToMessageHeader(cachedResponse.getHeaders());
1058                         cachedInputStream = cachedResponse.getBody();
1059                     }
1060                 }
1061             } catch (IOException ioex) {
1062                 // ignore and commence normal connection
1063             }
1064             if (cachedHeaders != null && cachedInputStream != null) {
1065                 connected = true;
1066                 return;
1067             } else {
1068                 cachedResponse = null;
1069             }
1070         }
1071         try {
1072             /* Try to open connections using the following scheme,
1073              * return on the first one that's successful:
1074              * 1) if (instProxy != null)
1075              *        connect to instProxy; raise exception if failed
1076              * 2) else use system default ProxySelector
1077              * 3) is 2) fails, make direct connection
1078              */
1079 
1080             if (instProxy == null) { // no instance Proxy is set
1081                 /**
1082                  * Do we have to use a proxy?
1083                  */
1084                 ProxySelector sel =
1085                     java.security.AccessController.doPrivileged(
1086                         new java.security.PrivilegedAction<>() {
1087                             public ProxySelector run() {
1088                                      return ProxySelector.getDefault();
1089                                  }
1090                              });
1091                 if (sel != null) {
1092                     URI uri = sun.net.www.ParseUtil.toURI(url);
1093                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1094                         logger.finest("ProxySelector Request for " + uri);
1095                     }
1096                     Iterator<Proxy> it = sel.select(uri).iterator();
1097                     Proxy p;
1098                     while (it.hasNext()) {
1099                         p = it.next();
1100                         try {
1101                             if (!failedOnce) {
1102                                 http = getNewHttpClient(url, p, connectTimeout);
1103                                 http.setReadTimeout(readTimeout);
1104                             } else {
1105                                 // make sure to construct new connection if first
1106                                 // attempt failed
1107                                 http = getNewHttpClient(url, p, connectTimeout, false);
1108                                 http.setReadTimeout(readTimeout);
1109                             }
1110                             if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1111                                 if (p != null) {
1112                                     logger.finest("Proxy used: " + p.toString());
1113                                 }
1114                             }
1115                             break;
1116                         } catch (IOException ioex) {
1117                             if (p != Proxy.NO_PROXY) {
1118                                 sel.connectFailed(uri, p.address(), ioex);
1119                                 if (!it.hasNext()) {
1120                                     // fallback to direct connection
1121                                     http = getNewHttpClient(url, null, connectTimeout, false);
1122                                     http.setReadTimeout(readTimeout);
1123                                     break;
1124                                 }
1125                             } else {
1126                                 throw ioex;
1127                             }
1128                             continue;
1129                         }
1130                     }
1131                 } else {
1132                     // No proxy selector, create http client with no proxy
1133                     if (!failedOnce) {
1134                         http = getNewHttpClient(url, null, connectTimeout);
1135                         http.setReadTimeout(readTimeout);
1136                     } else {
1137                         // make sure to construct new connection if first
1138                         // attempt failed
1139                         http = getNewHttpClient(url, null, connectTimeout, false);
1140                         http.setReadTimeout(readTimeout);
1141                     }
1142                 }
1143             } else {
1144                 if (!failedOnce) {
1145                     http = getNewHttpClient(url, instProxy, connectTimeout);
1146                     http.setReadTimeout(readTimeout);
1147                 } else {
1148                     // make sure to construct new connection if first
1149                     // attempt failed
1150                     http = getNewHttpClient(url, instProxy, connectTimeout, false);
1151                     http.setReadTimeout(readTimeout);
1152                 }
1153             }
1154 
1155             ps = (PrintStream)http.getOutputStream();
1156         } catch (IOException e) {
1157             throw e;
1158         }
1159         // constructor to HTTP client calls openserver
1160         connected = true;
1161     }
1162 
1163     // subclass HttpsClient will overwrite & return an instance of HttpsClient
1164     protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout)
1165         throws IOException {
1166         return HttpClient.New(url, p, connectTimeout, this);
1167     }
1168 
1169     // subclass HttpsClient will overwrite & return an instance of HttpsClient
1170     protected HttpClient getNewHttpClient(URL url, Proxy p,
1171                                           int connectTimeout, boolean useCache)
1172         throws IOException {
1173         return HttpClient.New(url, p, connectTimeout, useCache, this);
1174     }
1175 
1176     private void expect100Continue() throws IOException {
1177             // Expect: 100-Continue was set, so check the return code for
1178             // Acceptance
1179             int oldTimeout = http.getReadTimeout();
1180             boolean enforceTimeOut = false;
1181             boolean timedOut = false;
1182             if (oldTimeout <= 0) {
1183                 // 5s read timeout in case the server doesn't understand
1184                 // Expect: 100-Continue
1185                 http.setReadTimeout(5000);
1186                 enforceTimeOut = true;
1187             }
1188 
1189             try {
1190                 http.parseHTTP(responses, pi, this);
1191             } catch (SocketTimeoutException se) {
1192                 if (!enforceTimeOut) {
1193                     throw se;
1194                 }
1195                 timedOut = true;
1196                 http.setIgnoreContinue(true);
1197             }
1198             if (!timedOut) {
1199                 // Can't use getResponseCode() yet
1200                 String resp = responses.getValue(0);
1201                 // Parse the response which is of the form:
1202                 // HTTP/1.1 417 Expectation Failed
1203                 // HTTP/1.1 100 Continue
1204                 if (resp != null && resp.startsWith("HTTP/")) {
1205                     String[] sa = resp.split("\\s+");
1206                     responseCode = -1;
1207                     try {
1208                         // Response code is 2nd token on the line
1209                         if (sa.length > 1)
1210                             responseCode = Integer.parseInt(sa[1]);
1211                     } catch (NumberFormatException numberFormatException) {
1212                     }
1213                 }
1214                 if (responseCode != 100) {
1215                     throw new ProtocolException("Server rejected operation");
1216                 }
1217             }
1218 
1219             http.setReadTimeout(oldTimeout);
1220 
1221             responseCode = -1;
1222             responses.reset();
1223             // Proceed
1224     }
1225 
1226     /*
1227      * Allowable input/output sequences:
1228      * [interpreted as request entity]
1229      * - get output, [write output,] get input, [read input]
1230      * - get output, [write output]
1231      * [interpreted as GET]
1232      * - get input, [read input]
1233      * Disallowed:
1234      * - get input, [read input,] get output, [write output]
1235      */
1236 
1237     @Override
1238     public synchronized OutputStream getOutputStream() throws IOException {
1239         connecting = true;
1240         SocketPermission p = URLtoSocketPermission(this.url);
1241 
1242         if (p != null) {
1243             try {
1244                 return AccessController.doPrivilegedWithCombiner(
1245                     new PrivilegedExceptionAction<>() {
1246                         public OutputStream run() throws IOException {
1247                             return getOutputStream0();
1248                         }
1249                     }, null, p
1250                 );
1251             } catch (PrivilegedActionException e) {
1252                 throw (IOException) e.getException();
1253             }
1254         } else {
1255             return getOutputStream0();
1256         }
1257     }
1258 
1259     private synchronized OutputStream getOutputStream0() throws IOException {
1260         try {
1261             if (!doOutput) {
1262                 throw new ProtocolException("cannot write to a URLConnection"
1263                                + " if doOutput=false - call setDoOutput(true)");
1264             }
1265 
1266             if (method.equals("GET")) {
1267                 method = "POST"; // Backward compatibility
1268             }
1269             if ("TRACE".equals(method) && "http".equals(url.getProtocol())) {
1270                 throw new ProtocolException("HTTP method TRACE" +
1271                                             " doesn't support output");
1272             }
1273 
1274             // if there's already an input stream open, throw an exception
1275             if (inputStream != null) {
1276                 throw new ProtocolException("Cannot write output after reading input.");
1277             }
1278 
1279             if (!checkReuseConnection())
1280                 connect();
1281 
1282             boolean expectContinue = false;
1283             String expects = requests.findValue("Expect");
1284             if ("100-Continue".equalsIgnoreCase(expects) && streaming()) {
1285                 http.setIgnoreContinue(false);
1286                 expectContinue = true;
1287             }
1288 
1289             if (streaming() && strOutputStream == null) {
1290                 writeRequests();
1291             }
1292 
1293             if (expectContinue) {
1294                 expect100Continue();
1295             }
1296             ps = (PrintStream)http.getOutputStream();
1297             if (streaming()) {
1298                 if (strOutputStream == null) {
1299                     if (chunkLength != -1) { /* chunked */
1300                          strOutputStream = new StreamingOutputStream(
1301                                new ChunkedOutputStream(ps, chunkLength), -1L);
1302                     } else { /* must be fixed content length */
1303                         long length = 0L;
1304                         if (fixedContentLengthLong != -1) {
1305                             length = fixedContentLengthLong;
1306                         } else if (fixedContentLength != -1) {
1307                             length = fixedContentLength;
1308                         }
1309                         strOutputStream = new StreamingOutputStream(ps, length);
1310                     }
1311                 }
1312                 return strOutputStream;
1313             } else {
1314                 if (poster == null) {
1315                     poster = new PosterOutputStream();
1316                 }
1317                 return poster;
1318             }
1319         } catch (RuntimeException e) {
1320             disconnectInternal();
1321             throw e;
1322         } catch (ProtocolException e) {
1323             // Save the response code which may have been set while enforcing
1324             // the 100-continue. disconnectInternal() forces it to -1
1325             int i = responseCode;
1326             disconnectInternal();
1327             responseCode = i;
1328             throw e;
1329         } catch (IOException e) {
1330             disconnectInternal();
1331             throw e;
1332         }
1333     }
1334 
1335     public boolean streaming () {
1336         return (fixedContentLength != -1) || (fixedContentLengthLong != -1) ||
1337                (chunkLength != -1);
1338     }
1339 
1340     /*
1341      * get applicable cookies based on the uri and request headers
1342      * add them to the existing request headers
1343      */
1344     private void setCookieHeader() throws IOException {
1345         if (cookieHandler != null) {
1346             // we only want to capture the user defined Cookies once, as
1347             // they cannot be changed by user code after we are connected,
1348             // only internally.
1349             synchronized (this) {
1350                 if (setUserCookies) {
1351                     int k = requests.getKey("Cookie");
1352                     if (k != -1)
1353                         userCookies = requests.getValue(k);
1354                     k = requests.getKey("Cookie2");
1355                     if (k != -1)
1356                         userCookies2 = requests.getValue(k);
1357                     setUserCookies = false;
1358                 }
1359             }
1360 
1361             // remove old Cookie header before setting new one.
1362             requests.remove("Cookie");
1363             requests.remove("Cookie2");
1364 
1365             URI uri = ParseUtil.toURI(url);
1366             if (uri != null) {
1367                 if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1368                     logger.finest("CookieHandler request for " + uri);
1369                 }
1370                 Map<String, List<String>> cookies
1371                     = cookieHandler.get(
1372                         uri, requests.getHeaders(EXCLUDE_HEADERS));
1373                 if (!cookies.isEmpty()) {
1374                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
1375                         logger.finest("Cookies retrieved: " + cookies.toString());
1376                     }
1377                     for (Map.Entry<String, List<String>> entry :
1378                              cookies.entrySet()) {
1379                         String key = entry.getKey();
1380                         // ignore all entries that don't have "Cookie"
1381                         // or "Cookie2" as keys
1382                         if (!"Cookie".equalsIgnoreCase(key) &&
1383                             !"Cookie2".equalsIgnoreCase(key)) {
1384                             continue;
1385                         }
1386                         List<String> l = entry.getValue();
1387                         if (l != null && !l.isEmpty()) {
1388                             StringJoiner cookieValue = new StringJoiner("; ");
1389                             for (String value : l) {
1390                                 cookieValue.add(value);
1391                             }
1392                             requests.add(key, cookieValue.toString());
1393                         }
1394                     }
1395                 }
1396             }
1397             if (userCookies != null) {
1398                 int k;
1399                 if ((k = requests.getKey("Cookie")) != -1)
1400                     requests.set("Cookie", requests.getValue(k) + ";" + userCookies);
1401                 else
1402                     requests.set("Cookie", userCookies);
1403             }
1404             if (userCookies2 != null) {
1405                 int k;
1406                 if ((k = requests.getKey("Cookie2")) != -1)
1407                     requests.set("Cookie2", requests.getValue(k) + ";" + userCookies2);
1408                 else
1409                     requests.set("Cookie2", userCookies2);
1410             }
1411 
1412         } // end of getting cookies
1413     }
1414 
1415     @Override
1416     public synchronized InputStream getInputStream() throws IOException {
1417         connecting = true;
1418         SocketPermission p = URLtoSocketPermission(this.url);
1419 
1420         if (p != null) {
1421             try {
1422                 return AccessController.doPrivilegedWithCombiner(
1423                     new PrivilegedExceptionAction<>() {
1424                         public InputStream run() throws IOException {
1425                             return getInputStream0();
1426                         }
1427                     }, null, p
1428                 );
1429             } catch (PrivilegedActionException e) {
1430                 throw (IOException) e.getException();
1431             }
1432         } else {
1433             return getInputStream0();
1434         }
1435     }
1436 
1437     @SuppressWarnings("empty-statement")
1438     private synchronized InputStream getInputStream0() throws IOException {
1439 
1440         if (!doInput) {
1441             throw new ProtocolException("Cannot read from URLConnection"
1442                    + " if doInput=false (call setDoInput(true))");
1443         }
1444 
1445         if (rememberedException != null) {
1446             if (rememberedException instanceof RuntimeException)
1447                 throw new RuntimeException(rememberedException);
1448             else {
1449                 throw getChainedException((IOException)rememberedException);
1450             }
1451         }
1452 
1453         if (inputStream != null) {
1454             return inputStream;
1455         }
1456 
1457         if (streaming() ) {
1458             if (strOutputStream == null) {
1459                 getOutputStream();
1460             }
1461             /* make sure stream is closed */
1462             strOutputStream.close ();
1463             if (!strOutputStream.writtenOK()) {
1464                 throw new IOException ("Incomplete output stream");
1465             }
1466         }
1467 
1468         int redirects = 0;
1469         int respCode = 0;
1470         long cl = -1;
1471         AuthenticationInfo serverAuthentication = null;
1472         AuthenticationInfo proxyAuthentication = null;
1473         AuthenticationHeader srvHdr = null;
1474 
1475         /**
1476          * Failed Negotiate
1477          *
1478          * In some cases, the Negotiate auth is supported for the
1479          * remote host but the negotiate process still fails (For
1480          * example, if the web page is located on a backend server
1481          * and delegation is needed but fails). The authentication
1482          * process will start again, and we need to detect this
1483          * kind of failure and do proper fallback (say, to NTLM).
1484          *
1485          * In order to achieve this, the inNegotiate flag is set
1486          * when the first negotiate challenge is met (and reset
1487          * if authentication is finished). If a fresh new negotiate
1488          * challenge (no parameter) is found while inNegotiate is
1489          * set, we know there's a failed auth attempt recently.
1490          * Here we'll ignore the header line so that fallback
1491          * can be practiced.
1492          *
1493          * inNegotiateProxy is for proxy authentication.
1494          */
1495         boolean inNegotiate = false;
1496         boolean inNegotiateProxy = false;
1497 
1498         // If the user has set either of these headers then do not remove them
1499         isUserServerAuth = requests.getKey("Authorization") != -1;
1500         isUserProxyAuth = requests.getKey("Proxy-Authorization") != -1;
1501 
1502         try {
1503             do {
1504                 if (!checkReuseConnection())
1505                     connect();
1506 
1507                 if (cachedInputStream != null) {
1508                     return cachedInputStream;
1509                 }
1510 
1511                 // Check if URL should be metered
1512                 boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, method);
1513 
1514                 if (meteredInput)   {
1515                     pi = new ProgressSource(url, method);
1516                     pi.beginTracking();
1517                 }
1518 
1519                 /* REMIND: This exists to fix the HttpsURLConnection subclass.
1520                  * Hotjava needs to run on JDK1.1FCS.  Do proper fix once a
1521                  * proper solution for SSL can be found.
1522                  */
1523                 ps = (PrintStream)http.getOutputStream();
1524 
1525                 if (!streaming()) {
1526                     writeRequests();
1527                 }
1528                 http.parseHTTP(responses, pi, this);
1529                 if (logger.isLoggable(PlatformLogger.Level.FINE)) {
1530                     logger.fine(responses.toString());
1531                 }
1532 
1533                 boolean b1 = responses.filterNTLMResponses("WWW-Authenticate");
1534                 boolean b2 = responses.filterNTLMResponses("Proxy-Authenticate");
1535                 if (b1 || b2) {
1536                     if (logger.isLoggable(PlatformLogger.Level.FINE)) {
1537                         logger.fine(">>>> Headers are filtered");
1538                         logger.fine(responses.toString());
1539                     }
1540                 }
1541 
1542                 inputStream = http.getInputStream();
1543 
1544                 respCode = getResponseCode();
1545                 if (respCode == -1) {
1546                     disconnectInternal();
1547                     throw new IOException ("Invalid Http response");
1548                 }
1549                 if (respCode == HTTP_PROXY_AUTH) {
1550                     if (streaming()) {
1551                         disconnectInternal();
1552                         throw new HttpRetryException (
1553                             RETRY_MSG1, HTTP_PROXY_AUTH);
1554                     }
1555 
1556                     // Read comments labeled "Failed Negotiate" for details.
1557                     boolean dontUseNegotiate = false;
1558                     Iterator<String> iter = responses.multiValueIterator("Proxy-Authenticate");
1559                     while (iter.hasNext()) {
1560                         String value = iter.next().trim();
1561                         if (value.equalsIgnoreCase("Negotiate") ||
1562                                 value.equalsIgnoreCase("Kerberos")) {
1563                             if (!inNegotiateProxy) {
1564                                 inNegotiateProxy = true;
1565                             } else {
1566                                 dontUseNegotiate = true;
1567                                 doingNTLMp2ndStage = false;
1568                                 proxyAuthentication = null;
1569                             }
1570                             break;
1571                         }
1572                     }
1573 
1574                     // changes: add a 3rd parameter to the constructor of
1575                     // AuthenticationHeader, so that NegotiateAuthentication.
1576                     // isSupported can be tested.
1577                     // The other 2 appearances of "new AuthenticationHeader" is
1578                     // altered in similar ways.
1579 
1580                     AuthenticationHeader authhdr = new AuthenticationHeader (
1581                             "Proxy-Authenticate", responses,
1582                             new HttpCallerInfo(url, http.getProxyHostUsed(),
1583                                 http.getProxyPortUsed()),
1584                             dontUseNegotiate
1585                     );
1586 
1587                     if (!doingNTLMp2ndStage) {
1588                         proxyAuthentication =
1589                             resetProxyAuthentication(proxyAuthentication, authhdr);
1590                         if (proxyAuthentication != null) {
1591                             redirects++;
1592                             disconnectInternal();
1593                             continue;
1594                         }
1595                     } else {
1596                         /* in this case, only one header field will be present */
1597                         String raw = responses.findValue ("Proxy-Authenticate");
1598                         reset ();
1599                         if (!proxyAuthentication.setHeaders(this,
1600                                                         authhdr.headerParser(), raw)) {
1601                             disconnectInternal();
1602                             throw new IOException ("Authentication failure");
1603                         }
1604                         if (serverAuthentication != null && srvHdr != null &&
1605                                 !serverAuthentication.setHeaders(this,
1606                                                         srvHdr.headerParser(), raw)) {
1607                             disconnectInternal ();
1608                             throw new IOException ("Authentication failure");
1609                         }
1610                         authObj = null;
1611                         doingNTLMp2ndStage = false;
1612                         continue;
1613                     }
1614                 } else {
1615                     inNegotiateProxy = false;
1616                     doingNTLMp2ndStage = false;
1617                     if (!isUserProxyAuth)
1618                         requests.remove("Proxy-Authorization");
1619                 }
1620 
1621                 // cache proxy authentication info
1622                 if (proxyAuthentication != null) {
1623                     // cache auth info on success, domain header not relevant.
1624                     proxyAuthentication.addToCache();
1625                 }
1626 
1627                 if (respCode == HTTP_UNAUTHORIZED) {
1628                     if (streaming()) {
1629                         disconnectInternal();
1630                         throw new HttpRetryException (
1631                             RETRY_MSG2, HTTP_UNAUTHORIZED);
1632                     }
1633 
1634                     // Read comments labeled "Failed Negotiate" for details.
1635                     boolean dontUseNegotiate = false;
1636                     Iterator<String> iter = responses.multiValueIterator("WWW-Authenticate");
1637                     while (iter.hasNext()) {
1638                         String value = iter.next().trim();
1639                         if (value.equalsIgnoreCase("Negotiate") ||
1640                                 value.equalsIgnoreCase("Kerberos")) {
1641                             if (!inNegotiate) {
1642                                 inNegotiate = true;
1643                             } else {
1644                                 dontUseNegotiate = true;
1645                                 doingNTLM2ndStage = false;
1646                                 serverAuthentication = null;
1647                             }
1648                             break;
1649                         }
1650                     }
1651 
1652                     srvHdr = new AuthenticationHeader (
1653                             "WWW-Authenticate", responses,
1654                             new HttpCallerInfo(url),
1655                             dontUseNegotiate
1656                     );
1657 
1658                     String raw = srvHdr.raw();
1659                     if (!doingNTLM2ndStage) {
1660                         if ((serverAuthentication != null)&&
1661                             serverAuthentication.getAuthScheme() != NTLM) {
1662                             if (serverAuthentication.isAuthorizationStale (raw)) {
1663                                 /* we can retry with the current credentials */
1664                                 disconnectWeb();
1665                                 redirects++;
1666                                 requests.set(serverAuthentication.getHeaderName(),
1667                                             serverAuthentication.getHeaderValue(url, method));
1668                                 currentServerCredentials = serverAuthentication;
1669                                 setCookieHeader();
1670                                 continue;
1671                             } else {
1672                                 serverAuthentication.removeFromCache();
1673                             }
1674                         }
1675                         serverAuthentication = getServerAuthentication(srvHdr);
1676                         currentServerCredentials = serverAuthentication;
1677 
1678                         if (serverAuthentication != null) {
1679                             disconnectWeb();
1680                             redirects++; // don't let things loop ad nauseum
1681                             setCookieHeader();
1682                             continue;
1683                         }
1684                     } else {
1685                         reset ();
1686                         /* header not used for ntlm */
1687                         if (!serverAuthentication.setHeaders(this, null, raw)) {
1688                             disconnectWeb();
1689                             throw new IOException ("Authentication failure");
1690                         }
1691                         doingNTLM2ndStage = false;
1692                         authObj = null;
1693                         setCookieHeader();
1694                         continue;
1695                     }
1696                 }
1697                 // cache server authentication info
1698                 if (serverAuthentication != null) {
1699                     // cache auth info on success
1700                     if (!(serverAuthentication instanceof DigestAuthentication) ||
1701                         (domain == null)) {
1702                         if (serverAuthentication instanceof BasicAuthentication) {
1703                             // check if the path is shorter than the existing entry
1704                             String npath = AuthenticationInfo.reducePath (url.getPath());
1705                             String opath = serverAuthentication.path;
1706                             if (!opath.startsWith (npath) || npath.length() >= opath.length()) {
1707                                 /* npath is longer, there must be a common root */
1708                                 npath = BasicAuthentication.getRootPath (opath, npath);
1709                             }
1710                             // remove the entry and create a new one
1711                             BasicAuthentication a =
1712                                 (BasicAuthentication) serverAuthentication.clone();
1713                             serverAuthentication.removeFromCache();
1714                             a.path = npath;
1715                             serverAuthentication = a;
1716                         }
1717                         serverAuthentication.addToCache();
1718                     } else {
1719                         // what we cache is based on the domain list in the request
1720                         DigestAuthentication srv = (DigestAuthentication)
1721                             serverAuthentication;
1722                         StringTokenizer tok = new StringTokenizer (domain," ");
1723                         String realm = srv.realm;
1724                         PasswordAuthentication pw = srv.pw;
1725                         digestparams = srv.params;
1726                         while (tok.hasMoreTokens()) {
1727                             String path = tok.nextToken();
1728                             try {
1729                                 /* path could be an abs_path or a complete URI */
1730                                 URL u = new URL (url, path);
1731                                 DigestAuthentication d = new DigestAuthentication (
1732                                                    false, u, realm, "Digest", pw, digestparams);
1733                                 d.addToCache ();
1734                             } catch (Exception e) {}
1735                         }
1736                     }
1737                 }
1738 
1739                 // some flags should be reset to its initialized form so that
1740                 // even after a redirect the necessary checks can still be
1741                 // preformed.
1742                 inNegotiate = false;
1743                 inNegotiateProxy = false;
1744 
1745                 //serverAuthentication = null;
1746                 doingNTLMp2ndStage = false;
1747                 doingNTLM2ndStage = false;
1748                 if (!isUserServerAuth)
1749                     requests.remove("Authorization");
1750                 if (!isUserProxyAuth)
1751                     requests.remove("Proxy-Authorization");
1752 
1753                 if (respCode == HTTP_OK) {
1754                     checkResponseCredentials (false);
1755                 } else {
1756                     needToCheck = false;
1757                 }
1758 
1759                 // a flag need to clean
1760                 needToCheck = true;
1761 
1762                 if (followRedirect()) {
1763                     /* if we should follow a redirect, then the followRedirects()
1764                      * method will disconnect() and re-connect us to the new
1765                      * location
1766                      */
1767                     redirects++;
1768 
1769                     // redirecting HTTP response may have set cookie, so
1770                     // need to re-generate request header
1771                     setCookieHeader();
1772 
1773                     continue;
1774                 }
1775 
1776                 try {
1777                     cl = Long.parseLong(responses.findValue("content-length"));
1778                 } catch (Exception exc) { };
1779 
1780                 if (method.equals("HEAD") || cl == 0 ||
1781                     respCode == HTTP_NOT_MODIFIED ||
1782                     respCode == HTTP_NO_CONTENT) {
1783 
1784                     if (pi != null) {
1785                         pi.finishTracking();
1786                         pi = null;
1787                     }
1788                     http.finished();
1789                     http = null;
1790                     inputStream = new EmptyInputStream();
1791                     connected = false;
1792                 }
1793 
1794                 if (respCode == 200 || respCode == 203 || respCode == 206 ||
1795                     respCode == 300 || respCode == 301 || respCode == 410) {
1796                     if (cacheHandler != null && getUseCaches()) {
1797                         // give cache a chance to save response in cache
1798                         URI uri = ParseUtil.toURI(url);
1799                         if (uri != null) {
1800                             URLConnection uconn = this;
1801                             if ("https".equalsIgnoreCase(uri.getScheme())) {
1802                                 try {
1803                                 // use reflection to get to the public
1804                                 // HttpsURLConnection instance saved in
1805                                 // DelegateHttpsURLConnection
1806                                 uconn = (URLConnection)this.getClass().getField("httpsURLConnection").get(this);
1807                                 } catch (IllegalAccessException |
1808                                          NoSuchFieldException e) {
1809                                     // ignored; use 'this'
1810                                 }
1811                             }
1812                             CacheRequest cacheRequest =
1813                                 cacheHandler.put(uri, uconn);
1814                             if (cacheRequest != null && http != null) {
1815                                 http.setCacheRequest(cacheRequest);
1816                                 inputStream = new HttpInputStream(inputStream, cacheRequest);
1817                             }
1818                         }
1819                     }
1820                 }
1821 
1822                 if (!(inputStream instanceof HttpInputStream)) {
1823                     inputStream = new HttpInputStream(inputStream);
1824                 }
1825 
1826                 if (respCode >= 400) {
1827                     if (respCode == 404 || respCode == 410) {
1828                         throw new FileNotFoundException(url.toString());
1829                     } else {
1830                         throw new java.io.IOException("Server returned HTTP" +
1831                               " response code: " + respCode + " for URL: " +
1832                               url.toString());
1833                     }
1834                 }
1835                 poster = null;
1836                 strOutputStream = null;
1837                 return inputStream;
1838             } while (redirects < maxRedirects);
1839 
1840             throw new ProtocolException("Server redirected too many " +
1841                                         " times ("+ redirects + ")");
1842         } catch (RuntimeException e) {
1843             disconnectInternal();
1844             rememberedException = e;
1845             throw e;
1846         } catch (IOException e) {
1847             rememberedException = e;
1848 
1849             // buffer the error stream if bytes < 4k
1850             // and it can be buffered within 1 second
1851             String te = responses.findValue("Transfer-Encoding");
1852             if (http != null && http.isKeepingAlive() && enableESBuffer &&
1853                 (cl > 0 || (te != null && te.equalsIgnoreCase("chunked")))) {
1854                 errorStream = ErrorStream.getErrorStream(inputStream, cl, http);
1855             }
1856             throw e;
1857         } finally {
1858             if (proxyAuthKey != null) {
1859                 AuthenticationInfo.endAuthRequest(proxyAuthKey);
1860             }
1861             if (serverAuthKey != null) {
1862                 AuthenticationInfo.endAuthRequest(serverAuthKey);
1863             }
1864         }
1865     }
1866 
1867     /*
1868      * Creates a chained exception that has the same type as
1869      * original exception and with the same message. Right now,
1870      * there is no convenient APIs for doing so.
1871      */
1872     private IOException getChainedException(final IOException rememberedException) {
1873         try {
1874             final Object[] args = { rememberedException.getMessage() };
1875             IOException chainedException =
1876                 java.security.AccessController.doPrivileged(
1877                     new java.security.PrivilegedExceptionAction<>() {
1878                         public IOException run() throws Exception {
1879                             return (IOException)
1880                                 rememberedException.getClass()
1881                                 .getConstructor(new Class<?>[] { String.class })
1882                                 .newInstance(args);
1883                         }
1884                     });
1885             chainedException.initCause(rememberedException);
1886             return chainedException;
1887         } catch (Exception ignored) {
1888             return rememberedException;
1889         }
1890     }
1891 
1892     @Override
1893     public InputStream getErrorStream() {
1894         if (connected && responseCode >= 400) {
1895             // Client Error 4xx and Server Error 5xx
1896             if (errorStream != null) {
1897                 return errorStream;
1898             } else if (inputStream != null) {
1899                 return inputStream;
1900             }
1901         }
1902         return null;
1903     }
1904 
1905     /**
1906      * set or reset proxy authentication info in request headers
1907      * after receiving a 407 error. In the case of NTLM however,
1908      * receiving a 407 is normal and we just skip the stale check
1909      * because ntlm does not support this feature.
1910      */
1911     private AuthenticationInfo
1912         resetProxyAuthentication(AuthenticationInfo proxyAuthentication, AuthenticationHeader auth) throws IOException {
1913         if ((proxyAuthentication != null )&&
1914              proxyAuthentication.getAuthScheme() != NTLM) {
1915             String raw = auth.raw();
1916             if (proxyAuthentication.isAuthorizationStale (raw)) {
1917                 /* we can retry with the current credentials */
1918                 String value;
1919                 if (proxyAuthentication instanceof DigestAuthentication) {
1920                     DigestAuthentication digestProxy = (DigestAuthentication)
1921                         proxyAuthentication;
1922                     if (tunnelState() == TunnelState.SETUP) {
1923                         value = digestProxy.getHeaderValue(connectRequestURI(url), HTTP_CONNECT);
1924                     } else {
1925                         value = digestProxy.getHeaderValue(getRequestURI(), method);
1926                     }
1927                 } else {
1928                     value = proxyAuthentication.getHeaderValue(url, method);
1929                 }
1930                 requests.set(proxyAuthentication.getHeaderName(), value);
1931                 currentProxyCredentials = proxyAuthentication;
1932                 return proxyAuthentication;
1933             } else {
1934                 proxyAuthentication.removeFromCache();
1935             }
1936         }
1937         proxyAuthentication = getHttpProxyAuthentication(auth);
1938         currentProxyCredentials = proxyAuthentication;
1939         return  proxyAuthentication;
1940     }
1941 
1942     /**
1943      * Returns the tunnel state.
1944      *
1945      * @return  the state
1946      */
1947     TunnelState tunnelState() {
1948         return tunnelState;
1949     }
1950 
1951     /**
1952      * Set the tunneling status.
1953      *
1954      * @param tunnelState the state
1955      */
1956     public void setTunnelState(TunnelState tunnelState) {
1957         this.tunnelState = tunnelState;
1958     }
1959 
1960     /**
1961      * establish a tunnel through proxy server
1962      */
1963     public synchronized void doTunneling() throws IOException {
1964         int retryTunnel = 0;
1965         String statusLine = "";
1966         int respCode = 0;
1967         AuthenticationInfo proxyAuthentication = null;
1968         String proxyHost = null;
1969         int proxyPort = -1;
1970 
1971         // save current requests so that they can be restored after tunnel is setup.
1972         MessageHeader savedRequests = requests;
1973         requests = new MessageHeader();
1974 
1975         // Read comments labeled "Failed Negotiate" for details.
1976         boolean inNegotiateProxy = false;
1977 
1978         try {
1979             /* Actively setting up a tunnel */
1980             setTunnelState(TunnelState.SETUP);
1981 
1982             do {
1983                 if (!checkReuseConnection()) {
1984                     proxiedConnect(url, proxyHost, proxyPort, false);
1985                 }
1986                 // send the "CONNECT" request to establish a tunnel
1987                 // through proxy server
1988                 sendCONNECTRequest();
1989                 responses.reset();
1990 
1991                 // There is no need to track progress in HTTP Tunneling,
1992                 // so ProgressSource is null.
1993                 http.parseHTTP(responses, null, this);
1994 
1995                 /* Log the response to the CONNECT */
1996                 if (logger.isLoggable(PlatformLogger.Level.FINE)) {
1997                     logger.fine(responses.toString());
1998                 }
1999 
2000                 if (responses.filterNTLMResponses("Proxy-Authenticate")) {
2001                     if (logger.isLoggable(PlatformLogger.Level.FINE)) {
2002                         logger.fine(">>>> Headers are filtered");
2003                         logger.fine(responses.toString());
2004                     }
2005                 }
2006 
2007                 statusLine = responses.getValue(0);
2008                 StringTokenizer st = new StringTokenizer(statusLine);
2009                 st.nextToken();
2010                 respCode = Integer.parseInt(st.nextToken().trim());
2011                 if (respCode == HTTP_PROXY_AUTH) {
2012                     // Read comments labeled "Failed Negotiate" for details.
2013                     boolean dontUseNegotiate = false;
2014                     Iterator<String> iter = responses.multiValueIterator("Proxy-Authenticate");
2015                     while (iter.hasNext()) {
2016                         String value = iter.next().trim();
2017                         if (value.equalsIgnoreCase("Negotiate") ||
2018                                 value.equalsIgnoreCase("Kerberos")) {
2019                             if (!inNegotiateProxy) {
2020                                 inNegotiateProxy = true;
2021                             } else {
2022                                 dontUseNegotiate = true;
2023                                 doingNTLMp2ndStage = false;
2024                                 proxyAuthentication = null;
2025                             }
2026                             break;
2027                         }
2028                     }
2029 
2030                     AuthenticationHeader authhdr = new AuthenticationHeader (
2031                             "Proxy-Authenticate", responses,
2032                             new HttpCallerInfo(url, http.getProxyHostUsed(),
2033                                 http.getProxyPortUsed()),
2034                             dontUseNegotiate
2035                     );
2036                     if (!doingNTLMp2ndStage) {
2037                         proxyAuthentication =
2038                             resetProxyAuthentication(proxyAuthentication, authhdr);
2039                         if (proxyAuthentication != null) {
2040                             proxyHost = http.getProxyHostUsed();
2041                             proxyPort = http.getProxyPortUsed();
2042                             disconnectInternal();
2043                             retryTunnel++;
2044                             continue;
2045                         }
2046                     } else {
2047                         String raw = responses.findValue ("Proxy-Authenticate");
2048                         reset ();
2049                         if (!proxyAuthentication.setHeaders(this,
2050                                                 authhdr.headerParser(), raw)) {
2051                             disconnectInternal();
2052                             throw new IOException ("Authentication failure");
2053                         }
2054                         authObj = null;
2055                         doingNTLMp2ndStage = false;
2056                         continue;
2057                     }
2058                 }
2059                 // cache proxy authentication info
2060                 if (proxyAuthentication != null) {
2061                     // cache auth info on success, domain header not relevant.
2062                     proxyAuthentication.addToCache();
2063                 }
2064 
2065                 if (respCode == HTTP_OK) {
2066                     setTunnelState(TunnelState.TUNNELING);
2067                     break;
2068                 }
2069                 // we don't know how to deal with other response code
2070                 // so disconnect and report error
2071                 disconnectInternal();
2072                 setTunnelState(TunnelState.NONE);
2073                 break;
2074             } while (retryTunnel < maxRedirects);
2075 
2076             if (retryTunnel >= maxRedirects || (respCode != HTTP_OK)) {
2077                 throw new IOException("Unable to tunnel through proxy."+
2078                                       " Proxy returns \"" +
2079                                       statusLine + "\"");
2080             }
2081         } finally  {
2082             if (proxyAuthKey != null) {
2083                 AuthenticationInfo.endAuthRequest(proxyAuthKey);
2084             }
2085         }
2086 
2087         // restore original request headers
2088         requests = savedRequests;
2089 
2090         // reset responses
2091         responses.reset();
2092     }
2093 
2094     static String connectRequestURI(URL url) {
2095         String host = url.getHost();
2096         int port = url.getPort();
2097         port = port != -1 ? port : url.getDefaultPort();
2098 
2099         return host + ":" + port;
2100     }
2101 
2102     /**
2103      * send a CONNECT request for establishing a tunnel to proxy server
2104      */
2105     private void sendCONNECTRequest() throws IOException {
2106         int port = url.getPort();
2107 
2108         requests.set(0, HTTP_CONNECT + " " + connectRequestURI(url)
2109                          + " " + httpVersion, null);
2110         requests.setIfNotSet("User-Agent", userAgent);
2111 
2112         String host = url.getHost();
2113         if (port != -1 && port != url.getDefaultPort()) {
2114             host += ":" + String.valueOf(port);
2115         }
2116         requests.setIfNotSet("Host", host);
2117 
2118         // Not really necessary for a tunnel, but can't hurt
2119         requests.setIfNotSet("Accept", acceptString);
2120 
2121         if (http.getHttpKeepAliveSet()) {
2122             requests.setIfNotSet("Proxy-Connection", "keep-alive");
2123         }
2124 
2125         setPreemptiveProxyAuthentication(requests);
2126 
2127          /* Log the CONNECT request */
2128         if (logger.isLoggable(PlatformLogger.Level.FINE)) {
2129             logger.fine(requests.toString());
2130         }
2131 
2132         http.writeRequests(requests, null);
2133     }
2134 
2135     /**
2136      * Sets pre-emptive proxy authentication in header
2137      */
2138     private void setPreemptiveProxyAuthentication(MessageHeader requests) throws IOException {
2139         AuthenticationInfo pauth
2140             = AuthenticationInfo.getProxyAuth(http.getProxyHostUsed(),
2141                                               http.getProxyPortUsed());
2142         if (pauth != null && pauth.supportsPreemptiveAuthorization()) {
2143             String value;
2144             if (pauth instanceof DigestAuthentication) {
2145                 DigestAuthentication digestProxy = (DigestAuthentication) pauth;
2146                 if (tunnelState() == TunnelState.SETUP) {
2147                     value = digestProxy
2148                         .getHeaderValue(connectRequestURI(url), HTTP_CONNECT);
2149                 } else {
2150                     value = digestProxy.getHeaderValue(getRequestURI(), method);
2151                 }
2152             } else {
2153                 value = pauth.getHeaderValue(url, method);
2154             }
2155 
2156             // Sets "Proxy-authorization"
2157             requests.set(pauth.getHeaderName(), value);
2158             currentProxyCredentials = pauth;
2159         }
2160     }
2161 
2162     /**
2163      * Gets the authentication for an HTTP proxy, and applies it to
2164      * the connection.
2165      */
2166     @SuppressWarnings("fallthrough")
2167     private AuthenticationInfo getHttpProxyAuthentication (AuthenticationHeader authhdr) {
2168         /* get authorization from authenticator */
2169         AuthenticationInfo ret = null;
2170         String raw = authhdr.raw();
2171         String host = http.getProxyHostUsed();
2172         int port = http.getProxyPortUsed();
2173         if (host != null && authhdr.isPresent()) {
2174             HeaderParser p = authhdr.headerParser();
2175             String realm = p.findValue("realm");
2176             String scheme = authhdr.scheme();
2177             AuthScheme authScheme = UNKNOWN;
2178             if ("basic".equalsIgnoreCase(scheme)) {
2179                 authScheme = BASIC;
2180             } else if ("digest".equalsIgnoreCase(scheme)) {
2181                 authScheme = DIGEST;
2182             } else if ("ntlm".equalsIgnoreCase(scheme)) {
2183                 authScheme = NTLM;
2184                 doingNTLMp2ndStage = true;
2185             } else if ("Kerberos".equalsIgnoreCase(scheme)) {
2186                 authScheme = KERBEROS;
2187                 doingNTLMp2ndStage = true;
2188             } else if ("Negotiate".equalsIgnoreCase(scheme)) {
2189                 authScheme = NEGOTIATE;
2190                 doingNTLMp2ndStage = true;
2191             }
2192 
2193             if (realm == null)
2194                 realm = "";
2195             proxyAuthKey = AuthenticationInfo.getProxyAuthKey(host, port, realm, authScheme);
2196             ret = AuthenticationInfo.getProxyAuth(proxyAuthKey);
2197             if (ret == null) {
2198                 switch (authScheme) {
2199                 case BASIC:
2200                     InetAddress addr = null;
2201                     try {
2202                         final String finalHost = host;
2203                         addr = java.security.AccessController.doPrivileged(
2204                             new java.security.PrivilegedExceptionAction<>() {
2205                                 public InetAddress run()
2206                                     throws java.net.UnknownHostException {
2207                                     return InetAddress.getByName(finalHost);
2208                                 }
2209                             });
2210                     } catch (java.security.PrivilegedActionException ignored) {
2211                         // User will have an unknown host.
2212                     }
2213                     PasswordAuthentication a =
2214                         privilegedRequestPasswordAuthentication(
2215                                     host, addr, port, "http",
2216                                     realm, scheme, url, RequestorType.PROXY);
2217                     if (a != null) {
2218                         ret = new BasicAuthentication(true, host, port, realm, a);
2219                     }
2220                     break;
2221                 case DIGEST:
2222                     a = privilegedRequestPasswordAuthentication(
2223                                     host, null, port, url.getProtocol(),
2224                                     realm, scheme, url, RequestorType.PROXY);
2225                     if (a != null) {
2226                         DigestAuthentication.Parameters params =
2227                             new DigestAuthentication.Parameters();
2228                         ret = new DigestAuthentication(true, host, port, realm,
2229                                                             scheme, a, params);
2230                     }
2231                     break;
2232                 case NTLM:
2233                     if (NTLMAuthenticationProxy.supported) {
2234                         /* tryTransparentNTLMProxy will always be true the first
2235                          * time around, but verify that the platform supports it
2236                          * otherwise don't try. */
2237                         if (tryTransparentNTLMProxy) {
2238                             tryTransparentNTLMProxy =
2239                                     NTLMAuthenticationProxy.supportsTransparentAuth;
2240                             /* If the platform supports transparent authentication
2241                              * then normally it's ok to do transparent auth to a proxy
2242                                          * because we generally trust proxies (chosen by the user)
2243                                          * But not in the case of 305 response where the server
2244                              * chose it. */
2245                             if (tryTransparentNTLMProxy && useProxyResponseCode) {
2246                                 tryTransparentNTLMProxy = false;
2247                             }
2248 
2249                         }
2250                         a = null;
2251                         if (tryTransparentNTLMProxy) {
2252                             logger.finest("Trying Transparent NTLM authentication");
2253                         } else {
2254                             a = privilegedRequestPasswordAuthentication(
2255                                                 host, null, port, url.getProtocol(),
2256                                                 "", scheme, url, RequestorType.PROXY);
2257                         }
2258                         /* If we are not trying transparent authentication then
2259                          * we need to have a PasswordAuthentication instance. For
2260                          * transparent authentication (Windows only) the username
2261                          * and password will be picked up from the current logged
2262                          * on users credentials.
2263                         */
2264                         if (tryTransparentNTLMProxy ||
2265                               (!tryTransparentNTLMProxy && a != null)) {
2266                             ret = NTLMAuthenticationProxy.proxy.create(true, host, port, a);
2267                         }
2268 
2269                         /* set to false so that we do not try again */
2270                         tryTransparentNTLMProxy = false;
2271                     }
2272                     break;
2273                 case NEGOTIATE:
2274                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
2275                     break;
2276                 case KERBEROS:
2277                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
2278                     break;
2279                 case UNKNOWN:
2280                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
2281                         logger.finest("Unknown/Unsupported authentication scheme: " + scheme);
2282                     }
2283                 /*fall through*/
2284                 default:
2285                     throw new AssertionError("should not reach here");
2286                 }
2287             }
2288             // For backwards compatibility, we also try defaultAuth
2289             // REMIND:  Get rid of this for JDK2.0.
2290 
2291             if (ret == null && defaultAuth != null
2292                 && defaultAuth.schemeSupported(scheme)) {
2293                 try {
2294                     URL u = new URL("http", host, port, "/");
2295                     String a = defaultAuth.authString(u, scheme, realm);
2296                     if (a != null) {
2297                         ret = new BasicAuthentication (true, host, port, realm, a);
2298                         // not in cache by default - cache on success
2299                     }
2300                 } catch (java.net.MalformedURLException ignored) {
2301                 }
2302             }
2303             if (ret != null) {
2304                 if (!ret.setHeaders(this, p, raw)) {
2305                     ret = null;
2306                 }
2307             }
2308         }
2309         if (logger.isLoggable(PlatformLogger.Level.FINER)) {
2310             logger.finer("Proxy Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
2311         }
2312         return ret;
2313     }
2314 
2315     /**
2316      * Gets the authentication for an HTTP server, and applies it to
2317      * the connection.
2318      * @param authHdr the AuthenticationHeader which tells what auth scheme is
2319      * preferred.
2320      */
2321     @SuppressWarnings("fallthrough")
2322     private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr) {
2323         /* get authorization from authenticator */
2324         AuthenticationInfo ret = null;
2325         String raw = authhdr.raw();
2326         /* When we get an NTLM auth from cache, don't set any special headers */
2327         if (authhdr.isPresent()) {
2328             HeaderParser p = authhdr.headerParser();
2329             String realm = p.findValue("realm");
2330             String scheme = authhdr.scheme();
2331             AuthScheme authScheme = UNKNOWN;
2332             if ("basic".equalsIgnoreCase(scheme)) {
2333                 authScheme = BASIC;
2334             } else if ("digest".equalsIgnoreCase(scheme)) {
2335                 authScheme = DIGEST;
2336             } else if ("ntlm".equalsIgnoreCase(scheme)) {
2337                 authScheme = NTLM;
2338                 doingNTLM2ndStage = true;
2339             } else if ("Kerberos".equalsIgnoreCase(scheme)) {
2340                 authScheme = KERBEROS;
2341                 doingNTLM2ndStage = true;
2342             } else if ("Negotiate".equalsIgnoreCase(scheme)) {
2343                 authScheme = NEGOTIATE;
2344                 doingNTLM2ndStage = true;
2345             }
2346 
2347             domain = p.findValue ("domain");
2348             if (realm == null)
2349                 realm = "";
2350             serverAuthKey = AuthenticationInfo.getServerAuthKey(url, realm, authScheme);
2351             ret = AuthenticationInfo.getServerAuth(serverAuthKey);
2352             InetAddress addr = null;
2353             if (ret == null) {
2354                 try {
2355                     addr = InetAddress.getByName(url.getHost());
2356                 } catch (java.net.UnknownHostException ignored) {
2357                     // User will have addr = null
2358                 }
2359             }
2360             // replacing -1 with default port for a protocol
2361             int port = url.getPort();
2362             if (port == -1) {
2363                 port = url.getDefaultPort();
2364             }
2365             if (ret == null) {
2366                 switch(authScheme) {
2367                 case KERBEROS:
2368                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
2369                     break;
2370                 case NEGOTIATE:
2371                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
2372                     break;
2373                 case BASIC:
2374                     PasswordAuthentication a =
2375                         privilegedRequestPasswordAuthentication(
2376                             url.getHost(), addr, port, url.getProtocol(),
2377                             realm, scheme, url, RequestorType.SERVER);
2378                     if (a != null) {
2379                         ret = new BasicAuthentication(false, url, realm, a);
2380                     }
2381                     break;
2382                 case DIGEST:
2383                     a = privilegedRequestPasswordAuthentication(
2384                             url.getHost(), addr, port, url.getProtocol(),
2385                             realm, scheme, url, RequestorType.SERVER);
2386                     if (a != null) {
2387                         digestparams = new DigestAuthentication.Parameters();
2388                         ret = new DigestAuthentication(false, url, realm, scheme, a, digestparams);
2389                     }
2390                     break;
2391                 case NTLM:
2392                     if (NTLMAuthenticationProxy.supported) {
2393                         URL url1;
2394                         try {
2395                             url1 = new URL (url, "/"); /* truncate the path */
2396                         } catch (Exception e) {
2397                             url1 = url;
2398                         }
2399 
2400                         /* tryTransparentNTLMServer will always be true the first
2401                          * time around, but verify that the platform supports it
2402                          * otherwise don't try. */
2403                         if (tryTransparentNTLMServer) {
2404                             tryTransparentNTLMServer =
2405                                     NTLMAuthenticationProxy.supportsTransparentAuth;
2406                             /* If the platform supports transparent authentication
2407                              * then check if we are in a secure environment
2408                              * whether, or not, we should try transparent authentication.*/
2409                             if (tryTransparentNTLMServer) {
2410                                 tryTransparentNTLMServer =
2411                                         NTLMAuthenticationProxy.isTrustedSite(url);
2412                             }
2413                         }
2414                         a = null;
2415                         if (tryTransparentNTLMServer) {
2416                             logger.finest("Trying Transparent NTLM authentication");
2417                         } else {
2418                             a = privilegedRequestPasswordAuthentication(
2419                                 url.getHost(), addr, port, url.getProtocol(),
2420                                 "", scheme, url, RequestorType.SERVER);
2421                         }
2422 
2423                         /* If we are not trying transparent authentication then
2424                          * we need to have a PasswordAuthentication instance. For
2425                          * transparent authentication (Windows only) the username
2426                          * and password will be picked up from the current logged
2427                          * on users credentials.
2428                          */
2429                         if (tryTransparentNTLMServer ||
2430                               (!tryTransparentNTLMServer && a != null)) {
2431                             ret = NTLMAuthenticationProxy.proxy.create(false, url1, a);
2432                         }
2433 
2434                         /* set to false so that we do not try again */
2435                         tryTransparentNTLMServer = false;
2436                     }
2437                     break;
2438                 case UNKNOWN:
2439                     if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
2440                         logger.finest("Unknown/Unsupported authentication scheme: " + scheme);
2441                     }
2442                 /*fall through*/
2443                 default:
2444                     throw new AssertionError("should not reach here");
2445                 }
2446             }
2447 
2448             // For backwards compatibility, we also try defaultAuth
2449             // REMIND:  Get rid of this for JDK2.0.
2450 
2451             if (ret == null && defaultAuth != null
2452                 && defaultAuth.schemeSupported(scheme)) {
2453                 String a = defaultAuth.authString(url, scheme, realm);
2454                 if (a != null) {
2455                     ret = new BasicAuthentication (false, url, realm, a);
2456                     // not in cache by default - cache on success
2457                 }
2458             }
2459 
2460             if (ret != null ) {
2461                 if (!ret.setHeaders(this, p, raw)) {
2462                     ret = null;
2463                 }
2464             }
2465         }
2466         if (logger.isLoggable(PlatformLogger.Level.FINER)) {
2467             logger.finer("Server Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
2468         }
2469         return ret;
2470     }
2471 
2472     /* inclose will be true if called from close(), in which case we
2473      * force the call to check because this is the last chance to do so.
2474      * If not in close(), then the authentication info could arrive in a trailer
2475      * field, which we have not read yet.
2476      */
2477     private void checkResponseCredentials (boolean inClose) throws IOException {
2478         try {
2479             if (!needToCheck)
2480                 return;
2481             if ((validateProxy && currentProxyCredentials != null) &&
2482                 (currentProxyCredentials instanceof DigestAuthentication)) {
2483                 String raw = responses.findValue ("Proxy-Authentication-Info");
2484                 if (inClose || (raw != null)) {
2485                     DigestAuthentication da = (DigestAuthentication)
2486                         currentProxyCredentials;
2487                     da.checkResponse (raw, method, getRequestURI());
2488                     currentProxyCredentials = null;
2489                 }
2490             }
2491             if ((validateServer && currentServerCredentials != null) &&
2492                 (currentServerCredentials instanceof DigestAuthentication)) {
2493                 String raw = responses.findValue ("Authentication-Info");
2494                 if (inClose || (raw != null)) {
2495                     DigestAuthentication da = (DigestAuthentication)
2496                         currentServerCredentials;
2497                     da.checkResponse (raw, method, url);
2498                     currentServerCredentials = null;
2499                 }
2500             }
2501             if ((currentServerCredentials==null) && (currentProxyCredentials == null)) {
2502                 needToCheck = false;
2503             }
2504         } catch (IOException e) {
2505             disconnectInternal();
2506             connected = false;
2507             throw e;
2508         }
2509     }
2510 
2511    /* The request URI used in the request line for this request.
2512     * Also, needed for digest authentication
2513     */
2514 
2515     String requestURI = null;
2516 
2517     String getRequestURI() throws IOException {
2518         if (requestURI == null) {
2519             requestURI = http.getURLFile();
2520         }
2521         return requestURI;
2522     }
2523 
2524     /* Tells us whether to follow a redirect.  If so, it
2525      * closes the connection (break any keep-alive) and
2526      * resets the url, re-connects, and resets the request
2527      * property.
2528      */
2529     private boolean followRedirect() throws IOException {
2530         if (!getInstanceFollowRedirects()) {
2531             return false;
2532         }
2533 
2534         final int stat = getResponseCode();
2535         if (stat < 300 || stat > 307 || stat == 306
2536                                 || stat == HTTP_NOT_MODIFIED) {
2537             return false;
2538         }
2539         final String loc = getHeaderField("Location");
2540         if (loc == null) {
2541             /* this should be present - if not, we have no choice
2542              * but to go forward w/ the response we got
2543              */
2544             return false;
2545         }
2546 
2547         URL locUrl;
2548         try {
2549             locUrl = new URL(loc);
2550             if (!url.getProtocol().equalsIgnoreCase(locUrl.getProtocol())) {
2551                 return false;
2552             }
2553 
2554         } catch (MalformedURLException mue) {
2555           // treat loc as a relative URI to conform to popular browsers
2556           locUrl = new URL(url, loc);
2557         }
2558 
2559         final URL locUrl0 = locUrl;
2560         socketPermission = null; // force recalculation
2561         SocketPermission p = URLtoSocketPermission(locUrl);
2562 
2563         if (p != null) {
2564             try {
2565                 return AccessController.doPrivilegedWithCombiner(
2566                     new PrivilegedExceptionAction<>() {
2567                         public Boolean run() throws IOException {
2568                             return followRedirect0(loc, stat, locUrl0);
2569                         }
2570                     }, null, p
2571                 );
2572             } catch (PrivilegedActionException e) {
2573                 throw (IOException) e.getException();
2574             }
2575         } else {
2576             // run without additional permission
2577             return followRedirect0(loc, stat, locUrl);
2578         }
2579     }
2580 
2581     /* Tells us whether to follow a redirect.  If so, it
2582      * closes the connection (break any keep-alive) and
2583      * resets the url, re-connects, and resets the request
2584      * property.
2585      */
2586     private boolean followRedirect0(String loc, int stat, URL locUrl)
2587         throws IOException
2588     {
2589         disconnectInternal();
2590         if (streaming()) {
2591             throw new HttpRetryException (RETRY_MSG3, stat, loc);
2592         }
2593         if (logger.isLoggable(PlatformLogger.Level.FINE)) {
2594             logger.fine("Redirected from " + url + " to " + locUrl);
2595         }
2596 
2597         // clear out old response headers!!!!
2598         responses = new MessageHeader();
2599         if (stat == HTTP_USE_PROXY) {
2600             /* This means we must re-request the resource through the
2601              * proxy denoted in the "Location:" field of the response.
2602              * Judging by the spec, the string in the Location header
2603              * _should_ denote a URL - let's hope for "http://my.proxy.org"
2604              * Make a new HttpClient to the proxy, using HttpClient's
2605              * Instance-specific proxy fields, but note we're still fetching
2606              * the same URL.
2607              */
2608             String proxyHost = locUrl.getHost();
2609             int proxyPort = locUrl.getPort();
2610 
2611             SecurityManager security = System.getSecurityManager();
2612             if (security != null) {
2613                 security.checkConnect(proxyHost, proxyPort);
2614             }
2615 
2616             setProxiedClient (url, proxyHost, proxyPort);
2617             requests.set(0, method + " " + getRequestURI()+" "  +
2618                              httpVersion, null);
2619             connected = true;
2620             // need to remember this in case NTLM proxy authentication gets
2621             // used. We can't use transparent authentication when user
2622             // doesn't know about proxy.
2623             useProxyResponseCode = true;
2624         } else {
2625             // maintain previous headers, just change the name
2626             // of the file we're getting
2627             url = locUrl;
2628             requestURI = null; // force it to be recalculated
2629             if (method.equals("POST") && !Boolean.getBoolean("http.strictPostRedirect") && (stat!=307)) {
2630                 /* The HTTP/1.1 spec says that a redirect from a POST
2631                  * *should not* be immediately turned into a GET, and
2632                  * that some HTTP/1.0 clients incorrectly did this.
2633                  * Correct behavior redirects a POST to another POST.
2634                  * Unfortunately, since most browsers have this incorrect
2635                  * behavior, the web works this way now.  Typical usage
2636                  * seems to be:
2637                  *   POST a login code or passwd to a web page.
2638                  *   after validation, the server redirects to another
2639                  *     (welcome) page
2640                  *   The second request is (erroneously) expected to be GET
2641                  *
2642                  * We will do the incorrect thing (POST-->GET) by default.
2643                  * We will provide the capability to do the "right" thing
2644                  * (POST-->POST) by a system property, "http.strictPostRedirect=true"
2645                  */
2646 
2647                 requests = new MessageHeader();
2648                 setRequests = false;
2649                 super.setRequestMethod("GET"); // avoid the connecting check
2650                 poster = null;
2651                 if (!checkReuseConnection())
2652                     connect();
2653             } else {
2654                 if (!checkReuseConnection())
2655                     connect();
2656                 /* Even after a connect() call, http variable still can be
2657                  * null, if a ResponseCache has been installed and it returns
2658                  * a non-null CacheResponse instance. So check nullity before using it.
2659                  *
2660                  * And further, if http is null, there's no need to do anything
2661                  * about request headers because successive http session will use
2662                  * cachedInputStream/cachedHeaders anyway, which is returned by
2663                  * CacheResponse.
2664                  */
2665                 if (http != null) {
2666                     requests.set(0, method + " " + getRequestURI()+" "  +
2667                                  httpVersion, null);
2668                     int port = url.getPort();
2669                     String host = stripIPv6ZoneId(url.getHost());
2670                     if (port != -1 && port != url.getDefaultPort()) {
2671                         host += ":" + String.valueOf(port);
2672                     }
2673                     requests.set("Host", host);
2674                 }
2675             }
2676         }
2677         return true;
2678     }
2679 
2680     /* dummy byte buffer for reading off socket prior to closing */
2681     byte[] cdata = new byte [128];
2682 
2683     /**
2684      * Reset (without disconnecting the TCP conn) in order to do another transaction with this instance
2685      */
2686     private void reset() throws IOException {
2687         http.reuse = true;
2688         /* must save before calling close */
2689         reuseClient = http;
2690         InputStream is = http.getInputStream();
2691         if (!method.equals("HEAD")) {
2692             try {
2693                 /* we want to read the rest of the response without using the
2694                  * hurry mechanism, because that would close the connection
2695                  * if everything is not available immediately
2696                  */
2697                 if ((is instanceof ChunkedInputStream) ||
2698                     (is instanceof MeteredStream)) {
2699                     /* reading until eof will not block */
2700                     while (is.read (cdata) > 0) {}
2701                 } else {
2702                     /* raw stream, which will block on read, so only read
2703                      * the expected number of bytes, probably 0
2704                      */
2705                     long cl = 0;
2706                     int n = 0;
2707                     String cls = responses.findValue ("Content-Length");
2708                     if (cls != null) {
2709                         try {
2710                             cl = Long.parseLong (cls);
2711                         } catch (NumberFormatException e) {
2712                             cl = 0;
2713                         }
2714                     }
2715                     for (long i=0; i<cl; ) {
2716                         if ((n = is.read (cdata)) == -1) {
2717                             break;
2718                         } else {
2719                             i+= n;
2720                         }
2721                     }
2722                 }
2723             } catch (IOException e) {
2724                 http.reuse = false;
2725                 reuseClient = null;
2726                 disconnectInternal();
2727                 return;
2728             }
2729             try {
2730                 if (is instanceof MeteredStream) {
2731                     is.close();
2732                 }
2733             } catch (IOException e) { }
2734         }
2735         responseCode = -1;
2736         responses = new MessageHeader();
2737         connected = false;
2738     }
2739 
2740     /**
2741      * Disconnect from the web server at the first 401 error. Do not
2742      * disconnect when using a proxy, a good proxy should have already
2743      * closed the connection to the web server.
2744      */
2745     private void disconnectWeb() throws IOException {
2746         if (usingProxy() && http.isKeepingAlive()) {
2747             responseCode = -1;
2748             // clean up, particularly, skip the content part
2749             // of a 401 error response
2750             reset();
2751         } else {
2752             disconnectInternal();
2753         }
2754     }
2755 
2756     /**
2757      * Disconnect from the server (for internal use)
2758      */
2759     private void disconnectInternal() {
2760         responseCode = -1;
2761         inputStream = null;
2762         if (pi != null) {
2763             pi.finishTracking();
2764             pi = null;
2765         }
2766         if (http != null) {
2767             http.closeServer();
2768             http = null;
2769             connected = false;
2770         }
2771     }
2772 
2773     /**
2774      * Disconnect from the server (public API)
2775      */
2776     public void disconnect() {
2777 
2778         responseCode = -1;
2779         if (pi != null) {
2780             pi.finishTracking();
2781             pi = null;
2782         }
2783 
2784         if (http != null) {
2785             /*
2786              * If we have an input stream this means we received a response
2787              * from the server. That stream may have been read to EOF and
2788              * dependening on the stream type may already be closed or the
2789              * the http client may be returned to the keep-alive cache.
2790              * If the http client has been returned to the keep-alive cache
2791              * it may be closed (idle timeout) or may be allocated to
2792              * another request.
2793              *
2794              * In other to avoid timing issues we close the input stream
2795              * which will either close the underlying connection or return
2796              * the client to the cache. If there's a possibility that the
2797              * client has been returned to the cache (ie: stream is a keep
2798              * alive stream or a chunked input stream) then we remove an
2799              * idle connection to the server. Note that this approach
2800              * can be considered an approximation in that we may close a
2801              * different idle connection to that used by the request.
2802              * Additionally it's possible that we close two connections
2803              * - the first becuase it wasn't an EOF (and couldn't be
2804              * hurried) - the second, another idle connection to the
2805              * same server. The is okay because "disconnect" is an
2806              * indication that the application doesn't intend to access
2807              * this http server for a while.
2808              */
2809 
2810             if (inputStream != null) {
2811                 HttpClient hc = http;
2812 
2813                 // un-synchronized
2814                 boolean ka = hc.isKeepingAlive();
2815 
2816                 try {
2817                     inputStream.close();
2818                 } catch (IOException ioe) { }
2819 
2820                 // if the connection is persistent it may have been closed
2821                 // or returned to the keep-alive cache. If it's been returned
2822                 // to the keep-alive cache then we would like to close it
2823                 // but it may have been allocated
2824 
2825                 if (ka) {
2826                     hc.closeIdleConnection();
2827                 }
2828 
2829 
2830             } else {
2831                 // We are deliberatly being disconnected so HttpClient
2832                 // should not try to resend the request no matter what stage
2833                 // of the connection we are in.
2834                 http.setDoNotRetry(true);
2835 
2836                 http.closeServer();
2837             }
2838 
2839             //      poster = null;
2840             http = null;
2841             connected = false;
2842         }
2843         cachedInputStream = null;
2844         if (cachedHeaders != null) {
2845             cachedHeaders.reset();
2846         }
2847     }
2848 
2849     public boolean usingProxy() {
2850         if (http != null) {
2851             return (http.getProxyHostUsed() != null);
2852         }
2853         return false;
2854     }
2855 
2856     // constant strings represent set-cookie header names
2857     private static final String SET_COOKIE = "set-cookie";
2858     private static final String SET_COOKIE2 = "set-cookie2";
2859 
2860     /**
2861      * Returns a filtered version of the given headers value.
2862      *
2863      * Note: The implementation currently only filters out HttpOnly cookies
2864      *       from Set-Cookie and Set-Cookie2 headers.
2865      */
2866     private String filterHeaderField(String name, String value) {
2867         if (value == null)
2868             return null;
2869 
2870         if (SET_COOKIE.equalsIgnoreCase(name) ||
2871             SET_COOKIE2.equalsIgnoreCase(name)) {
2872 
2873             // Filtering only if there is a cookie handler. [Assumption: the
2874             // cookie handler will store/retrieve the HttpOnly cookies]
2875             if (cookieHandler == null || value.length() == 0)
2876                 return value;
2877 
2878             JavaNetHttpCookieAccess access =
2879                     SharedSecrets.getJavaNetHttpCookieAccess();
2880             StringJoiner retValue = new StringJoiner(",");  // RFC 2965, comma separated
2881             List<HttpCookie> cookies = access.parse(value);
2882             for (HttpCookie cookie : cookies) {
2883                 // skip HttpOnly cookies
2884                 if (!cookie.isHttpOnly())
2885                     retValue.add(access.header(cookie));
2886             }
2887             return retValue.toString();
2888         }
2889 
2890         return value;
2891     }
2892 
2893     // Cache the filtered response headers so that they don't need
2894     // to be generated for every getHeaderFields() call.
2895     private Map<String, List<String>> filteredHeaders;  // null
2896 
2897     private Map<String, List<String>> getFilteredHeaderFields() {
2898         if (filteredHeaders != null)
2899             return filteredHeaders;
2900 
2901         Map<String, List<String>> headers, tmpMap = new HashMap<>();
2902 
2903         if (cachedHeaders != null)
2904             headers = cachedHeaders.getHeaders();
2905         else
2906             headers = responses.getHeaders();
2907 
2908         for (Map.Entry<String, List<String>> e: headers.entrySet()) {
2909             String key = e.getKey();
2910             List<String> values = e.getValue(), filteredVals = new ArrayList<>();
2911             for (String value : values) {
2912                 String fVal = filterHeaderField(key, value);
2913                 if (fVal != null)
2914                     filteredVals.add(fVal);
2915             }
2916             if (!filteredVals.isEmpty())
2917                 tmpMap.put(key, Collections.unmodifiableList(filteredVals));
2918         }
2919 
2920         return filteredHeaders = Collections.unmodifiableMap(tmpMap);
2921     }
2922 
2923     /**
2924      * Gets a header field by name. Returns null if not known.
2925      * @param name the name of the header field
2926      */
2927     @Override
2928     public String getHeaderField(String name) {
2929         try {
2930             getInputStream();
2931         } catch (IOException e) {}
2932 
2933         if (cachedHeaders != null) {
2934             return filterHeaderField(name, cachedHeaders.findValue(name));
2935         }
2936 
2937         return filterHeaderField(name, responses.findValue(name));
2938     }
2939 
2940     /**
2941      * Returns an unmodifiable Map of the header fields.
2942      * The Map keys are Strings that represent the
2943      * response-header field names. Each Map value is an
2944      * unmodifiable List of Strings that represents
2945      * the corresponding field values.
2946      *
2947      * @return a Map of header fields
2948      * @since 1.4
2949      */
2950     @Override
2951     public Map<String, List<String>> getHeaderFields() {
2952         try {
2953             getInputStream();
2954         } catch (IOException e) {}
2955 
2956         return getFilteredHeaderFields();
2957     }
2958 
2959     /**
2960      * Gets a header field by index. Returns null if not known.
2961      * @param n the index of the header field
2962      */
2963     @Override
2964     public String getHeaderField(int n) {
2965         try {
2966             getInputStream();
2967         } catch (IOException e) {}
2968 
2969         if (cachedHeaders != null) {
2970            return filterHeaderField(cachedHeaders.getKey(n),
2971                                     cachedHeaders.getValue(n));
2972         }
2973         return filterHeaderField(responses.getKey(n), responses.getValue(n));
2974     }
2975 
2976     /**
2977      * Gets a header field by index. Returns null if not known.
2978      * @param n the index of the header field
2979      */
2980     @Override
2981     public String getHeaderFieldKey(int n) {
2982         try {
2983             getInputStream();
2984         } catch (IOException e) {}
2985 
2986         if (cachedHeaders != null) {
2987             return cachedHeaders.getKey(n);
2988         }
2989 
2990         return responses.getKey(n);
2991     }
2992 
2993     /**
2994      * Sets request property. If a property with the key already
2995      * exists, overwrite its value with the new value.
2996      * @param value the value to be set
2997      */
2998     @Override
2999     public synchronized void setRequestProperty(String key, String value) {
3000         if (connected || connecting)
3001             throw new IllegalStateException("Already connected");
3002         if (key == null)
3003             throw new NullPointerException ("key is null");
3004 
3005         if (isExternalMessageHeaderAllowed(key, value)) {
3006             requests.set(key, value);
3007             if (!key.equalsIgnoreCase("Content-Type")) {
3008                 userHeaders.set(key, value);
3009             }
3010         }
3011     }
3012 
3013     MessageHeader getUserSetHeaders() {
3014         return userHeaders;
3015     }
3016 
3017     /**
3018      * Adds a general request property specified by a
3019      * key-value pair.  This method will not overwrite
3020      * existing values associated with the same key.
3021      *
3022      * @param   key     the keyword by which the request is known
3023      *                  (e.g., "<code>accept</code>").
3024      * @param   value  the value associated with it.
3025      * @see #getRequestProperties(java.lang.String)
3026      * @since 1.4
3027      */
3028     @Override
3029     public synchronized void addRequestProperty(String key, String value) {
3030         if (connected || connecting)
3031             throw new IllegalStateException("Already connected");
3032         if (key == null)
3033             throw new NullPointerException ("key is null");
3034 
3035         if (isExternalMessageHeaderAllowed(key, value)) {
3036             requests.add(key, value);
3037             if (!key.equalsIgnoreCase("Content-Type")) {
3038                     userHeaders.add(key, value);
3039             }
3040         }
3041     }
3042 
3043     //
3044     // Set a property for authentication.  This can safely disregard
3045     // the connected test.
3046     //
3047     public void setAuthenticationProperty(String key, String value) {
3048         checkMessageHeader(key, value);
3049         requests.set(key, value);
3050     }
3051 
3052     @Override
3053     public synchronized String getRequestProperty (String key) {
3054         if (key == null) {
3055             return null;
3056         }
3057 
3058         // don't return headers containing security sensitive information
3059         for (int i=0; i < EXCLUDE_HEADERS.length; i++) {
3060             if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
3061                 return null;
3062             }
3063         }
3064         if (!setUserCookies) {
3065             if (key.equalsIgnoreCase("Cookie")) {
3066                 return userCookies;
3067             }
3068             if (key.equalsIgnoreCase("Cookie2")) {
3069                 return userCookies2;
3070             }
3071         }
3072         return requests.findValue(key);
3073     }
3074 
3075     /**
3076      * Returns an unmodifiable Map of general request
3077      * properties for this connection. The Map keys
3078      * are Strings that represent the request-header
3079      * field names. Each Map value is a unmodifiable List
3080      * of Strings that represents the corresponding
3081      * field values.
3082      *
3083      * @return  a Map of the general request properties for this connection.
3084      * @throws IllegalStateException if already connected
3085      * @since 1.4
3086      */
3087     @Override
3088     public synchronized Map<String, List<String>> getRequestProperties() {
3089         if (connected)
3090             throw new IllegalStateException("Already connected");
3091 
3092         // exclude headers containing security-sensitive info
3093         if (setUserCookies) {
3094             return requests.getHeaders(EXCLUDE_HEADERS);
3095         }
3096         /*
3097          * The cookies in the requests message headers may have
3098          * been modified. Use the saved user cookies instead.
3099          */
3100         Map<String, List<String>> userCookiesMap = null;
3101         if (userCookies != null || userCookies2 != null) {
3102             userCookiesMap = new HashMap<>();
3103             if (userCookies != null) {
3104                 userCookiesMap.put("Cookie", Arrays.asList(userCookies));
3105             }
3106             if (userCookies2 != null) {
3107                 userCookiesMap.put("Cookie2", Arrays.asList(userCookies2));
3108             }
3109         }
3110         return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap);
3111     }
3112 
3113     @Override
3114     public void setConnectTimeout(int timeout) {
3115         if (timeout < 0)
3116             throw new IllegalArgumentException("timeouts can't be negative");
3117         connectTimeout = timeout;
3118     }
3119 
3120 
3121     /**
3122      * Returns setting for connect timeout.
3123      * <p>
3124      * 0 return implies that the option is disabled
3125      * (i.e., timeout of infinity).
3126      *
3127      * @return an <code>int</code> that indicates the connect timeout
3128      *         value in milliseconds
3129      * @see java.net.URLConnection#setConnectTimeout(int)
3130      * @see java.net.URLConnection#connect()
3131      * @since 1.5
3132      */
3133     @Override
3134     public int getConnectTimeout() {
3135         return (connectTimeout < 0 ? 0 : connectTimeout);
3136     }
3137 
3138     /**
3139      * Sets the read timeout to a specified timeout, in
3140      * milliseconds. A non-zero value specifies the timeout when
3141      * reading from Input stream when a connection is established to a
3142      * resource. If the timeout expires before there is data available
3143      * for read, a java.net.SocketTimeoutException is raised. A
3144      * timeout of zero is interpreted as an infinite timeout.
3145      *
3146      * <p> Some non-standard implementation of this method ignores the
3147      * specified timeout. To see the read timeout set, please call
3148      * getReadTimeout().
3149      *
3150      * @param timeout an <code>int</code> that specifies the timeout
3151      * value to be used in milliseconds
3152      * @throws IllegalArgumentException if the timeout parameter is negative
3153      *
3154      * @see java.net.URLConnectiongetReadTimeout()
3155      * @see java.io.InputStream#read()
3156      * @since 1.5
3157      */
3158     @Override
3159     public void setReadTimeout(int timeout) {
3160         if (timeout < 0)
3161             throw new IllegalArgumentException("timeouts can't be negative");
3162         readTimeout = timeout;
3163     }
3164 
3165     /**
3166      * Returns setting for read timeout. 0 return implies that the
3167      * option is disabled (i.e., timeout of infinity).
3168      *
3169      * @return an <code>int</code> that indicates the read timeout
3170      *         value in milliseconds
3171      *
3172      * @see java.net.URLConnection#setReadTimeout(int)
3173      * @see java.io.InputStream#read()
3174      * @since 1.5
3175      */
3176     @Override
3177     public int getReadTimeout() {
3178         return readTimeout < 0 ? 0 : readTimeout;
3179     }
3180 
3181     public CookieHandler getCookieHandler() {
3182         return cookieHandler;
3183     }
3184 
3185     String getMethod() {
3186         return method;
3187     }
3188 
3189     private MessageHeader mapToMessageHeader(Map<String, List<String>> map) {
3190         MessageHeader headers = new MessageHeader();
3191         if (map == null || map.isEmpty()) {
3192             return headers;
3193         }
3194         for (Map.Entry<String, List<String>> entry : map.entrySet()) {
3195             String key = entry.getKey();
3196             List<String> values = entry.getValue();
3197             for (String value : values) {
3198                 if (key == null) {
3199                     headers.prepend(key, value);
3200                 } else {
3201                     headers.add(key, value);
3202                 }
3203             }
3204         }
3205         return headers;
3206     }
3207 
3208     /**
3209      * Returns the given host, without the IPv6 Zone Id, if present.
3210      * (e.g. [fe80::a00:27ff:aaaa:aaaa%eth0] -> [fe80::a00:27ff:aaaa:aaaa])
3211      *
3212      * @param host host address (not null, not empty)
3213      * @return host address without Zone Id
3214      */
3215     static String stripIPv6ZoneId(String host) {
3216         if (host.charAt(0) != '[') { // not an IPv6-literal
3217             return host;
3218         }
3219         int i = host.lastIndexOf('%');
3220         if (i == -1) { // doesn't contain zone_id
3221             return host;
3222         }
3223         return host.substring(0, i) + "]";
3224     }
3225 
3226     /* The purpose of this wrapper is just to capture the close() call
3227      * so we can check authentication information that may have
3228      * arrived in a Trailer field
3229      */
3230     class HttpInputStream extends FilterInputStream {
3231         private CacheRequest cacheRequest;
3232         private OutputStream outputStream;
3233         private boolean marked = false;
3234         private int inCache = 0;
3235         private int markCount = 0;
3236         private boolean closed;  // false
3237 
3238         public HttpInputStream (InputStream is) {
3239             super (is);
3240             this.cacheRequest = null;
3241             this.outputStream = null;
3242         }
3243 
3244         public HttpInputStream (InputStream is, CacheRequest cacheRequest) {
3245             super (is);
3246             this.cacheRequest = cacheRequest;
3247             try {
3248                 this.outputStream = cacheRequest.getBody();
3249             } catch (IOException ioex) {
3250                 this.cacheRequest.abort();
3251                 this.cacheRequest = null;
3252                 this.outputStream = null;
3253             }
3254         }
3255 
3256         /**
3257          * Marks the current position in this input stream. A subsequent
3258          * call to the <code>reset</code> method repositions this stream at
3259          * the last marked position so that subsequent reads re-read the same
3260          * bytes.
3261          * <p>
3262          * The <code>readlimit</code> argument tells this input stream to
3263          * allow that many bytes to be read before the mark position gets
3264          * invalidated.
3265          * <p>
3266          * This method simply performs <code>in.mark(readlimit)</code>.
3267          *
3268          * @param   readlimit   the maximum limit of bytes that can be read before
3269          *                      the mark position becomes invalid.
3270          * @see     java.io.FilterInputStream#in
3271          * @see     java.io.FilterInputStream#reset()
3272          */
3273         @Override
3274         public synchronized void mark(int readlimit) {
3275             super.mark(readlimit);
3276             if (cacheRequest != null) {
3277                 marked = true;
3278                 markCount = 0;
3279             }
3280         }
3281 
3282         /**
3283          * Repositions this stream to the position at the time the
3284          * <code>mark</code> method was last called on this input stream.
3285          * <p>
3286          * This method
3287          * simply performs <code>in.reset()</code>.
3288          * <p>
3289          * Stream marks are intended to be used in
3290          * situations where you need to read ahead a little to see what's in
3291          * the stream. Often this is most easily done by invoking some
3292          * general parser. If the stream is of the type handled by the
3293          * parse, it just chugs along happily. If the stream is not of
3294          * that type, the parser should toss an exception when it fails.
3295          * If this happens within readlimit bytes, it allows the outer
3296          * code to reset the stream and try another parser.
3297          *
3298          * @exception  IOException  if the stream has not been marked or if the
3299          *               mark has been invalidated.
3300          * @see        java.io.FilterInputStream#in
3301          * @see        java.io.FilterInputStream#mark(int)
3302          */
3303         @Override
3304         public synchronized void reset() throws IOException {
3305             super.reset();
3306             if (cacheRequest != null) {
3307                 marked = false;
3308                 inCache += markCount;
3309             }
3310         }
3311 
3312         private void ensureOpen() throws IOException {
3313             if (closed)
3314                 throw new IOException("stream is closed");
3315         }
3316 
3317         @Override
3318         public int read() throws IOException {
3319             ensureOpen();
3320             try {
3321                 byte[] b = new byte[1];
3322                 int ret = read(b);
3323                 return (ret == -1? ret : (b[0] & 0x00FF));
3324             } catch (IOException ioex) {
3325                 if (cacheRequest != null) {
3326                     cacheRequest.abort();
3327                 }
3328                 throw ioex;
3329             }
3330         }
3331 
3332         @Override
3333         public int read(byte[] b) throws IOException {
3334             return read(b, 0, b.length);
3335         }
3336 
3337         @Override
3338         public int read(byte[] b, int off, int len) throws IOException {
3339             ensureOpen();
3340             try {
3341                 int newLen = super.read(b, off, len);
3342                 int nWrite;
3343                 // write to cache
3344                 if (inCache > 0) {
3345                     if (inCache >= newLen) {
3346                         inCache -= newLen;
3347                         nWrite = 0;
3348                     } else {
3349                         nWrite = newLen - inCache;
3350                         inCache = 0;
3351                     }
3352                 } else {
3353                     nWrite = newLen;
3354                 }
3355                 if (nWrite > 0 && outputStream != null)
3356                     outputStream.write(b, off + (newLen-nWrite), nWrite);
3357                 if (marked) {
3358                     markCount += newLen;
3359                 }
3360                 return newLen;
3361             } catch (IOException ioex) {
3362                 if (cacheRequest != null) {
3363                     cacheRequest.abort();
3364                 }
3365                 throw ioex;
3366             }
3367         }
3368 
3369         /* skip() calls read() in order to ensure that entire response gets
3370          * cached. same implementation as InputStream.skip */
3371 
3372         private byte[] skipBuffer;
3373         private static final int SKIP_BUFFER_SIZE = 8096;
3374 
3375         @Override
3376         public long skip (long n) throws IOException {
3377             ensureOpen();
3378             long remaining = n;
3379             int nr;
3380             if (skipBuffer == null)
3381                 skipBuffer = new byte[SKIP_BUFFER_SIZE];
3382 
3383             byte[] localSkipBuffer = skipBuffer;
3384 
3385             if (n <= 0) {
3386                 return 0;
3387             }
3388 
3389             while (remaining > 0) {
3390                 nr = read(localSkipBuffer, 0,
3391                           (int) Math.min(SKIP_BUFFER_SIZE, remaining));
3392                 if (nr < 0) {
3393                     break;
3394                 }
3395                 remaining -= nr;
3396             }
3397 
3398             return n - remaining;
3399         }
3400 
3401         @Override
3402         public void close () throws IOException {
3403             if (closed)
3404                 return;
3405 
3406             try {
3407                 if (outputStream != null) {
3408                     if (read() != -1) {
3409                         cacheRequest.abort();
3410                     } else {
3411                         outputStream.close();
3412                     }
3413                 }
3414                 super.close ();
3415             } catch (IOException ioex) {
3416                 if (cacheRequest != null) {
3417                     cacheRequest.abort();
3418                 }
3419                 throw ioex;
3420             } finally {
3421                 closed = true;
3422                 HttpURLConnection.this.http = null;
3423                 checkResponseCredentials (true);
3424             }
3425         }
3426     }
3427 
3428     class StreamingOutputStream extends FilterOutputStream {
3429 
3430         long expected;
3431         long written;
3432         boolean closed;
3433         boolean error;
3434         IOException errorExcp;
3435 
3436         /**
3437          * expectedLength == -1 if the stream is chunked
3438          * expectedLength > 0 if the stream is fixed content-length
3439          *    In the 2nd case, we make sure the expected number of
3440          *    of bytes are actually written
3441          */
3442         StreamingOutputStream (OutputStream os, long expectedLength) {
3443             super (os);
3444             expected = expectedLength;
3445             written = 0L;
3446             closed = false;
3447             error = false;
3448         }
3449 
3450         @Override
3451         public void write (int b) throws IOException {
3452             checkError();
3453             written ++;
3454             if (expected != -1L && written > expected) {
3455                 throw new IOException ("too many bytes written");
3456             }
3457             out.write (b);
3458         }
3459 
3460         @Override
3461         public void write (byte[] b) throws IOException {
3462             write (b, 0, b.length);
3463         }
3464 
3465         @Override
3466         public void write (byte[] b, int off, int len) throws IOException {
3467             checkError();
3468             written += len;
3469             if (expected != -1L && written > expected) {
3470                 out.close ();
3471                 throw new IOException ("too many bytes written");
3472             }
3473             out.write (b, off, len);
3474         }
3475 
3476         void checkError () throws IOException {
3477             if (closed) {
3478                 throw new IOException ("Stream is closed");
3479             }
3480             if (error) {
3481                 throw errorExcp;
3482             }
3483             if (((PrintStream)out).checkError()) {
3484                 throw new IOException("Error writing request body to server");
3485             }
3486         }
3487 
3488         /* this is called to check that all the bytes
3489          * that were supposed to be written were written
3490          * and that the stream is now closed().
3491          */
3492         boolean writtenOK () {
3493             return closed && ! error;
3494         }
3495 
3496         @Override
3497         public void close () throws IOException {
3498             if (closed) {
3499                 return;
3500             }
3501             closed = true;
3502             if (expected != -1L) {
3503                 /* not chunked */
3504                 if (written != expected) {
3505                     error = true;
3506                     errorExcp = new IOException ("insufficient data written");
3507                     out.close ();
3508                     throw errorExcp;
3509                 }
3510                 super.flush(); /* can't close the socket */
3511             } else {
3512                 /* chunked */
3513                 super.close (); /* force final chunk to be written */
3514                 /* trailing \r\n */
3515                 OutputStream o = http.getOutputStream();
3516                 o.write ('\r');
3517                 o.write ('\n');
3518                 o.flush();
3519             }
3520         }
3521     }
3522 
3523 
3524     static class ErrorStream extends InputStream {
3525         ByteBuffer buffer;
3526         InputStream is;
3527 
3528         private ErrorStream(ByteBuffer buf) {
3529             buffer = buf;
3530             is = null;
3531         }
3532 
3533         private ErrorStream(ByteBuffer buf, InputStream is) {
3534             buffer = buf;
3535             this.is = is;
3536         }
3537 
3538         // when this method is called, it's either the case that cl > 0, or
3539         // if chunk-encoded, cl = -1; in other words, cl can't be 0
3540         public static InputStream getErrorStream(InputStream is, long cl, HttpClient http) {
3541 
3542             // cl can't be 0; this following is here for extra precaution
3543             if (cl == 0) {
3544                 return null;
3545             }
3546 
3547             try {
3548                 // set SO_TIMEOUT to 1/5th of the total timeout
3549                 // remember the old timeout value so that we can restore it
3550                 int oldTimeout = http.getReadTimeout();
3551                 http.setReadTimeout(timeout4ESBuffer/5);
3552 
3553                 long expected = 0;
3554                 boolean isChunked = false;
3555                 // the chunked case
3556                 if (cl < 0) {
3557                     expected = bufSize4ES;
3558                     isChunked = true;
3559                 } else {
3560                     expected = cl;
3561                 }
3562                 if (expected <= bufSize4ES) {
3563                     int exp = (int) expected;
3564                     byte[] buffer = new byte[exp];
3565                     int count = 0, time = 0, len = 0;
3566                     do {
3567                         try {
3568                             len = is.read(buffer, count,
3569                                              buffer.length - count);
3570                             if (len < 0) {
3571                                 if (isChunked) {
3572                                     // chunked ended
3573                                     // if chunked ended prematurely,
3574                                     // an IOException would be thrown
3575                                     break;
3576                                 }
3577                                 // the server sends less than cl bytes of data
3578                                 throw new IOException("the server closes"+
3579                                                       " before sending "+cl+
3580                                                       " bytes of data");
3581                             }
3582                             count += len;
3583                         } catch (SocketTimeoutException ex) {
3584                             time += timeout4ESBuffer/5;
3585                         }
3586                     } while (count < exp && time < timeout4ESBuffer);
3587 
3588                     // reset SO_TIMEOUT to old value
3589                     http.setReadTimeout(oldTimeout);
3590 
3591                     // if count < cl at this point, we will not try to reuse
3592                     // the connection
3593                     if (count == 0) {
3594                         // since we haven't read anything,
3595                         // we will return the underlying
3596                         // inputstream back to the application
3597                         return null;
3598                     }  else if ((count == expected && !(isChunked)) || (isChunked && len <0)) {
3599                         // put the connection into keep-alive cache
3600                         // the inputstream will try to do the right thing
3601                         is.close();
3602                         return new ErrorStream(ByteBuffer.wrap(buffer, 0, count));
3603                     } else {
3604                         // we read part of the response body
3605                         return new ErrorStream(
3606                                       ByteBuffer.wrap(buffer, 0, count), is);
3607                     }
3608                 }
3609                 return null;
3610             } catch (IOException ioex) {
3611                 // ioex.printStackTrace();
3612                 return null;
3613             }
3614         }
3615 
3616         @Override
3617         public int available() throws IOException {
3618             if (is == null) {
3619                 return buffer.remaining();
3620             } else {
3621                 return buffer.remaining()+is.available();
3622             }
3623         }
3624 
3625         public int read() throws IOException {
3626             byte[] b = new byte[1];
3627             int ret = read(b);
3628             return (ret == -1? ret : (b[0] & 0x00FF));
3629         }
3630 
3631         @Override
3632         public int read(byte[] b) throws IOException {
3633             return read(b, 0, b.length);
3634         }
3635 
3636         @Override
3637         public int read(byte[] b, int off, int len) throws IOException {
3638             int rem = buffer.remaining();
3639             if (rem > 0) {
3640                 int ret = rem < len? rem : len;
3641                 buffer.get(b, off, ret);
3642                 return ret;
3643             } else {
3644                 if (is == null) {
3645                     return -1;
3646                 } else {
3647                     return is.read(b, off, len);
3648                 }
3649             }
3650         }
3651 
3652         @Override
3653         public void close() throws IOException {
3654             buffer = null;
3655             if (is != null) {
3656                 is.close();
3657             }
3658         }
3659     }
3660 }
3661 
3662 /** An input stream that just returns EOF.  This is for
3663  * HTTP URLConnections that are KeepAlive && use the
3664  * HEAD method - i.e., stream not dead, but nothing to be read.
3665  */
3666 
3667 class EmptyInputStream extends InputStream {
3668 
3669     @Override
3670     public int available() {
3671         return 0;
3672     }
3673 
3674     public int read() {
3675         return -1;
3676     }
3677 }