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                     if (logger.isLoggable(PlatformLogger.FINEST)) {
2069                         logger.finest("Unknown/Unsupported authentication scheme: " + scheme);
2070                     }
2071                 /*fall through*/
2072                 default:
2073                     throw new AssertionError("should not reach here");
2074                 }
2075             }
2076             // For backwards compatibility, we also try defaultAuth
2077             // REMIND:  Get rid of this for JDK2.0.
2078 
2079             if (ret == null && defaultAuth != null
2080                 && defaultAuth.schemeSupported(scheme)) {
2081                 try {
2082                     URL u = new URL("http", host, port, "/");
2083                     String a = defaultAuth.authString(u, scheme, realm);
2084                     if (a != null) {
2085                         ret = new BasicAuthentication (true, host, port, realm, a);
2086                         // not in cache by default - cache on success
2087                     }
2088                 } catch (java.net.MalformedURLException ignored) {
2089                 }
2090             }
2091             if (ret != null) {
2092                 if (!ret.setHeaders(this, p, raw)) {
2093                     ret = null;
2094                 }
2095             }
2096         }
2097         if (logger.isLoggable(PlatformLogger.FINER)) {
2098             logger.finer("Proxy Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
2099         }
2100         return ret;
2101     }
2102 
2103     /**
2104      * Gets the authentication for an HTTP server, and applies it to
2105      * the connection.
2106      * @param authHdr the AuthenticationHeader which tells what auth scheme is
2107      * prefered.
2108      */
2109     @SuppressWarnings("fallthrough")
2110     private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr) {
2111         /* get authorization from authenticator */
2112         AuthenticationInfo ret = null;
2113         String raw = authhdr.raw();
2114         /* When we get an NTLM auth from cache, don't set any special headers */
2115         if (authhdr.isPresent()) {
2116             HeaderParser p = authhdr.headerParser();
2117             String realm = p.findValue("realm");
2118             String scheme = authhdr.scheme();
2119             AuthScheme authScheme = UNKNOWN;
2120             if ("basic".equalsIgnoreCase(scheme)) {
2121                 authScheme = BASIC;
2122             } else if ("digest".equalsIgnoreCase(scheme)) {
2123                 authScheme = DIGEST;
2124             } else if ("ntlm".equalsIgnoreCase(scheme)) {
2125                 authScheme = NTLM;
2126                 doingNTLM2ndStage = true;
2127             } else if ("Kerberos".equalsIgnoreCase(scheme)) {
2128                 authScheme = KERBEROS;
2129                 doingNTLM2ndStage = true;
2130             } else if ("Negotiate".equalsIgnoreCase(scheme)) {
2131                 authScheme = NEGOTIATE;
2132                 doingNTLM2ndStage = true;
2133             }
2134 
2135             domain = p.findValue ("domain");
2136             if (realm == null)
2137                 realm = "";
2138             serverAuthKey = AuthenticationInfo.getServerAuthKey(url, realm, authScheme);
2139             ret = AuthenticationInfo.getServerAuth(serverAuthKey);
2140             InetAddress addr = null;
2141             if (ret == null) {
2142                 try {
2143                     addr = InetAddress.getByName(url.getHost());
2144                 } catch (java.net.UnknownHostException ignored) {
2145                     // User will have addr = null
2146                 }
2147             }
2148             // replacing -1 with default port for a protocol
2149             int port = url.getPort();
2150             if (port == -1) {
2151                 port = url.getDefaultPort();
2152             }
2153             if (ret == null) {
2154                 switch(authScheme) {
2155                 case KERBEROS:
2156                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
2157                     break;
2158                 case NEGOTIATE:
2159                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
2160                     break;
2161                 case BASIC:
2162                     PasswordAuthentication a =
2163                         privilegedRequestPasswordAuthentication(
2164                             url.getHost(), addr, port, url.getProtocol(),
2165                             realm, scheme, url, RequestorType.SERVER);
2166                     if (a != null) {
2167                         ret = new BasicAuthentication(false, url, realm, a);
2168                     }
2169                     break;
2170                 case DIGEST:
2171                     a = privilegedRequestPasswordAuthentication(
2172                             url.getHost(), addr, port, url.getProtocol(),
2173                             realm, scheme, url, RequestorType.SERVER);
2174                     if (a != null) {
2175                         digestparams = new DigestAuthentication.Parameters();
2176                         ret = new DigestAuthentication(false, url, realm, scheme, a, digestparams);
2177                     }
2178                     break;
2179                 case NTLM:
2180                     if (NTLMAuthenticationProxy.supported) {
2181                         URL url1;
2182                         try {
2183                             url1 = new URL (url, "/"); /* truncate the path */
2184                         } catch (Exception e) {
2185                             url1 = url;
2186                         }
2187 
2188                         /* tryTransparentNTLMServer will always be true the first
2189                          * time around, but verify that the platform supports it
2190                          * otherwise don't try. */
2191                         if (tryTransparentNTLMServer) {
2192                             tryTransparentNTLMServer =
2193                                     NTLMAuthenticationProxy.supportsTransparentAuth;
2194                             /* If the platform supports transparent authentication
2195                              * then check if we are in a secure environment
2196                              * whether, or not, we should try transparent authentication.*/
2197                             if (tryTransparentNTLMServer) {
2198                                 tryTransparentNTLMServer =
2199                                         NTLMAuthenticationProxy.isTrustedSite(url);
2200                             }
2201                         }
2202                         a = null;
2203                         if (tryTransparentNTLMServer) {
2204                             logger.finest("Trying Transparent NTLM authentication");
2205                         } else {
2206                             a = privilegedRequestPasswordAuthentication(
2207                                 url.getHost(), addr, port, url.getProtocol(),
2208                                 "", scheme, url, RequestorType.SERVER);
2209                         }
2210 
2211                         /* If we are not trying transparent authentication then
2212                          * we need to have a PasswordAuthentication instance. For
2213                          * transparent authentication (Windows only) the username
2214                          * and password will be picked up from the current logged
2215                          * on users credentials.
2216                          */
2217                         if (tryTransparentNTLMServer ||
2218                               (!tryTransparentNTLMServer && a != null)) {
2219                             ret = NTLMAuthenticationProxy.proxy.create(false, url1, a);
2220                         }
2221 
2222                         /* set to false so that we do not try again */
2223                         tryTransparentNTLMServer = false;
2224                     }
2225                     break;
2226                 case UNKNOWN:
2227                     if (logger.isLoggable(PlatformLogger.FINEST)) {
2228                         logger.finest("Unknown/Unsupported authentication scheme: " + scheme);
2229                     }
2230                 /*fall through*/
2231                 default:
2232                     throw new AssertionError("should not reach here");
2233                 }
2234             }
2235 
2236             // For backwards compatibility, we also try defaultAuth
2237             // REMIND:  Get rid of this for JDK2.0.
2238 
2239             if (ret == null && defaultAuth != null
2240                 && defaultAuth.schemeSupported(scheme)) {
2241                 String a = defaultAuth.authString(url, scheme, realm);
2242                 if (a != null) {
2243                     ret = new BasicAuthentication (false, url, realm, a);
2244                     // not in cache by default - cache on success
2245                 }
2246             }
2247 
2248             if (ret != null ) {
2249                 if (!ret.setHeaders(this, p, raw)) {
2250                     ret = null;
2251                 }
2252             }
2253         }
2254         if (logger.isLoggable(PlatformLogger.FINER)) {
2255             logger.finer("Server Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
2256         }
2257         return ret;
2258     }
2259 
2260     /* inclose will be true if called from close(), in which case we
2261      * force the call to check because this is the last chance to do so.
2262      * If not in close(), then the authentication info could arrive in a trailer
2263      * field, which we have not read yet.
2264      */
2265     private void checkResponseCredentials (boolean inClose) throws IOException {
2266         try {
2267             if (!needToCheck)
2268                 return;
2269             if ((validateProxy && currentProxyCredentials != null) &&
2270                 (currentProxyCredentials instanceof DigestAuthentication)) {
2271                 String raw = responses.findValue ("Proxy-Authentication-Info");
2272                 if (inClose || (raw != null)) {
2273                     DigestAuthentication da = (DigestAuthentication)
2274                         currentProxyCredentials;
2275                     da.checkResponse (raw, method, getRequestURI());
2276                     currentProxyCredentials = null;
2277                 }
2278             }
2279             if ((validateServer && currentServerCredentials != null) &&
2280                 (currentServerCredentials instanceof DigestAuthentication)) {
2281                 String raw = responses.findValue ("Authentication-Info");
2282                 if (inClose || (raw != null)) {
2283                     DigestAuthentication da = (DigestAuthentication)
2284                         currentServerCredentials;
2285                     da.checkResponse (raw, method, url);
2286                     currentServerCredentials = null;
2287                 }
2288             }
2289             if ((currentServerCredentials==null) && (currentProxyCredentials == null)) {
2290                 needToCheck = false;
2291             }
2292         } catch (IOException e) {
2293             disconnectInternal();
2294             connected = false;
2295             throw e;
2296         }
2297     }
2298 
2299    /* The request URI used in the request line for this request.
2300     * Also, needed for digest authentication
2301     */
2302 
2303     String requestURI = null;
2304 
2305     String getRequestURI() throws IOException {
2306         if (requestURI == null) {
2307             requestURI = http.getURLFile();
2308         }
2309         return requestURI;
2310     }
2311 
2312     /* Tells us whether to follow a redirect.  If so, it
2313      * closes the connection (break any keep-alive) and
2314      * resets the url, re-connects, and resets the request
2315      * property.
2316      */
2317     private boolean followRedirect() throws IOException {
2318         if (!getInstanceFollowRedirects()) {
2319             return false;
2320         }
2321 
2322         int stat = getResponseCode();
2323         if (stat < 300 || stat > 307 || stat == 306
2324                                 || stat == HTTP_NOT_MODIFIED) {
2325             return false;
2326         }
2327         String loc = getHeaderField("Location");
2328         if (loc == null) {
2329             /* this should be present - if not, we have no choice
2330              * but to go forward w/ the response we got
2331              */
2332             return false;
2333         }
2334         URL locUrl;
2335         try {
2336             locUrl = new URL(loc);
2337             if (!url.getProtocol().equalsIgnoreCase(locUrl.getProtocol())) {
2338                 return false;
2339             }
2340 
2341         } catch (MalformedURLException mue) {
2342           // treat loc as a relative URI to conform to popular browsers
2343           locUrl = new URL(url, loc);
2344         }
2345         disconnectInternal();
2346         if (streaming()) {
2347             throw new HttpRetryException (RETRY_MSG3, stat, loc);
2348         }
2349         if (logger.isLoggable(PlatformLogger.FINE)) {
2350             logger.fine("Redirected from " + url + " to " + locUrl);
2351         }
2352 
2353         // clear out old response headers!!!!
2354         responses = new MessageHeader();
2355         if (stat == HTTP_USE_PROXY) {
2356             /* This means we must re-request the resource through the
2357              * proxy denoted in the "Location:" field of the response.
2358              * Judging by the spec, the string in the Location header
2359              * _should_ denote a URL - let's hope for "http://my.proxy.org"
2360              * Make a new HttpClient to the proxy, using HttpClient's
2361              * Instance-specific proxy fields, but note we're still fetching
2362              * the same URL.
2363              */
2364             String proxyHost = locUrl.getHost();
2365             int proxyPort = locUrl.getPort();
2366 
2367             SecurityManager security = System.getSecurityManager();
2368             if (security != null) {
2369                 security.checkConnect(proxyHost, proxyPort);
2370             }
2371 
2372             setProxiedClient (url, proxyHost, proxyPort);
2373             requests.set(0, method + " " + getRequestURI()+" "  +
2374                              httpVersion, null);
2375             connected = true;
2376         } else {
2377             // maintain previous headers, just change the name
2378             // of the file we're getting
2379             url = locUrl;
2380             requestURI = null; // force it to be recalculated
2381             if (method.equals("POST") && !Boolean.getBoolean("http.strictPostRedirect") && (stat!=307)) {
2382                 /* The HTTP/1.1 spec says that a redirect from a POST
2383                  * *should not* be immediately turned into a GET, and
2384                  * that some HTTP/1.0 clients incorrectly did this.
2385                  * Correct behavior redirects a POST to another POST.
2386                  * Unfortunately, since most browsers have this incorrect
2387                  * behavior, the web works this way now.  Typical usage
2388                  * seems to be:
2389                  *   POST a login code or passwd to a web page.
2390                  *   after validation, the server redirects to another
2391                  *     (welcome) page
2392                  *   The second request is (erroneously) expected to be GET
2393                  *
2394                  * We will do the incorrect thing (POST-->GET) by default.
2395                  * We will provide the capability to do the "right" thing
2396                  * (POST-->POST) by a system property, "http.strictPostRedirect=true"
2397                  */
2398 
2399                 requests = new MessageHeader();
2400                 setRequests = false;
2401                 setRequestMethod("GET");
2402                 poster = null;
2403                 if (!checkReuseConnection())
2404                     connect();
2405             } else {
2406                 if (!checkReuseConnection())
2407                     connect();
2408                 /* Even after a connect() call, http variable still can be
2409                  * null, if a ResponseCache has been installed and it returns
2410                  * a non-null CacheResponse instance. So check nullity before using it.
2411                  *
2412                  * And further, if http is null, there's no need to do anything
2413                  * about request headers because successive http session will use
2414                  * cachedInputStream/cachedHeaders anyway, which is returned by
2415                  * CacheResponse.
2416                  */
2417                 if (http != null) {
2418                     requests.set(0, method + " " + getRequestURI()+" "  +
2419                                  httpVersion, null);
2420                     int port = url.getPort();
2421                     String host = url.getHost();
2422                     if (port != -1 && port != url.getDefaultPort()) {
2423                         host += ":" + String.valueOf(port);
2424                     }
2425                     requests.set("Host", host);
2426                 }
2427             }
2428         }
2429         return true;
2430     }
2431 
2432     /* dummy byte buffer for reading off socket prior to closing */
2433     byte[] cdata = new byte [128];
2434 
2435     /**
2436      * Reset (without disconnecting the TCP conn) in order to do another transaction with this instance
2437      */
2438     private void reset() throws IOException {
2439         http.reuse = true;
2440         /* must save before calling close */
2441         reuseClient = http;
2442         InputStream is = http.getInputStream();
2443         if (!method.equals("HEAD")) {
2444             try {
2445                 /* we want to read the rest of the response without using the
2446                  * hurry mechanism, because that would close the connection
2447                  * if everything is not available immediately
2448                  */
2449                 if ((is instanceof ChunkedInputStream) ||
2450                     (is instanceof MeteredStream)) {
2451                     /* reading until eof will not block */
2452                     while (is.read (cdata) > 0) {}
2453                 } else {
2454                     /* raw stream, which will block on read, so only read
2455                      * the expected number of bytes, probably 0
2456                      */
2457                     long cl = 0;
2458                     int n = 0;
2459                     String cls = responses.findValue ("Content-Length");
2460                     if (cls != null) {
2461                         try {
2462                             cl = Long.parseLong (cls);
2463                         } catch (NumberFormatException e) {
2464                             cl = 0;
2465                         }
2466                     }
2467                     for (long i=0; i<cl; ) {
2468                         if ((n = is.read (cdata)) == -1) {
2469                             break;
2470                         } else {
2471                             i+= n;
2472                         }
2473                     }
2474                 }
2475             } catch (IOException e) {
2476                 http.reuse = false;
2477                 reuseClient = null;
2478                 disconnectInternal();
2479                 return;
2480             }
2481             try {
2482                 if (is instanceof MeteredStream) {
2483                     is.close();
2484                 }
2485             } catch (IOException e) { }
2486         }
2487         responseCode = -1;
2488         responses = new MessageHeader();
2489         connected = false;
2490     }
2491 
2492     /**
2493      * Disconnect from the web server at the first 401 error. Do not
2494      * disconnect when using a proxy, a good proxy should have already
2495      * closed the connection to the web server.
2496      */
2497     private void disconnectWeb() throws IOException {
2498         if (usingProxy() && http.isKeepingAlive()) {
2499             responseCode = -1;
2500             // clean up, particularly, skip the content part
2501             // of a 401 error response
2502             reset();
2503         } else {
2504             disconnectInternal();
2505         }
2506     }
2507 
2508     /**
2509      * Disconnect from the server (for internal use)
2510      */
2511     private void disconnectInternal() {
2512         responseCode = -1;
2513         inputStream = null;
2514         if (pi != null) {
2515             pi.finishTracking();
2516             pi = null;
2517         }
2518         if (http != null) {
2519             http.closeServer();
2520             http = null;
2521             connected = false;
2522         }
2523     }
2524 
2525     /**
2526      * Disconnect from the server (public API)
2527      */
2528     public void disconnect() {
2529 
2530         responseCode = -1;
2531         if (pi != null) {
2532             pi.finishTracking();
2533             pi = null;
2534         }
2535 
2536         if (http != null) {
2537             /*
2538              * If we have an input stream this means we received a response
2539              * from the server. That stream may have been read to EOF and
2540              * dependening on the stream type may already be closed or the
2541              * the http client may be returned to the keep-alive cache.
2542              * If the http client has been returned to the keep-alive cache
2543              * it may be closed (idle timeout) or may be allocated to
2544              * another request.
2545              *
2546              * In other to avoid timing issues we close the input stream
2547              * which will either close the underlying connection or return
2548              * the client to the cache. If there's a possibility that the
2549              * client has been returned to the cache (ie: stream is a keep
2550              * alive stream or a chunked input stream) then we remove an
2551              * idle connection to the server. Note that this approach
2552              * can be considered an approximation in that we may close a
2553              * different idle connection to that used by the request.
2554              * Additionally it's possible that we close two connections
2555              * - the first becuase it wasn't an EOF (and couldn't be
2556              * hurried) - the second, another idle connection to the
2557              * same server. The is okay because "disconnect" is an
2558              * indication that the application doesn't intend to access
2559              * this http server for a while.
2560              */
2561 
2562             if (inputStream != null) {
2563                 HttpClient hc = http;
2564 
2565                 // un-synchronized
2566                 boolean ka = hc.isKeepingAlive();
2567 
2568                 try {
2569                     inputStream.close();
2570                 } catch (IOException ioe) { }
2571 
2572                 // if the connection is persistent it may have been closed
2573                 // or returned to the keep-alive cache. If it's been returned
2574                 // to the keep-alive cache then we would like to close it
2575                 // but it may have been allocated
2576 
2577                 if (ka) {
2578                     hc.closeIdleConnection();
2579                 }
2580 
2581 
2582             } else {
2583                 // We are deliberatly being disconnected so HttpClient
2584                 // should not try to resend the request no matter what stage
2585                 // of the connection we are in.
2586                 http.setDoNotRetry(true);
2587 
2588                 http.closeServer();
2589             }
2590 
2591             //      poster = null;
2592             http = null;
2593             connected = false;
2594         }
2595         cachedInputStream = null;
2596         if (cachedHeaders != null) {
2597             cachedHeaders.reset();
2598         }
2599     }
2600 
2601     public boolean usingProxy() {
2602         if (http != null) {
2603             return (http.getProxyHostUsed() != null);
2604         }
2605         return false;
2606     }
2607 
2608     // constant strings represent set-cookie header names
2609     private final static String SET_COOKIE = "set-cookie";
2610     private final static String SET_COOKIE2 = "set-cookie2";
2611 
2612     /**
2613      * Returns a filtered version of the given headers value.
2614      *
2615      * Note: The implementation currently only filters out HttpOnly cookies
2616      *       from Set-Cookie and Set-Cookie2 headers.
2617      */
2618     private String filterHeaderField(String name, String value) {
2619         if (value == null)
2620             return null;
2621 
2622         if (SET_COOKIE.equalsIgnoreCase(name) ||
2623             SET_COOKIE2.equalsIgnoreCase(name)) {
2624             // Filtering only if there is a cookie handler. [Assumption: the
2625             // cookie handler will store/retrieve the HttpOnly cookies]
2626             if (cookieHandler == null)
2627                 return value;
2628 
2629             sun.misc.JavaNetHttpCookieAccess access =
2630                     sun.misc.SharedSecrets.getJavaNetHttpCookieAccess();
2631             StringBuilder retValue = new StringBuilder();
2632             List<HttpCookie> cookies = access.parse(value);
2633             boolean multipleCookies = false;
2634             for (HttpCookie cookie : cookies) {
2635                 // skip HttpOnly cookies
2636                 if (cookie.isHttpOnly())
2637                     continue;
2638                 if (multipleCookies)
2639                     retValue.append(',');  // RFC 2965, comma separated
2640                 retValue.append(access.header(cookie));
2641                 multipleCookies = true;
2642             }
2643 
2644             return retValue.length() == 0 ? "" : retValue.toString();
2645         }
2646 
2647         return value;
2648     }
2649 
2650     // Cache the filtered response headers so that they don't need
2651     // to be generated for every getHeaderFields() call.
2652     private Map<String, List<String>> filteredHeaders;  // null
2653 
2654     private Map<String, List<String>> getFilteredHeaderFields() {
2655         if (filteredHeaders != null)
2656             return filteredHeaders;
2657 
2658         Map<String, List<String>> headers, tmpMap = new HashMap<>();
2659 
2660         if (cachedHeaders != null)
2661             headers = cachedHeaders.getHeaders();
2662         else
2663             headers = responses.getHeaders();
2664 
2665         for (Map.Entry<String, List<String>> e: headers.entrySet()) {
2666             String key = e.getKey();
2667             List<String> values = e.getValue(), filteredVals = new ArrayList<>();
2668             for (String value : values) {
2669                 String fVal = filterHeaderField(key, value);
2670                 if (fVal != null)
2671                     filteredVals.add(fVal);
2672             }
2673             if (!filteredVals.isEmpty())
2674                 tmpMap.put(key, Collections.unmodifiableList(filteredVals));
2675         }
2676 
2677         return filteredHeaders = Collections.unmodifiableMap(tmpMap);
2678     }
2679 
2680     /**
2681      * Gets a header field by name. Returns null if not known.
2682      * @param name the name of the header field
2683      */
2684     @Override
2685     public String getHeaderField(String name) {
2686         try {
2687             getInputStream();
2688         } catch (IOException e) {}
2689 
2690         if (cachedHeaders != null) {
2691             return filterHeaderField(name, cachedHeaders.findValue(name));
2692         }
2693 
2694         return filterHeaderField(name, responses.findValue(name));
2695     }
2696 
2697     /**
2698      * Returns an unmodifiable Map of the header fields.
2699      * The Map keys are Strings that represent the
2700      * response-header field names. Each Map value is an
2701      * unmodifiable List of Strings that represents
2702      * the corresponding field values.
2703      *
2704      * @return a Map of header fields
2705      * @since 1.4
2706      */
2707     @Override
2708     public Map<String, List<String>> getHeaderFields() {
2709         try {
2710             getInputStream();
2711         } catch (IOException e) {}
2712 
2713         return getFilteredHeaderFields();
2714     }
2715 
2716     /**
2717      * Gets a header field by index. Returns null if not known.
2718      * @param n the index of the header field
2719      */
2720     @Override
2721     public String getHeaderField(int n) {
2722         try {
2723             getInputStream();
2724         } catch (IOException e) {}
2725 
2726         if (cachedHeaders != null) {
2727            return filterHeaderField(cachedHeaders.getKey(n),
2728                                     cachedHeaders.getValue(n));
2729         }
2730         return filterHeaderField(responses.getKey(n), responses.getValue(n));
2731     }
2732 
2733     /**
2734      * Gets a header field by index. Returns null if not known.
2735      * @param n the index of the header field
2736      */
2737     @Override
2738     public String getHeaderFieldKey(int n) {
2739         try {
2740             getInputStream();
2741         } catch (IOException e) {}
2742 
2743         if (cachedHeaders != null) {
2744             return cachedHeaders.getKey(n);
2745         }
2746 
2747         return responses.getKey(n);
2748     }
2749 
2750     /**
2751      * Sets request property. If a property with the key already
2752      * exists, overwrite its value with the new value.
2753      * @param value the value to be set
2754      */
2755     @Override
2756     public void setRequestProperty(String key, String value) {
2757         if (connected)
2758             throw new IllegalStateException("Already connected");
2759         if (key == null)
2760             throw new NullPointerException ("key is null");
2761 
2762         if (isExternalMessageHeaderAllowed(key, value)) {
2763             requests.set(key, value);
2764         }
2765     }
2766 
2767     /**
2768      * Adds a general request property specified by a
2769      * key-value pair.  This method will not overwrite
2770      * existing values associated with the same key.
2771      *
2772      * @param   key     the keyword by which the request is known
2773      *                  (e.g., "<code>accept</code>").
2774      * @param   value  the value associated with it.
2775      * @see #getRequestProperties(java.lang.String)
2776      * @since 1.4
2777      */
2778     @Override
2779     public void addRequestProperty(String key, String value) {
2780         if (connected)
2781             throw new IllegalStateException("Already connected");
2782         if (key == null)
2783             throw new NullPointerException ("key is null");
2784 
2785         if (isExternalMessageHeaderAllowed(key, value)) {
2786             requests.add(key, value);
2787         }
2788     }
2789 
2790     //
2791     // Set a property for authentication.  This can safely disregard
2792     // the connected test.
2793     //
2794     public void setAuthenticationProperty(String key, String value) {
2795         checkMessageHeader(key, value);
2796         requests.set(key, value);
2797     }
2798 
2799     @Override
2800     public synchronized String getRequestProperty (String key) {
2801         if (key == null) {
2802             return null;
2803         }
2804 
2805         // don't return headers containing security sensitive information
2806         for (int i=0; i < EXCLUDE_HEADERS.length; i++) {
2807             if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
2808                 return null;
2809             }
2810         }
2811         if (!setUserCookies) {
2812             if (key.equalsIgnoreCase("Cookie")) {
2813                 return userCookies;
2814             }
2815             if (key.equalsIgnoreCase("Cookie2")) {
2816                 return userCookies2;
2817             }
2818         }
2819         return requests.findValue(key);
2820     }
2821 
2822     /**
2823      * Returns an unmodifiable Map of general request
2824      * properties for this connection. The Map keys
2825      * are Strings that represent the request-header
2826      * field names. Each Map value is a unmodifiable List
2827      * of Strings that represents the corresponding
2828      * field values.
2829      *
2830      * @return  a Map of the general request properties for this connection.
2831      * @throws IllegalStateException if already connected
2832      * @since 1.4
2833      */
2834     @Override
2835     public synchronized Map<String, List<String>> getRequestProperties() {
2836         if (connected)
2837             throw new IllegalStateException("Already connected");
2838 
2839         // exclude headers containing security-sensitive info
2840         if (setUserCookies) {
2841             return requests.getHeaders(EXCLUDE_HEADERS);
2842         }
2843         /*
2844          * The cookies in the requests message headers may have
2845          * been modified. Use the saved user cookies instead.
2846          */
2847         Map<String, List<String>> userCookiesMap = null;
2848         if (userCookies != null || userCookies2 != null) {
2849             userCookiesMap = new HashMap<>();
2850             if (userCookies != null) {
2851                 userCookiesMap.put("Cookie", Arrays.asList(userCookies));
2852             }
2853             if (userCookies2 != null) {
2854                 userCookiesMap.put("Cookie2", Arrays.asList(userCookies2));
2855             }
2856         }
2857         return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap);
2858     }
2859 
2860     @Override
2861     public void setConnectTimeout(int timeout) {
2862         if (timeout < 0)
2863             throw new IllegalArgumentException("timeouts can't be negative");
2864         connectTimeout = timeout;
2865     }
2866 
2867 
2868     /**
2869      * Returns setting for connect timeout.
2870      * <p>
2871      * 0 return implies that the option is disabled
2872      * (i.e., timeout of infinity).
2873      *
2874      * @return an <code>int</code> that indicates the connect timeout
2875      *         value in milliseconds
2876      * @see java.net.URLConnection#setConnectTimeout(int)
2877      * @see java.net.URLConnection#connect()
2878      * @since 1.5
2879      */
2880     @Override
2881     public int getConnectTimeout() {
2882         return (connectTimeout < 0 ? 0 : connectTimeout);
2883     }
2884 
2885     /**
2886      * Sets the read timeout to a specified timeout, in
2887      * milliseconds. A non-zero value specifies the timeout when
2888      * reading from Input stream when a connection is established to a
2889      * resource. If the timeout expires before there is data available
2890      * for read, a java.net.SocketTimeoutException is raised. A
2891      * timeout of zero is interpreted as an infinite timeout.
2892      *
2893      * <p> Some non-standard implementation of this method ignores the
2894      * specified timeout. To see the read timeout set, please call
2895      * getReadTimeout().
2896      *
2897      * @param timeout an <code>int</code> that specifies the timeout
2898      * value to be used in milliseconds
2899      * @throws IllegalArgumentException if the timeout parameter is negative
2900      *
2901      * @see java.net.URLConnectiongetReadTimeout()
2902      * @see java.io.InputStream#read()
2903      * @since 1.5
2904      */
2905     @Override
2906     public void setReadTimeout(int timeout) {
2907         if (timeout < 0)
2908             throw new IllegalArgumentException("timeouts can't be negative");
2909         readTimeout = timeout;
2910     }
2911 
2912     /**
2913      * Returns setting for read timeout. 0 return implies that the
2914      * option is disabled (i.e., timeout of infinity).
2915      *
2916      * @return an <code>int</code> that indicates the read timeout
2917      *         value in milliseconds
2918      *
2919      * @see java.net.URLConnection#setReadTimeout(int)
2920      * @see java.io.InputStream#read()
2921      * @since 1.5
2922      */
2923     @Override
2924     public int getReadTimeout() {
2925         return readTimeout < 0 ? 0 : readTimeout;
2926     }
2927 
2928     public CookieHandler getCookieHandler() {
2929         return cookieHandler;
2930     }
2931 
2932     String getMethod() {
2933         return method;
2934     }
2935 
2936     private MessageHeader mapToMessageHeader(Map<String, List<String>> map) {
2937         MessageHeader headers = new MessageHeader();
2938         if (map == null || map.isEmpty()) {
2939             return headers;
2940         }
2941         for (Map.Entry<String, List<String>> entry : map.entrySet()) {
2942             String key = entry.getKey();
2943             List<String> values = entry.getValue();
2944             for (String value : values) {
2945                 if (key == null) {
2946                     headers.prepend(key, value);
2947                 } else {
2948                     headers.add(key, value);
2949                 }
2950             }
2951         }
2952         return headers;
2953     }
2954 
2955     /* The purpose of this wrapper is just to capture the close() call
2956      * so we can check authentication information that may have
2957      * arrived in a Trailer field
2958      */
2959     class HttpInputStream extends FilterInputStream {
2960         private CacheRequest cacheRequest;
2961         private OutputStream outputStream;
2962         private boolean marked = false;
2963         private int inCache = 0;
2964         private int markCount = 0;
2965 
2966         public HttpInputStream (InputStream is) {
2967             super (is);
2968             this.cacheRequest = null;
2969             this.outputStream = null;
2970         }
2971 
2972         public HttpInputStream (InputStream is, CacheRequest cacheRequest) {
2973             super (is);
2974             this.cacheRequest = cacheRequest;
2975             try {
2976                 this.outputStream = cacheRequest.getBody();
2977             } catch (IOException ioex) {
2978                 this.cacheRequest.abort();
2979                 this.cacheRequest = null;
2980                 this.outputStream = null;
2981             }
2982         }
2983 
2984         /**
2985          * Marks the current position in this input stream. A subsequent
2986          * call to the <code>reset</code> method repositions this stream at
2987          * the last marked position so that subsequent reads re-read the same
2988          * bytes.
2989          * <p>
2990          * The <code>readlimit</code> argument tells this input stream to
2991          * allow that many bytes to be read before the mark position gets
2992          * invalidated.
2993          * <p>
2994          * This method simply performs <code>in.mark(readlimit)</code>.
2995          *
2996          * @param   readlimit   the maximum limit of bytes that can be read before
2997          *                      the mark position becomes invalid.
2998          * @see     java.io.FilterInputStream#in
2999          * @see     java.io.FilterInputStream#reset()
3000          */
3001         @Override
3002         public synchronized void mark(int readlimit) {
3003             super.mark(readlimit);
3004             if (cacheRequest != null) {
3005                 marked = true;
3006                 markCount = 0;
3007             }
3008         }
3009 
3010         /**
3011          * Repositions this stream to the position at the time the
3012          * <code>mark</code> method was last called on this input stream.
3013          * <p>
3014          * This method
3015          * simply performs <code>in.reset()</code>.
3016          * <p>
3017          * Stream marks are intended to be used in
3018          * situations where you need to read ahead a little to see what's in
3019          * the stream. Often this is most easily done by invoking some
3020          * general parser. If the stream is of the type handled by the
3021          * parse, it just chugs along happily. If the stream is not of
3022          * that type, the parser should toss an exception when it fails.
3023          * If this happens within readlimit bytes, it allows the outer
3024          * code to reset the stream and try another parser.
3025          *
3026          * @exception  IOException  if the stream has not been marked or if the
3027          *               mark has been invalidated.
3028          * @see        java.io.FilterInputStream#in
3029          * @see        java.io.FilterInputStream#mark(int)
3030          */
3031         @Override
3032         public synchronized void reset() throws IOException {
3033             super.reset();
3034             if (cacheRequest != null) {
3035                 marked = false;
3036                 inCache += markCount;
3037             }
3038         }
3039 
3040         @Override
3041         public int read() throws IOException {
3042             try {
3043                 byte[] b = new byte[1];
3044                 int ret = read(b);
3045                 return (ret == -1? ret : (b[0] & 0x00FF));
3046             } catch (IOException ioex) {
3047                 if (cacheRequest != null) {
3048                     cacheRequest.abort();
3049                 }
3050                 throw ioex;
3051             }
3052         }
3053 
3054         @Override
3055         public int read(byte[] b) throws IOException {
3056             return read(b, 0, b.length);
3057         }
3058 
3059         @Override
3060         public int read(byte[] b, int off, int len) throws IOException {
3061             try {
3062                 int newLen = super.read(b, off, len);
3063                 int nWrite;
3064                 // write to cache
3065                 if (inCache > 0) {
3066                     if (inCache >= newLen) {
3067                         inCache -= newLen;
3068                         nWrite = 0;
3069                     } else {
3070                         nWrite = newLen - inCache;
3071                         inCache = 0;
3072                     }
3073                 } else {
3074                     nWrite = newLen;
3075                 }
3076                 if (nWrite > 0 && outputStream != null)
3077                     outputStream.write(b, off + (newLen-nWrite), nWrite);
3078                 if (marked) {
3079                     markCount += newLen;
3080                 }
3081                 return newLen;
3082             } catch (IOException ioex) {
3083                 if (cacheRequest != null) {
3084                     cacheRequest.abort();
3085                 }
3086                 throw ioex;
3087             }
3088         }
3089 
3090         /* skip() calls read() in order to ensure that entire response gets
3091          * cached. same implementation as InputStream.skip */
3092 
3093         private byte[] skipBuffer;
3094         private static final int SKIP_BUFFER_SIZE = 8096;
3095 
3096         @Override
3097         public long skip (long n) throws IOException {
3098 
3099             long remaining = n;
3100             int nr;
3101             if (skipBuffer == null)
3102                 skipBuffer = new byte[SKIP_BUFFER_SIZE];
3103 
3104             byte[] localSkipBuffer = skipBuffer;
3105 
3106             if (n <= 0) {
3107                 return 0;
3108             }
3109 
3110             while (remaining > 0) {
3111                 nr = read(localSkipBuffer, 0,
3112                           (int) Math.min(SKIP_BUFFER_SIZE, remaining));
3113                 if (nr < 0) {
3114                     break;
3115                 }
3116                 remaining -= nr;
3117             }
3118 
3119             return n - remaining;
3120         }
3121 
3122         @Override
3123         public void close () throws IOException {
3124             try {
3125                 if (outputStream != null) {
3126                     if (read() != -1) {
3127                         cacheRequest.abort();
3128                     } else {
3129                         outputStream.close();
3130                     }
3131                 }
3132                 super.close ();
3133             } catch (IOException ioex) {
3134                 if (cacheRequest != null) {
3135                     cacheRequest.abort();
3136                 }
3137                 throw ioex;
3138             } finally {
3139                 HttpURLConnection.this.http = null;
3140                 checkResponseCredentials (true);
3141             }
3142         }
3143     }
3144 
3145     class StreamingOutputStream extends FilterOutputStream {
3146 
3147         long expected;
3148         long written;
3149         boolean closed;
3150         boolean error;
3151         IOException errorExcp;
3152 
3153         /**
3154          * expectedLength == -1 if the stream is chunked
3155          * expectedLength > 0 if the stream is fixed content-length
3156          *    In the 2nd case, we make sure the expected number of
3157          *    of bytes are actually written
3158          */
3159         StreamingOutputStream (OutputStream os, long expectedLength) {
3160             super (os);
3161             expected = expectedLength;
3162             written = 0L;
3163             closed = false;
3164             error = false;
3165         }
3166 
3167         @Override
3168         public void write (int b) throws IOException {
3169             checkError();
3170             written ++;
3171             if (expected != -1L && written > expected) {
3172                 throw new IOException ("too many bytes written");
3173             }
3174             out.write (b);
3175         }
3176 
3177         @Override
3178         public void write (byte[] b) throws IOException {
3179             write (b, 0, b.length);
3180         }
3181 
3182         @Override
3183         public void write (byte[] b, int off, int len) throws IOException {
3184             checkError();
3185             written += len;
3186             if (expected != -1L && written > expected) {
3187                 out.close ();
3188                 throw new IOException ("too many bytes written");
3189             }
3190             out.write (b, off, len);
3191         }
3192 
3193         void checkError () throws IOException {
3194             if (closed) {
3195                 throw new IOException ("Stream is closed");
3196             }
3197             if (error) {
3198                 throw errorExcp;
3199             }
3200             if (((PrintStream)out).checkError()) {
3201                 throw new IOException("Error writing request body to server");
3202             }
3203         }
3204 
3205         /* this is called to check that all the bytes
3206          * that were supposed to be written were written
3207          * and that the stream is now closed().
3208          */
3209         boolean writtenOK () {
3210             return closed && ! error;
3211         }
3212 
3213         @Override
3214         public void close () throws IOException {
3215             if (closed) {
3216                 return;
3217             }
3218             closed = true;
3219             if (expected != -1L) {
3220                 /* not chunked */
3221                 if (written != expected) {
3222                     error = true;
3223                     errorExcp = new IOException ("insufficient data written");
3224                     out.close ();
3225                     throw errorExcp;
3226                 }
3227                 super.flush(); /* can't close the socket */
3228             } else {
3229                 /* chunked */
3230                 super.close (); /* force final chunk to be written */
3231                 /* trailing \r\n */
3232                 OutputStream o = http.getOutputStream();
3233                 o.write ('\r');
3234                 o.write ('\n');
3235                 o.flush();
3236             }
3237         }
3238     }
3239 
3240 
3241     static class ErrorStream extends InputStream {
3242         ByteBuffer buffer;
3243         InputStream is;
3244 
3245         private ErrorStream(ByteBuffer buf) {
3246             buffer = buf;
3247             is = null;
3248         }
3249 
3250         private ErrorStream(ByteBuffer buf, InputStream is) {
3251             buffer = buf;
3252             this.is = is;
3253         }
3254 
3255         // when this method is called, it's either the case that cl > 0, or
3256         // if chunk-encoded, cl = -1; in other words, cl can't be 0
3257         public static InputStream getErrorStream(InputStream is, long cl, HttpClient http) {
3258 
3259             // cl can't be 0; this following is here for extra precaution
3260             if (cl == 0) {
3261                 return null;
3262             }
3263 
3264             try {
3265                 // set SO_TIMEOUT to 1/5th of the total timeout
3266                 // remember the old timeout value so that we can restore it
3267                 int oldTimeout = http.getReadTimeout();
3268                 http.setReadTimeout(timeout4ESBuffer/5);
3269 
3270                 long expected = 0;
3271                 boolean isChunked = false;
3272                 // the chunked case
3273                 if (cl < 0) {
3274                     expected = bufSize4ES;
3275                     isChunked = true;
3276                 } else {
3277                     expected = cl;
3278                 }
3279                 if (expected <= bufSize4ES) {
3280                     int exp = (int) expected;
3281                     byte[] buffer = new byte[exp];
3282                     int count = 0, time = 0, len = 0;
3283                     do {
3284                         try {
3285                             len = is.read(buffer, count,
3286                                              buffer.length - count);
3287                             if (len < 0) {
3288                                 if (isChunked) {
3289                                     // chunked ended
3290                                     // if chunked ended prematurely,
3291                                     // an IOException would be thrown
3292                                     break;
3293                                 }
3294                                 // the server sends less than cl bytes of data
3295                                 throw new IOException("the server closes"+
3296                                                       " before sending "+cl+
3297                                                       " bytes of data");
3298                             }
3299                             count += len;
3300                         } catch (SocketTimeoutException ex) {
3301                             time += timeout4ESBuffer/5;
3302                         }
3303                     } while (count < exp && time < timeout4ESBuffer);
3304 
3305                     // reset SO_TIMEOUT to old value
3306                     http.setReadTimeout(oldTimeout);
3307 
3308                     // if count < cl at this point, we will not try to reuse
3309                     // the connection
3310                     if (count == 0) {
3311                         // since we haven't read anything,
3312                         // we will return the underlying
3313                         // inputstream back to the application
3314                         return null;
3315                     }  else if ((count == expected && !(isChunked)) || (isChunked && len <0)) {
3316                         // put the connection into keep-alive cache
3317                         // the inputstream will try to do the right thing
3318                         is.close();
3319                         return new ErrorStream(ByteBuffer.wrap(buffer, 0, count));
3320                     } else {
3321                         // we read part of the response body
3322                         return new ErrorStream(
3323                                       ByteBuffer.wrap(buffer, 0, count), is);
3324                     }
3325                 }
3326                 return null;
3327             } catch (IOException ioex) {
3328                 // ioex.printStackTrace();
3329                 return null;
3330             }
3331         }
3332 
3333         @Override
3334         public int available() throws IOException {
3335             if (is == null) {
3336                 return buffer.remaining();
3337             } else {
3338                 return buffer.remaining()+is.available();
3339             }
3340         }
3341 
3342         public int read() throws IOException {
3343             byte[] b = new byte[1];
3344             int ret = read(b);
3345             return (ret == -1? ret : (b[0] & 0x00FF));
3346         }
3347 
3348         @Override
3349         public int read(byte[] b) throws IOException {
3350             return read(b, 0, b.length);
3351         }
3352 
3353         @Override
3354         public int read(byte[] b, int off, int len) throws IOException {
3355             int rem = buffer.remaining();
3356             if (rem > 0) {
3357                 int ret = rem < len? rem : len;
3358                 buffer.get(b, off, ret);
3359                 return ret;
3360             } else {
3361                 if (is == null) {
3362                     return -1;
3363                 } else {
3364                     return is.read(b, off, len);
3365                 }
3366             }
3367         }
3368 
3369         @Override
3370         public void close() throws IOException {
3371             buffer = null;
3372             if (is != null) {
3373                 is.close();
3374             }
3375         }
3376     }
3377 }
3378 
3379 /** An input stream that just returns EOF.  This is for
3380  * HTTP URLConnections that are KeepAlive && use the
3381  * HEAD method - i.e., stream not dead, but nothing to be read.
3382  */
3383 
3384 class EmptyInputStream extends InputStream {
3385 
3386     @Override
3387     public int available() {
3388         return 0;
3389     }
3390 
3391     public int read() {
3392         return -1;
3393     }
3394 }