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