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