1 /*
   2  * Copyright (c) 1995, 2016, 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             // maintain previous headers, just change the name
2729             // of the file we're getting
2730             url = locUrl;
2731             requestURI = null; // force it to be recalculated
2732             if (method.equals("POST") && !Boolean.getBoolean("http.strictPostRedirect") && (stat!=307)) {
2733                 /* The HTTP/1.1 spec says that a redirect from a POST
2734                  * *should not* be immediately turned into a GET, and
2735                  * that some HTTP/1.0 clients incorrectly did this.
2736                  * Correct behavior redirects a POST to another POST.
2737                  * Unfortunately, since most browsers have this incorrect
2738                  * behavior, the web works this way now.  Typical usage
2739                  * seems to be:
2740                  *   POST a login code or passwd to a web page.
2741                  *   after validation, the server redirects to another
2742                  *     (welcome) page
2743                  *   The second request is (erroneously) expected to be GET
2744                  *
2745                  * We will do the incorrect thing (POST-->GET) by default.
2746                  * We will provide the capability to do the "right" thing
2747                  * (POST-->POST) by a system property, "http.strictPostRedirect=true"
2748                  */
2749 
2750                 requests = new MessageHeader();
2751                 setRequests = false;
2752                 super.setRequestMethod("GET"); // avoid the connecting check
2753                 poster = null;
2754                 if (!checkReuseConnection())
2755                     connect();
2756             } else {
2757                 if (!checkReuseConnection())
2758                     connect();
2759                 /* Even after a connect() call, http variable still can be
2760                  * null, if a ResponseCache has been installed and it returns
2761                  * a non-null CacheResponse instance. So check nullity before using it.
2762                  *
2763                  * And further, if http is null, there's no need to do anything
2764                  * about request headers because successive http session will use
2765                  * cachedInputStream/cachedHeaders anyway, which is returned by
2766                  * CacheResponse.
2767                  */
2768                 if (http != null) {
2769                     requests.set(0, method + " " + getRequestURI()+" "  +
2770                                  httpVersion, null);
2771                     int port = url.getPort();
2772                     String host = stripIPv6ZoneId(url.getHost());
2773                     if (port != -1 && port != url.getDefaultPort()) {
2774                         host += ":" + String.valueOf(port);
2775                     }
2776                     requests.set("Host", host);
2777                 }
2778             }
2779         }
2780         return true;
2781     }
2782 
2783     /* dummy byte buffer for reading off socket prior to closing */
2784     byte[] cdata = new byte [128];
2785 
2786     /**
2787      * Reset (without disconnecting the TCP conn) in order to do another transaction with this instance
2788      */
2789     private void reset() throws IOException {
2790         http.reuse = true;
2791         /* must save before calling close */
2792         reuseClient = http;
2793         InputStream is = http.getInputStream();
2794         if (!method.equals("HEAD")) {
2795             try {
2796                 /* we want to read the rest of the response without using the
2797                  * hurry mechanism, because that would close the connection
2798                  * if everything is not available immediately
2799                  */
2800                 if ((is instanceof ChunkedInputStream) ||
2801                     (is instanceof MeteredStream)) {
2802                     /* reading until eof will not block */
2803                     while (is.read (cdata) > 0) {}
2804                 } else {
2805                     /* raw stream, which will block on read, so only read
2806                      * the expected number of bytes, probably 0
2807                      */
2808                     long cl = 0;
2809                     int n = 0;
2810                     String cls = responses.findValue ("Content-Length");
2811                     if (cls != null) {
2812                         try {
2813                             cl = Long.parseLong (cls);
2814                         } catch (NumberFormatException e) {
2815                             cl = 0;
2816                         }
2817                     }
2818                     for (long i=0; i<cl; ) {
2819                         if ((n = is.read (cdata)) == -1) {
2820                             break;
2821                         } else {
2822                             i+= n;
2823                         }
2824                     }
2825                 }
2826             } catch (IOException e) {
2827                 http.reuse = false;
2828                 reuseClient = null;
2829                 disconnectInternal();
2830                 return;
2831             }
2832             try {
2833                 if (is instanceof MeteredStream) {
2834                     is.close();
2835                 }
2836             } catch (IOException e) { }
2837         }
2838         responseCode = -1;
2839         responses = new MessageHeader();
2840         connected = false;
2841     }
2842 
2843     /**
2844      * Disconnect from the web server at the first 401 error. Do not
2845      * disconnect when using a proxy, a good proxy should have already
2846      * closed the connection to the web server.
2847      */
2848     private void disconnectWeb() throws IOException {
2849         if (usingProxy() && http.isKeepingAlive()) {
2850             responseCode = -1;
2851             // clean up, particularly, skip the content part
2852             // of a 401 error response
2853             reset();
2854         } else {
2855             disconnectInternal();
2856         }
2857     }
2858 
2859     /**
2860      * Disconnect from the server (for internal use)
2861      */
2862     private void disconnectInternal() {
2863         responseCode = -1;
2864         inputStream = null;
2865         if (pi != null) {
2866             pi.finishTracking();
2867             pi = null;
2868         }
2869         if (http != null) {
2870             http.closeServer();
2871             http = null;
2872             connected = false;
2873         }
2874     }
2875 
2876     /**
2877      * Disconnect from the server (public API)
2878      */
2879     public void disconnect() {
2880 
2881         responseCode = -1;
2882         if (pi != null) {
2883             pi.finishTracking();
2884             pi = null;
2885         }
2886 
2887         if (http != null) {
2888             /*
2889              * If we have an input stream this means we received a response
2890              * from the server. That stream may have been read to EOF and
2891              * depending on the stream type may already be closed or
2892              * the http client may be returned to the keep-alive cache.
2893              * If the http client has been returned to the keep-alive cache
2894              * it may be closed (idle timeout) or may be allocated to
2895              * another request.
2896              *
2897              * In other to avoid timing issues we close the input stream
2898              * which will either close the underlying connection or return
2899              * the client to the cache. If there's a possibility that the
2900              * client has been returned to the cache (ie: stream is a keep
2901              * alive stream or a chunked input stream) then we remove an
2902              * idle connection to the server. Note that this approach
2903              * can be considered an approximation in that we may close a
2904              * different idle connection to that used by the request.
2905              * Additionally it's possible that we close two connections
2906              * - the first becuase it wasn't an EOF (and couldn't be
2907              * hurried) - the second, another idle connection to the
2908              * same server. The is okay because "disconnect" is an
2909              * indication that the application doesn't intend to access
2910              * this http server for a while.
2911              */
2912 
2913             if (inputStream != null) {
2914                 HttpClient hc = http;
2915 
2916                 // un-synchronized
2917                 boolean ka = hc.isKeepingAlive();
2918 
2919                 try {
2920                     inputStream.close();
2921                 } catch (IOException ioe) { }
2922 
2923                 // if the connection is persistent it may have been closed
2924                 // or returned to the keep-alive cache. If it's been returned
2925                 // to the keep-alive cache then we would like to close it
2926                 // but it may have been allocated
2927 
2928                 if (ka) {
2929                     hc.closeIdleConnection();
2930                 }
2931 
2932 
2933             } else {
2934                 // We are deliberatly being disconnected so HttpClient
2935                 // should not try to resend the request no matter what stage
2936                 // of the connection we are in.
2937                 http.setDoNotRetry(true);
2938 
2939                 http.closeServer();
2940             }
2941 
2942             //      poster = null;
2943             http = null;
2944             connected = false;
2945         }
2946         cachedInputStream = null;
2947         if (cachedHeaders != null) {
2948             cachedHeaders.reset();
2949         }
2950     }
2951 
2952     public boolean usingProxy() {
2953         if (http != null) {
2954             return (http.getProxyHostUsed() != null);
2955         }
2956         return false;
2957     }
2958 
2959     // constant strings represent set-cookie header names
2960     private static final String SET_COOKIE = "set-cookie";
2961     private static final String SET_COOKIE2 = "set-cookie2";
2962 
2963     /**
2964      * Returns a filtered version of the given headers value.
2965      *
2966      * Note: The implementation currently only filters out HttpOnly cookies
2967      *       from Set-Cookie and Set-Cookie2 headers.
2968      */
2969     private String filterHeaderField(String name, String value) {
2970         if (value == null)
2971             return null;
2972 
2973         if (SET_COOKIE.equalsIgnoreCase(name) ||
2974             SET_COOKIE2.equalsIgnoreCase(name)) {
2975 
2976             // Filtering only if there is a cookie handler. [Assumption: the
2977             // cookie handler will store/retrieve the HttpOnly cookies]
2978             if (cookieHandler == null || value.length() == 0)
2979                 return value;
2980 
2981             JavaNetHttpCookieAccess access =
2982                     SharedSecrets.getJavaNetHttpCookieAccess();
2983             StringJoiner retValue = new StringJoiner(",");  // RFC 2965, comma separated
2984             List<HttpCookie> cookies = access.parse(value);
2985             for (HttpCookie cookie : cookies) {
2986                 // skip HttpOnly cookies
2987                 if (!cookie.isHttpOnly())
2988                     retValue.add(access.header(cookie));
2989             }
2990             return retValue.toString();
2991         }
2992 
2993         return value;
2994     }
2995 
2996     // Cache the filtered response headers so that they don't need
2997     // to be generated for every getHeaderFields() call.
2998     private Map<String, List<String>> filteredHeaders;  // null
2999 
3000     private Map<String, List<String>> getFilteredHeaderFields() {
3001         if (filteredHeaders != null)
3002             return filteredHeaders;
3003 
3004         Map<String, List<String>> headers, tmpMap = new HashMap<>();
3005 
3006         if (cachedHeaders != null)
3007             headers = cachedHeaders.getHeaders();
3008         else
3009             headers = responses.getHeaders();
3010 
3011         for (Map.Entry<String, List<String>> e: headers.entrySet()) {
3012             String key = e.getKey();
3013             List<String> values = e.getValue(), filteredVals = new ArrayList<>();
3014             for (String value : values) {
3015                 String fVal = filterHeaderField(key, value);
3016                 if (fVal != null)
3017                     filteredVals.add(fVal);
3018             }
3019             if (!filteredVals.isEmpty())
3020                 tmpMap.put(key, Collections.unmodifiableList(filteredVals));
3021         }
3022 
3023         return filteredHeaders = Collections.unmodifiableMap(tmpMap);
3024     }
3025 
3026     /**
3027      * Gets a header field by name. Returns null if not known.
3028      * @param name the name of the header field
3029      */
3030     @Override
3031     public String getHeaderField(String name) {
3032         try {
3033             getInputStream();
3034         } catch (IOException e) {}
3035 
3036         if (cachedHeaders != null) {
3037             return filterHeaderField(name, cachedHeaders.findValue(name));
3038         }
3039 
3040         return filterHeaderField(name, responses.findValue(name));
3041     }
3042 
3043     /**
3044      * Returns an unmodifiable Map of the header fields.
3045      * The Map keys are Strings that represent the
3046      * response-header field names. Each Map value is an
3047      * unmodifiable List of Strings that represents
3048      * the corresponding field values.
3049      *
3050      * @return a Map of header fields
3051      * @since 1.4
3052      */
3053     @Override
3054     public Map<String, List<String>> getHeaderFields() {
3055         try {
3056             getInputStream();
3057         } catch (IOException e) {}
3058 
3059         return getFilteredHeaderFields();
3060     }
3061 
3062     /**
3063      * Gets a header field by index. Returns null if not known.
3064      * @param n the index of the header field
3065      */
3066     @Override
3067     public String getHeaderField(int n) {
3068         try {
3069             getInputStream();
3070         } catch (IOException e) {}
3071 
3072         if (cachedHeaders != null) {
3073            return filterHeaderField(cachedHeaders.getKey(n),
3074                                     cachedHeaders.getValue(n));
3075         }
3076         return filterHeaderField(responses.getKey(n), responses.getValue(n));
3077     }
3078 
3079     /**
3080      * Gets a header field by index. Returns null if not known.
3081      * @param n the index of the header field
3082      */
3083     @Override
3084     public String getHeaderFieldKey(int n) {
3085         try {
3086             getInputStream();
3087         } catch (IOException e) {}
3088 
3089         if (cachedHeaders != null) {
3090             return cachedHeaders.getKey(n);
3091         }
3092 
3093         return responses.getKey(n);
3094     }
3095 
3096     /**
3097      * Sets request property. If a property with the key already
3098      * exists, overwrite its value with the new value.
3099      * @param value the value to be set
3100      */
3101     @Override
3102     public synchronized void setRequestProperty(String key, String value) {
3103         if (connected || connecting)
3104             throw new IllegalStateException("Already connected");
3105         if (key == null)
3106             throw new NullPointerException ("key is null");
3107 
3108         if (isExternalMessageHeaderAllowed(key, value)) {
3109             requests.set(key, value);
3110             if (!key.equalsIgnoreCase("Content-Type")) {
3111                 userHeaders.set(key, value);
3112             }
3113         }
3114     }
3115 
3116     MessageHeader getUserSetHeaders() {
3117         return userHeaders;
3118     }
3119 
3120     /**
3121      * Adds a general request property specified by a
3122      * key-value pair.  This method will not overwrite
3123      * existing values associated with the same key.
3124      *
3125      * @param   key     the keyword by which the request is known
3126      *                  (e.g., "<code>accept</code>").
3127      * @param   value  the value associated with it.
3128      * @see #getRequestProperties(java.lang.String)
3129      * @since 1.4
3130      */
3131     @Override
3132     public synchronized void addRequestProperty(String key, String value) {
3133         if (connected || connecting)
3134             throw new IllegalStateException("Already connected");
3135         if (key == null)
3136             throw new NullPointerException ("key is null");
3137 
3138         if (isExternalMessageHeaderAllowed(key, value)) {
3139             requests.add(key, value);
3140             if (!key.equalsIgnoreCase("Content-Type")) {
3141                     userHeaders.add(key, value);
3142             }
3143         }
3144     }
3145 
3146     //
3147     // Set a property for authentication.  This can safely disregard
3148     // the connected test.
3149     //
3150     public void setAuthenticationProperty(String key, String value) {
3151         checkMessageHeader(key, value);
3152         requests.set(key, value);
3153     }
3154 
3155     @Override
3156     public synchronized String getRequestProperty (String key) {
3157         if (key == null) {
3158             return null;
3159         }
3160 
3161         // don't return headers containing security sensitive information
3162         for (int i=0; i < EXCLUDE_HEADERS.length; i++) {
3163             if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
3164                 return null;
3165             }
3166         }
3167         if (!setUserCookies) {
3168             if (key.equalsIgnoreCase("Cookie")) {
3169                 return userCookies;
3170             }
3171             if (key.equalsIgnoreCase("Cookie2")) {
3172                 return userCookies2;
3173             }
3174         }
3175         return requests.findValue(key);
3176     }
3177 
3178     /**
3179      * Returns an unmodifiable Map of general request
3180      * properties for this connection. The Map keys
3181      * are Strings that represent the request-header
3182      * field names. Each Map value is a unmodifiable List
3183      * of Strings that represents the corresponding
3184      * field values.
3185      *
3186      * @return  a Map of the general request properties for this connection.
3187      * @throws IllegalStateException if already connected
3188      * @since 1.4
3189      */
3190     @Override
3191     public synchronized Map<String, List<String>> getRequestProperties() {
3192         if (connected)
3193             throw new IllegalStateException("Already connected");
3194 
3195         // exclude headers containing security-sensitive info
3196         if (setUserCookies) {
3197             return requests.getHeaders(EXCLUDE_HEADERS);
3198         }
3199         /*
3200          * The cookies in the requests message headers may have
3201          * been modified. Use the saved user cookies instead.
3202          */
3203         Map<String, List<String>> userCookiesMap = null;
3204         if (userCookies != null || userCookies2 != null) {
3205             userCookiesMap = new HashMap<>();
3206             if (userCookies != null) {
3207                 userCookiesMap.put("Cookie", Arrays.asList(userCookies));
3208             }
3209             if (userCookies2 != null) {
3210                 userCookiesMap.put("Cookie2", Arrays.asList(userCookies2));
3211             }
3212         }
3213         return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap);
3214     }
3215 
3216     @Override
3217     public void setConnectTimeout(int timeout) {
3218         if (timeout < 0)
3219             throw new IllegalArgumentException("timeouts can't be negative");
3220         connectTimeout = timeout;
3221     }
3222 
3223 
3224     /**
3225      * Returns setting for connect timeout.
3226      * <p>
3227      * 0 return implies that the option is disabled
3228      * (i.e., timeout of infinity).
3229      *
3230      * @return an <code>int</code> that indicates the connect timeout
3231      *         value in milliseconds
3232      * @see java.net.URLConnection#setConnectTimeout(int)
3233      * @see java.net.URLConnection#connect()
3234      * @since 1.5
3235      */
3236     @Override
3237     public int getConnectTimeout() {
3238         return (connectTimeout < 0 ? 0 : connectTimeout);
3239     }
3240 
3241     /**
3242      * Sets the read timeout to a specified timeout, in
3243      * milliseconds. A non-zero value specifies the timeout when
3244      * reading from Input stream when a connection is established to a
3245      * resource. If the timeout expires before there is data available
3246      * for read, a java.net.SocketTimeoutException is raised. A
3247      * timeout of zero is interpreted as an infinite timeout.
3248      *
3249      * <p> Some non-standard implementation of this method ignores the
3250      * specified timeout. To see the read timeout set, please call
3251      * getReadTimeout().
3252      *
3253      * @param timeout an <code>int</code> that specifies the timeout
3254      * value to be used in milliseconds
3255      * @throws IllegalArgumentException if the timeout parameter is negative
3256      *
3257      * @see java.net.URLConnectiongetReadTimeout()
3258      * @see java.io.InputStream#read()
3259      * @since 1.5
3260      */
3261     @Override
3262     public void setReadTimeout(int timeout) {
3263         if (timeout < 0)
3264             throw new IllegalArgumentException("timeouts can't be negative");
3265         readTimeout = timeout;
3266     }
3267 
3268     /**
3269      * Returns setting for read timeout. 0 return implies that the
3270      * option is disabled (i.e., timeout of infinity).
3271      *
3272      * @return an <code>int</code> that indicates the read timeout
3273      *         value in milliseconds
3274      *
3275      * @see java.net.URLConnection#setReadTimeout(int)
3276      * @see java.io.InputStream#read()
3277      * @since 1.5
3278      */
3279     @Override
3280     public int getReadTimeout() {
3281         return readTimeout < 0 ? 0 : readTimeout;
3282     }
3283 
3284     public CookieHandler getCookieHandler() {
3285         return cookieHandler;
3286     }
3287 
3288     String getMethod() {
3289         return method;
3290     }
3291 
3292     private MessageHeader mapToMessageHeader(Map<String, List<String>> map) {
3293         MessageHeader headers = new MessageHeader();
3294         if (map == null || map.isEmpty()) {
3295             return headers;
3296         }
3297         for (Map.Entry<String, List<String>> entry : map.entrySet()) {
3298             String key = entry.getKey();
3299             List<String> values = entry.getValue();
3300             for (String value : values) {
3301                 if (key == null) {
3302                     headers.prepend(key, value);
3303                 } else {
3304                     headers.add(key, value);
3305                 }
3306             }
3307         }
3308         return headers;
3309     }
3310 
3311     /**
3312      * Returns the given host, without the IPv6 Zone Id, if present.
3313      * (e.g. [fe80::a00:27ff:aaaa:aaaa%eth0] -> [fe80::a00:27ff:aaaa:aaaa])
3314      *
3315      * @param host host address (not null, not empty)
3316      * @return host address without Zone Id
3317      */
3318     static String stripIPv6ZoneId(String host) {
3319         if (host.charAt(0) != '[') { // not an IPv6-literal
3320             return host;
3321         }
3322         int i = host.lastIndexOf('%');
3323         if (i == -1) { // doesn't contain zone_id
3324             return host;
3325         }
3326         return host.substring(0, i) + "]";
3327     }
3328 
3329     /* The purpose of this wrapper is just to capture the close() call
3330      * so we can check authentication information that may have
3331      * arrived in a Trailer field
3332      */
3333     class HttpInputStream extends FilterInputStream {
3334         private CacheRequest cacheRequest;
3335         private OutputStream outputStream;
3336         private boolean marked = false;
3337         private int inCache = 0;
3338         private int markCount = 0;
3339         private boolean closed;  // false
3340 
3341         public HttpInputStream (InputStream is) {
3342             super (is);
3343             this.cacheRequest = null;
3344             this.outputStream = null;
3345         }
3346 
3347         public HttpInputStream (InputStream is, CacheRequest cacheRequest) {
3348             super (is);
3349             this.cacheRequest = cacheRequest;
3350             try {
3351                 this.outputStream = cacheRequest.getBody();
3352             } catch (IOException ioex) {
3353                 this.cacheRequest.abort();
3354                 this.cacheRequest = null;
3355                 this.outputStream = null;
3356             }
3357         }
3358 
3359         /**
3360          * Marks the current position in this input stream. A subsequent
3361          * call to the <code>reset</code> method repositions this stream at
3362          * the last marked position so that subsequent reads re-read the same
3363          * bytes.
3364          * <p>
3365          * The <code>readlimit</code> argument tells this input stream to
3366          * allow that many bytes to be read before the mark position gets
3367          * invalidated.
3368          * <p>
3369          * This method simply performs <code>in.mark(readlimit)</code>.
3370          *
3371          * @param   readlimit   the maximum limit of bytes that can be read before
3372          *                      the mark position becomes invalid.
3373          * @see     java.io.FilterInputStream#in
3374          * @see     java.io.FilterInputStream#reset()
3375          */
3376         @Override
3377         public synchronized void mark(int readlimit) {
3378             super.mark(readlimit);
3379             if (cacheRequest != null) {
3380                 marked = true;
3381                 markCount = 0;
3382             }
3383         }
3384 
3385         /**
3386          * Repositions this stream to the position at the time the
3387          * <code>mark</code> method was last called on this input stream.
3388          * <p>
3389          * This method
3390          * simply performs <code>in.reset()</code>.
3391          * <p>
3392          * Stream marks are intended to be used in
3393          * situations where you need to read ahead a little to see what's in
3394          * the stream. Often this is most easily done by invoking some
3395          * general parser. If the stream is of the type handled by the
3396          * parse, it just chugs along happily. If the stream is not of
3397          * that type, the parser should toss an exception when it fails.
3398          * If this happens within readlimit bytes, it allows the outer
3399          * code to reset the stream and try another parser.
3400          *
3401          * @exception  IOException  if the stream has not been marked or if the
3402          *               mark has been invalidated.
3403          * @see        java.io.FilterInputStream#in
3404          * @see        java.io.FilterInputStream#mark(int)
3405          */
3406         @Override
3407         public synchronized void reset() throws IOException {
3408             super.reset();
3409             if (cacheRequest != null) {
3410                 marked = false;
3411                 inCache += markCount;
3412             }
3413         }
3414 
3415         private void ensureOpen() throws IOException {
3416             if (closed)
3417                 throw new IOException("stream is closed");
3418         }
3419 
3420         @Override
3421         public int read() throws IOException {
3422             ensureOpen();
3423             try {
3424                 byte[] b = new byte[1];
3425                 int ret = read(b);
3426                 return (ret == -1? ret : (b[0] & 0x00FF));
3427             } catch (IOException ioex) {
3428                 if (cacheRequest != null) {
3429                     cacheRequest.abort();
3430                 }
3431                 throw ioex;
3432             }
3433         }
3434 
3435         @Override
3436         public int read(byte[] b) throws IOException {
3437             return read(b, 0, b.length);
3438         }
3439 
3440         @Override
3441         public int read(byte[] b, int off, int len) throws IOException {
3442             ensureOpen();
3443             try {
3444                 int newLen = super.read(b, off, len);
3445                 int nWrite;
3446                 // write to cache
3447                 if (inCache > 0) {
3448                     if (inCache >= newLen) {
3449                         inCache -= newLen;
3450                         nWrite = 0;
3451                     } else {
3452                         nWrite = newLen - inCache;
3453                         inCache = 0;
3454                     }
3455                 } else {
3456                     nWrite = newLen;
3457                 }
3458                 if (nWrite > 0 && outputStream != null)
3459                     outputStream.write(b, off + (newLen-nWrite), nWrite);
3460                 if (marked) {
3461                     markCount += newLen;
3462                 }
3463                 return newLen;
3464             } catch (IOException ioex) {
3465                 if (cacheRequest != null) {
3466                     cacheRequest.abort();
3467                 }
3468                 throw ioex;
3469             }
3470         }
3471 
3472         /* skip() calls read() in order to ensure that entire response gets
3473          * cached. same implementation as InputStream.skip */
3474 
3475         private byte[] skipBuffer;
3476         private static final int SKIP_BUFFER_SIZE = 8096;
3477 
3478         @Override
3479         public long skip (long n) throws IOException {
3480             ensureOpen();
3481             long remaining = n;
3482             int nr;
3483             if (skipBuffer == null)
3484                 skipBuffer = new byte[SKIP_BUFFER_SIZE];
3485 
3486             byte[] localSkipBuffer = skipBuffer;
3487 
3488             if (n <= 0) {
3489                 return 0;
3490             }
3491 
3492             while (remaining > 0) {
3493                 nr = read(localSkipBuffer, 0,
3494                           (int) Math.min(SKIP_BUFFER_SIZE, remaining));
3495                 if (nr < 0) {
3496                     break;
3497                 }
3498                 remaining -= nr;
3499             }
3500 
3501             return n - remaining;
3502         }
3503 
3504         @Override
3505         public void close () throws IOException {
3506             if (closed)
3507                 return;
3508 
3509             try {
3510                 if (outputStream != null) {
3511                     if (read() != -1) {
3512                         cacheRequest.abort();
3513                     } else {
3514                         outputStream.close();
3515                     }
3516                 }
3517                 super.close ();
3518             } catch (IOException ioex) {
3519                 if (cacheRequest != null) {
3520                     cacheRequest.abort();
3521                 }
3522                 throw ioex;
3523             } finally {
3524                 closed = true;
3525                 HttpURLConnection.this.http = null;
3526                 checkResponseCredentials (true);
3527             }
3528         }
3529     }
3530 
3531     class StreamingOutputStream extends FilterOutputStream {
3532 
3533         long expected;
3534         long written;
3535         boolean closed;
3536         boolean error;
3537         IOException errorExcp;
3538 
3539         /**
3540          * expectedLength == -1 if the stream is chunked
3541          * expectedLength > 0 if the stream is fixed content-length
3542          *    In the 2nd case, we make sure the expected number of
3543          *    of bytes are actually written
3544          */
3545         StreamingOutputStream (OutputStream os, long expectedLength) {
3546             super (os);
3547             expected = expectedLength;
3548             written = 0L;
3549             closed = false;
3550             error = false;
3551         }
3552 
3553         @Override
3554         public void write (int b) throws IOException {
3555             checkError();
3556             written ++;
3557             if (expected != -1L && written > expected) {
3558                 throw new IOException ("too many bytes written");
3559             }
3560             out.write (b);
3561         }
3562 
3563         @Override
3564         public void write (byte[] b) throws IOException {
3565             write (b, 0, b.length);
3566         }
3567 
3568         @Override
3569         public void write (byte[] b, int off, int len) throws IOException {
3570             checkError();
3571             written += len;
3572             if (expected != -1L && written > expected) {
3573                 out.close ();
3574                 throw new IOException ("too many bytes written");
3575             }
3576             out.write (b, off, len);
3577         }
3578 
3579         void checkError () throws IOException {
3580             if (closed) {
3581                 throw new IOException ("Stream is closed");
3582             }
3583             if (error) {
3584                 throw errorExcp;
3585             }
3586             if (((PrintStream)out).checkError()) {
3587                 throw new IOException("Error writing request body to server");
3588             }
3589         }
3590 
3591         /* this is called to check that all the bytes
3592          * that were supposed to be written were written
3593          * and that the stream is now closed().
3594          */
3595         boolean writtenOK () {
3596             return closed && ! error;
3597         }
3598 
3599         @Override
3600         public void close () throws IOException {
3601             if (closed) {
3602                 return;
3603             }
3604             closed = true;
3605             if (expected != -1L) {
3606                 /* not chunked */
3607                 if (written != expected) {
3608                     error = true;
3609                     errorExcp = new IOException ("insufficient data written");
3610                     out.close ();
3611                     throw errorExcp;
3612                 }
3613                 super.flush(); /* can't close the socket */
3614             } else {
3615                 /* chunked */
3616                 super.close (); /* force final chunk to be written */
3617                 /* trailing \r\n */
3618                 OutputStream o = http.getOutputStream();
3619                 o.write ('\r');
3620                 o.write ('\n');
3621                 o.flush();
3622             }
3623         }
3624     }
3625 
3626 
3627     static class ErrorStream extends InputStream {
3628         ByteBuffer buffer;
3629         InputStream is;
3630 
3631         private ErrorStream(ByteBuffer buf) {
3632             buffer = buf;
3633             is = null;
3634         }
3635 
3636         private ErrorStream(ByteBuffer buf, InputStream is) {
3637             buffer = buf;
3638             this.is = is;
3639         }
3640 
3641         // when this method is called, it's either the case that cl > 0, or
3642         // if chunk-encoded, cl = -1; in other words, cl can't be 0
3643         public static InputStream getErrorStream(InputStream is, long cl, HttpClient http) {
3644 
3645             // cl can't be 0; this following is here for extra precaution
3646             if (cl == 0) {
3647                 return null;
3648             }
3649 
3650             try {
3651                 // set SO_TIMEOUT to 1/5th of the total timeout
3652                 // remember the old timeout value so that we can restore it
3653                 int oldTimeout = http.getReadTimeout();
3654                 http.setReadTimeout(timeout4ESBuffer/5);
3655 
3656                 long expected = 0;
3657                 boolean isChunked = false;
3658                 // the chunked case
3659                 if (cl < 0) {
3660                     expected = bufSize4ES;
3661                     isChunked = true;
3662                 } else {
3663                     expected = cl;
3664                 }
3665                 if (expected <= bufSize4ES) {
3666                     int exp = (int) expected;
3667                     byte[] buffer = new byte[exp];
3668                     int count = 0, time = 0, len = 0;
3669                     do {
3670                         try {
3671                             len = is.read(buffer, count,
3672                                              buffer.length - count);
3673                             if (len < 0) {
3674                                 if (isChunked) {
3675                                     // chunked ended
3676                                     // if chunked ended prematurely,
3677                                     // an IOException would be thrown
3678                                     break;
3679                                 }
3680                                 // the server sends less than cl bytes of data
3681                                 throw new IOException("the server closes"+
3682                                                       " before sending "+cl+
3683                                                       " bytes of data");
3684                             }
3685                             count += len;
3686                         } catch (SocketTimeoutException ex) {
3687                             time += timeout4ESBuffer/5;
3688                         }
3689                     } while (count < exp && time < timeout4ESBuffer);
3690 
3691                     // reset SO_TIMEOUT to old value
3692                     http.setReadTimeout(oldTimeout);
3693 
3694                     // if count < cl at this point, we will not try to reuse
3695                     // the connection
3696                     if (count == 0) {
3697                         // since we haven't read anything,
3698                         // we will return the underlying
3699                         // inputstream back to the application
3700                         return null;
3701                     }  else if ((count == expected && !(isChunked)) || (isChunked && len <0)) {
3702                         // put the connection into keep-alive cache
3703                         // the inputstream will try to do the right thing
3704                         is.close();
3705                         return new ErrorStream(ByteBuffer.wrap(buffer, 0, count));
3706                     } else {
3707                         // we read part of the response body
3708                         return new ErrorStream(
3709                                       ByteBuffer.wrap(buffer, 0, count), is);
3710                     }
3711                 }
3712                 return null;
3713             } catch (IOException ioex) {
3714                 // ioex.printStackTrace();
3715                 return null;
3716             }
3717         }
3718 
3719         @Override
3720         public int available() throws IOException {
3721             if (is == null) {
3722                 return buffer.remaining();
3723             } else {
3724                 return buffer.remaining()+is.available();
3725             }
3726         }
3727 
3728         public int read() throws IOException {
3729             byte[] b = new byte[1];
3730             int ret = read(b);
3731             return (ret == -1? ret : (b[0] & 0x00FF));
3732         }
3733 
3734         @Override
3735         public int read(byte[] b) throws IOException {
3736             return read(b, 0, b.length);
3737         }
3738 
3739         @Override
3740         public int read(byte[] b, int off, int len) throws IOException {
3741             int rem = buffer.remaining();
3742             if (rem > 0) {
3743                 int ret = rem < len? rem : len;
3744                 buffer.get(b, off, ret);
3745                 return ret;
3746             } else {
3747                 if (is == null) {
3748                     return -1;
3749                 } else {
3750                     return is.read(b, off, len);
3751                 }
3752             }
3753         }
3754 
3755         @Override
3756         public void close() throws IOException {
3757             buffer = null;
3758             if (is != null) {
3759                 is.close();
3760             }
3761         }
3762     }
3763 }
3764 
3765 /** An input stream that just returns EOF.  This is for
3766  * HTTP URLConnections that are KeepAlive && use the
3767  * HEAD method - i.e., stream not dead, but nothing to be read.
3768  */
3769 
3770 class EmptyInputStream extends InputStream {
3771 
3772     @Override
3773     public int available() {
3774         return 0;
3775     }
3776 
3777     public int read() {
3778         return -1;
3779     }
3780 }