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