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