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