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