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