1 /*
   2  * Copyright 1995-2010 Sun Microsystems, Inc.  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.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package sun.net.www.protocol.http;
  27 
  28 import java.net.URL;
  29 import java.net.URLConnection;
  30 import java.net.ProtocolException;
  31 import java.net.HttpRetryException;
  32 import java.net.PasswordAuthentication;
  33 import java.net.Authenticator;
  34 import java.net.InetAddress;
  35 import java.net.UnknownHostException;
  36 import java.net.SocketTimeoutException;
  37 import java.net.Proxy;
  38 import java.net.ProxySelector;
  39 import java.net.URI;
  40 import java.net.InetSocketAddress;
  41 import java.net.CookieHandler;
  42 import java.net.ResponseCache;
  43 import java.net.CacheResponse;
  44 import java.net.SecureCacheResponse;
  45 import java.net.CacheRequest;
  46 import java.net.Authenticator.RequestorType;
  47 import java.io.*;
  48 import java.util.Date;
  49 import java.util.Map;
  50 import java.util.List;
  51 import java.util.Locale;
  52 import java.util.StringTokenizer;
  53 import java.util.Iterator;
  54 import sun.net.*;
  55 import sun.net.www.*;
  56 import sun.net.www.http.HttpClient;
  57 import sun.net.www.http.PosterOutputStream;
  58 import sun.net.www.http.ChunkedInputStream;
  59 import sun.net.www.http.ChunkedOutputStream;
  60 import sun.util.logging.PlatformLogger;
  61 import java.text.SimpleDateFormat;
  62 import java.util.TimeZone;
  63 import java.net.MalformedURLException;
  64 import java.nio.ByteBuffer;
  65 import static sun.net.www.protocol.http.AuthScheme.BASIC;
  66 import static sun.net.www.protocol.http.AuthScheme.DIGEST;
  67 import static sun.net.www.protocol.http.AuthScheme.NTLM;
  68 import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;
  69 import static sun.net.www.protocol.http.AuthScheme.KERBEROS;
  70 import static sun.net.www.protocol.http.AuthScheme.UNKNOWN;
  71 
  72 /**
  73  * A class to represent an HTTP connection to a remote object.
  74  */
  75 
  76 
  77 public class HttpURLConnection extends java.net.HttpURLConnection {
  78 
  79     static String HTTP_CONNECT = "CONNECT";
  80 
  81     static final String version;
  82     public static final String userAgent;
  83 
  84     /* max # of allowed re-directs */
  85     static final int defaultmaxRedirects = 20;
  86     static final int maxRedirects;
  87 
  88     /* Not all servers support the (Proxy)-Authentication-Info headers.
  89      * By default, we don't require them to be sent
  90      */
  91     static final boolean validateProxy;
  92     static final boolean validateServer;
  93 
  94     private StreamingOutputStream strOutputStream;
  95     private final static String RETRY_MSG1 =
  96         "cannot retry due to proxy authentication, in streaming mode";
  97     private final static String RETRY_MSG2 =
  98         "cannot retry due to server authentication, in streaming mode";
  99     private final static String RETRY_MSG3 =
 100         "cannot retry due to redirection, in streaming mode";
 101 
 102     /*
 103      * System properties related to error stream handling:
 104      *
 105      * sun.net.http.errorstream.enableBuffering = <boolean>
 106      *
 107      * With the above system property set to true (default is false),
 108      * when the response code is >=400, the HTTP handler will try to
 109      * buffer the response body (up to a certain amount and within a
 110      * time limit). Thus freeing up the underlying socket connection
 111      * for reuse. The rationale behind this is that usually when the
 112      * server responds with a >=400 error (client error or server
 113      * error, such as 404 file not found), the server will send a
 114      * small response body to explain who to contact and what to do to
 115      * recover. With this property set to true, even if the
 116      * application doesn't call getErrorStream(), read the response
 117      * body, and then call close(), the underlying socket connection
 118      * can still be kept-alive and reused. The following two system
 119      * properties provide further control to the error stream
 120      * buffering behaviour.
 121      *
 122      * sun.net.http.errorstream.timeout = <int>
 123      *     the timeout (in millisec) waiting the error stream
 124      *     to be buffered; default is 300 ms
 125      *
 126      * sun.net.http.errorstream.bufferSize = <int>
 127      *     the size (in bytes) to use for the buffering the error stream;
 128      *     default is 4k
 129      */
 130 
 131 
 132     /* Should we enable buffering of error streams? */
 133     private static boolean enableESBuffer = false;
 134 
 135     /* timeout waiting for read for buffered error stream;
 136      */
 137     private static int timeout4ESBuffer = 0;
 138 
 139     /* buffer size for buffered error stream;
 140     */
 141     private static int bufSize4ES = 0;
 142 
 143     static {
 144         maxRedirects = java.security.AccessController.doPrivileged(
 145             new sun.security.action.GetIntegerAction(
 146                 "http.maxRedirects", defaultmaxRedirects)).intValue();
 147         version = java.security.AccessController.doPrivileged(
 148                     new sun.security.action.GetPropertyAction("java.version"));
 149         String agent = java.security.AccessController.doPrivileged(
 150                     new sun.security.action.GetPropertyAction("http.agent"));
 151         if (agent == null) {
 152             agent = "Java/"+version;
 153         } else {
 154             agent = agent + " Java/"+version;
 155         }
 156         userAgent = agent;
 157         validateProxy = java.security.AccessController.doPrivileged(
 158                 new sun.security.action.GetBooleanAction(
 159                     "http.auth.digest.validateProxy")).booleanValue();
 160         validateServer = java.security.AccessController.doPrivileged(
 161                 new sun.security.action.GetBooleanAction(
 162                     "http.auth.digest.validateServer")).booleanValue();
 163 
 164         enableESBuffer = java.security.AccessController.doPrivileged(
 165                 new sun.security.action.GetBooleanAction(
 166                     "sun.net.http.errorstream.enableBuffering")).booleanValue();
 167         timeout4ESBuffer = java.security.AccessController.doPrivileged(
 168                 new sun.security.action.GetIntegerAction(
 169                     "sun.net.http.errorstream.timeout", 300)).intValue();
 170         if (timeout4ESBuffer <= 0) {
 171             timeout4ESBuffer = 300; // use the default
 172         }
 173 
 174         bufSize4ES = java.security.AccessController.doPrivileged(
 175                 new sun.security.action.GetIntegerAction(
 176                     "sun.net.http.errorstream.bufferSize", 4096)).intValue();
 177         if (bufSize4ES <= 0) {
 178             bufSize4ES = 4096; // use the default
 179         }
 180 
 181 
 182     }
 183 
 184     static final String httpVersion = "HTTP/1.1";
 185     static final String acceptString =
 186         "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";
 187 
 188     // the following http request headers should NOT have their values
 189     // returned for security reasons.
 190     private static final String[] EXCLUDE_HEADERS = {
 191             "Proxy-Authorization",
 192             "Authorization"
 193     };
 194     protected HttpClient http;
 195     protected Handler handler;
 196     protected Proxy instProxy;
 197 
 198     private CookieHandler cookieHandler;
 199     private ResponseCache cacheHandler;
 200 
 201     // the cached response, and cached response headers and body
 202     protected CacheResponse cachedResponse;
 203     private MessageHeader cachedHeaders;
 204     private InputStream cachedInputStream;
 205 
 206     /* output stream to server */
 207     protected PrintStream ps = null;
 208 
 209 
 210     /* buffered error stream */
 211     private InputStream errorStream = null;
 212 
 213     /* User set Cookies */
 214     private boolean setUserCookies = true;
 215     private String userCookies = null;
 216 
 217     /* We only have a single static authenticator for now.
 218      * REMIND:  backwards compatibility with JDK 1.1.  Should be
 219      * eliminated for JDK 2.0.
 220      */
 221     private static HttpAuthenticator defaultAuth;
 222 
 223     /* all the headers we send
 224      * NOTE: do *NOT* dump out the content of 'requests' in the
 225      * output or stacktrace since it may contain security-sensitive
 226      * headers such as those defined in EXCLUDE_HEADERS.
 227      */
 228     private MessageHeader requests;
 229 
 230     /* The following two fields are only used with Digest Authentication */
 231     String domain;      /* The list of authentication domains */
 232     DigestAuthentication.Parameters digestparams;
 233 
 234     /* Current credentials in use */
 235     AuthenticationInfo  currentProxyCredentials = null;
 236     AuthenticationInfo  currentServerCredentials = null;
 237     boolean             needToCheck = true;
 238     private boolean doingNTLM2ndStage = false; /* doing the 2nd stage of an NTLM server authentication */
 239     private boolean doingNTLMp2ndStage = false; /* doing the 2nd stage of an NTLM proxy authentication */
 240 
 241     /* try auth without calling Authenticator. Used for transparent NTLM authentication */
 242     private boolean tryTransparentNTLMServer = true;
 243     private boolean tryTransparentNTLMProxy = true;
 244 
 245     /* Used by Windows specific code */
 246     private Object authObj;
 247 
 248     /* Set if the user is manually setting the Authorization or Proxy-Authorization headers */
 249     boolean isUserServerAuth;
 250     boolean isUserProxyAuth;
 251 


 252     /* Progress source */
 253     protected ProgressSource pi;
 254 
 255     /* all the response headers we get back */
 256     private MessageHeader responses;
 257     /* the stream _from_ the server */
 258     private InputStream inputStream = null;
 259     /* post stream _to_ the server, if any */
 260     private PosterOutputStream poster = null;
 261 
 262     /* Indicates if the std. request headers have been set in requests. */
 263     private boolean setRequests=false;
 264 
 265     /* Indicates whether a request has already failed or not */
 266     private boolean failedOnce=false;
 267 
 268     /* Remembered Exception, we will throw it again if somebody
 269        calls getInputStream after disconnect */
 270     private Exception rememberedException = null;
 271 
 272     /* If we decide we want to reuse a client, we put it here */
 273     private HttpClient reuseClient = null;
 274 
 275     /* Tunnel states */
 276     enum TunnelState {
 277         /* No tunnel */
 278         NONE,
 279 
 280         /* Setting up a tunnel */
 281         SETUP,
 282 
 283         /* Tunnel has been successfully setup */
 284         TUNNELING
 285     }
 286 
 287     private TunnelState tunnelState = TunnelState.NONE;
 288 
 289     /* Redefine timeouts from java.net.URLConnection as we nee -1 to mean
 290      * not set. This is to ensure backward compatibility.
 291      */
 292     private int connectTimeout = -1;
 293     private int readTimeout = -1;
 294 
 295     /* Logging support */
 296     private static final PlatformLogger logger =
 297             PlatformLogger.getLogger("sun.net.www.protocol.http.HttpURLConnection");
 298 
 299     /*
 300      * privileged request password authentication
 301      *
 302      */
 303     private static PasswordAuthentication
 304     privilegedRequestPasswordAuthentication(
 305                             final String host,
 306                             final InetAddress addr,
 307                             final int port,
 308                             final String protocol,
 309                             final String prompt,
 310                             final String scheme,
 311                             final URL url,
 312                             final RequestorType authType) {
 313         return java.security.AccessController.doPrivileged(
 314             new java.security.PrivilegedAction<PasswordAuthentication>() {
 315                 public PasswordAuthentication run() {
 316                     if (logger.isLoggable(PlatformLogger.FINEST)) {
 317                         logger.finest("Requesting Authentication: host =" + host + " url = " + url);
 318                     }
 319                     PasswordAuthentication pass = Authenticator.requestPasswordAuthentication(
 320                         host, addr, port, protocol,
 321                         prompt, scheme, url, authType);
 322                     if (logger.isLoggable(PlatformLogger.FINEST)) {
 323                         logger.finest("Authentication returned: " + (pass != null ? pass.toString() : "null"));
 324                     }
 325                     return pass;
 326                 }
 327             });
 328     }
 329 
 330     /* Logging support */
 331     public static PlatformLogger getHttpLogger() {
 332         return logger;
 333     }
 334 
 335     /* Used for Windows NTLM implementation */
 336     public Object authObj() {
 337         return authObj;
 338     }
 339 
 340     public void authObj(Object authObj) {
 341         this.authObj = authObj;
 342     }
 343 
 344     /*
 345      * checks the validity of http message header and throws
 346      * IllegalArgumentException if invalid.
 347      */
 348     private void checkMessageHeader(String key, String value) {
 349         char LF = '\n';
 350         int index = key.indexOf(LF);
 351         if (index != -1) {
 352             throw new IllegalArgumentException(
 353                 "Illegal character(s) in message header field: " + key);
 354         }
 355         else {
 356             if (value == null) {
 357                 return;
 358             }
 359 
 360             index = value.indexOf(LF);
 361             while (index != -1) {
 362                 index++;
 363                 if (index < value.length()) {
 364                     char c = value.charAt(index);
 365                     if ((c==' ') || (c=='\t')) {
 366                         // ok, check the next occurrence
 367                         index = value.indexOf(LF, index);
 368                         continue;
 369                     }
 370                 }
 371                 throw new IllegalArgumentException(
 372                     "Illegal character(s) in message header value: " + value);
 373             }
 374         }
 375     }
 376 
 377     /* adds the standard key/val pairs to reqests if necessary & write to
 378      * given PrintStream
 379      */
 380     private void writeRequests() throws IOException {
 381         /* print all message headers in the MessageHeader
 382          * onto the wire - all the ones we've set and any
 383          * others that have been set
 384          */
 385         // send any pre-emptive authentication
 386         if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) {
 387             setPreemptiveProxyAuthentication(requests);
 388         }
 389         if (!setRequests) {
 390 
 391             /* We're very particular about the order in which we
 392              * set the request headers here.  The order should not
 393              * matter, but some careless CGI programs have been
 394              * written to expect a very particular order of the
 395              * standard headers.  To name names, the order in which
 396              * Navigator3.0 sends them.  In particular, we make *sure*
 397              * to send Content-type: <> and Content-length:<> second
 398              * to last and last, respectively, in the case of a POST
 399              * request.
 400              */
 401             if (!failedOnce)
 402                 requests.prepend(method + " " + getRequestURI()+" "  +
 403                                  httpVersion, null);
 404             if (!getUseCaches()) {
 405                 requests.setIfNotSet ("Cache-Control", "no-cache");
 406                 requests.setIfNotSet ("Pragma", "no-cache");
 407             }
 408             requests.setIfNotSet("User-Agent", userAgent);
 409             int port = url.getPort();
 410             String host = url.getHost();
 411             if (port != -1 && port != url.getDefaultPort()) {
 412                 host += ":" + String.valueOf(port);
 413             }
 414             requests.setIfNotSet("Host", host);
 415             requests.setIfNotSet("Accept", acceptString);
 416 
 417             /*
 418              * For HTTP/1.1 the default behavior is to keep connections alive.
 419              * However, we may be talking to a 1.0 server so we should set
 420              * keep-alive just in case, except if we have encountered an error
 421              * or if keep alive is disabled via a system property
 422              */
 423 
 424             // Try keep-alive only on first attempt
 425             if (!failedOnce && http.getHttpKeepAliveSet()) {
 426                 if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) {
 427                     requests.setIfNotSet("Proxy-Connection", "keep-alive");
 428                 } else {
 429                     requests.setIfNotSet("Connection", "keep-alive");
 430                 }
 431             } else {
 432                 /*
 433                  * RFC 2616 HTTP/1.1 section 14.10 says:
 434                  * HTTP/1.1 applications that do not support persistent
 435                  * connections MUST include the "close" connection option
 436                  * in every message
 437                  */
 438                 requests.setIfNotSet("Connection", "close");
 439             }
 440             // Set modified since if necessary
 441             long modTime = getIfModifiedSince();
 442             if (modTime != 0 ) {
 443                 Date date = new Date(modTime);
 444                 //use the preferred date format according to RFC 2068(HTTP1.1),
 445                 // RFC 822 and RFC 1123
 446                 SimpleDateFormat fo =
 447                   new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
 448                 fo.setTimeZone(TimeZone.getTimeZone("GMT"));
 449                 requests.setIfNotSet("If-Modified-Since", fo.format(date));
 450             }
 451             // check for preemptive authorization
 452             AuthenticationInfo sauth = AuthenticationInfo.getServerAuth(url);
 453             if (sauth != null && sauth.supportsPreemptiveAuthorization() ) {
 454                 // Sets "Authorization"
 455                 requests.setIfNotSet(sauth.getHeaderName(), sauth.getHeaderValue(url,method));
 456                 currentServerCredentials = sauth;
 457             }
 458 
 459             if (!method.equals("PUT") && (poster != null || streaming())) {
 460                 requests.setIfNotSet ("Content-type",
 461                         "application/x-www-form-urlencoded");
 462             }
 463 
 464             if (streaming()) {
 465                 if (chunkLength != -1) {
 466                     requests.set ("Transfer-Encoding", "chunked");
 467                 } else { /* fixed content length */
 468                     if (fixedContentLengthLong != -1) {
 469                         requests.set ("Content-Length",
 470                                       String.valueOf(fixedContentLengthLong));
 471                     } else if (fixedContentLength != -1) {
 472                         requests.set ("Content-Length",
 473                                       String.valueOf(fixedContentLength));
 474                     }
 475                 }
 476             } else if (poster != null) {
 477                 /* add Content-Length & POST/PUT data */
 478                 synchronized (poster) {
 479                     /* close it, so no more data can be added */
 480                     poster.close();
 481                     requests.set("Content-Length",
 482                                  String.valueOf(poster.size()));
 483                 }
 484             }
 485 
 486             // get applicable cookies based on the uri and request headers
 487             // add them to the existing request headers
 488             setCookieHeader();
 489 
 490             setRequests=true;
 491         }
 492         if (logger.isLoggable(PlatformLogger.FINE)) {
 493             logger.fine(requests.toString());
 494         }
 495         http.writeRequests(requests, poster);
 496         if (ps.checkError()) {
 497             String proxyHost = http.getProxyHostUsed();
 498             int proxyPort = http.getProxyPortUsed();
 499             disconnectInternal();
 500             if (failedOnce) {
 501                 throw new IOException("Error writing to server");
 502             } else { // try once more
 503                 failedOnce=true;
 504                 if (proxyHost != null) {
 505                     setProxiedClient(url, proxyHost, proxyPort);
 506                 } else {
 507                     setNewClient (url);
 508                 }
 509                 ps = (PrintStream) http.getOutputStream();
 510                 connected=true;
 511                 responses = new MessageHeader();
 512                 setRequests=false;
 513                 writeRequests();
 514             }
 515         }
 516     }
 517 
 518 
 519     /**
 520      * Create a new HttpClient object, bypassing the cache of
 521      * HTTP client objects/connections.
 522      *
 523      * @param url       the URL being accessed
 524      */
 525     protected void setNewClient (URL url)
 526     throws IOException {
 527         setNewClient(url, false);
 528     }
 529 
 530     /**
 531      * Obtain a HttpsClient object. Use the cached copy if specified.
 532      *
 533      * @param url       the URL being accessed
 534      * @param useCache  whether the cached connection should be used
 535      *        if present
 536      */
 537     protected void setNewClient (URL url, boolean useCache)
 538         throws IOException {
 539         http = HttpClient.New(url, null, -1, useCache, connectTimeout);
 540         http.setReadTimeout(readTimeout);
 541     }
 542 
 543 
 544     /**
 545      * Create a new HttpClient object, set up so that it uses
 546      * per-instance proxying to the given HTTP proxy.  This
 547      * bypasses the cache of HTTP client objects/connections.
 548      *
 549      * @param url       the URL being accessed
 550      * @param proxyHost the proxy host to use
 551      * @param proxyPort the proxy port to use
 552      */
 553     protected void setProxiedClient (URL url, String proxyHost, int proxyPort)
 554     throws IOException {
 555         setProxiedClient(url, proxyHost, proxyPort, false);
 556     }
 557 
 558     /**
 559      * Obtain a HttpClient object, set up so that it uses per-instance
 560      * proxying to the given HTTP proxy. Use the cached copy of HTTP
 561      * client objects/connections if specified.
 562      *
 563      * @param url       the URL being accessed
 564      * @param proxyHost the proxy host to use
 565      * @param proxyPort the proxy port to use
 566      * @param useCache  whether the cached connection should be used
 567      *        if present
 568      */
 569     protected void setProxiedClient (URL url,
 570                                            String proxyHost, int proxyPort,
 571                                            boolean useCache)
 572         throws IOException {
 573         proxiedConnect(url, proxyHost, proxyPort, useCache);
 574     }
 575 
 576     protected void proxiedConnect(URL url,
 577                                            String proxyHost, int proxyPort,
 578                                            boolean useCache)
 579         throws IOException {
 580         http = HttpClient.New (url, proxyHost, proxyPort, useCache, connectTimeout);
 581         http.setReadTimeout(readTimeout);
 582     }
 583 
 584     protected HttpURLConnection(URL u, Handler handler)
 585     throws IOException {
 586         // we set proxy == null to distinguish this case with the case
 587         // when per connection proxy is set
 588         this(u, null, handler);
 589     }
 590 
 591     public HttpURLConnection(URL u, String host, int port) {
 592         this(u, new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(host, port)));
 593     }
 594 
 595     /** this constructor is used by other protocol handlers such as ftp
 596         that want to use http to fetch urls on their behalf.*/
 597     public HttpURLConnection(URL u, Proxy p) {
 598         this(u, p, new Handler());
 599     }
 600 
 601     protected HttpURLConnection(URL u, Proxy p, Handler handler) {
 602         super(u);
 603         requests = new MessageHeader();
 604         responses = new MessageHeader();
 605         this.handler = handler;
 606         instProxy = p;
 607         if (instProxy instanceof sun.net.ApplicationProxy) {
 608             /* Application set Proxies should not have access to cookies
 609              * in a secure environment unless explicitly allowed. */
 610             try {
 611                 cookieHandler = CookieHandler.getDefault();
 612             } catch (SecurityException se) { /* swallow exception */ }
 613         } else {
 614             cookieHandler = java.security.AccessController.doPrivileged(
 615                 new java.security.PrivilegedAction<CookieHandler>() {
 616                 public CookieHandler run() {
 617                     return CookieHandler.getDefault();
 618                 }
 619             });
 620         }
 621         cacheHandler = java.security.AccessController.doPrivileged(
 622             new java.security.PrivilegedAction<ResponseCache>() {
 623                 public ResponseCache run() {
 624                 return ResponseCache.getDefault();
 625             }
 626         });
 627     }
 628 
 629     /**
 630      * @deprecated.  Use java.net.Authenticator.setDefault() instead.
 631      */
 632     public static void setDefaultAuthenticator(HttpAuthenticator a) {
 633         defaultAuth = a;
 634     }
 635 
 636     /**
 637      * opens a stream allowing redirects only to the same host.
 638      */
 639     public static InputStream openConnectionCheckRedirects(URLConnection c)
 640         throws IOException
 641     {
 642         boolean redir;
 643         int redirects = 0;
 644         InputStream in;
 645 
 646         do {
 647             if (c instanceof HttpURLConnection) {
 648                 ((HttpURLConnection) c).setInstanceFollowRedirects(false);
 649             }
 650 
 651             // We want to open the input stream before
 652             // getting headers, because getHeaderField()
 653             // et al swallow IOExceptions.
 654             in = c.getInputStream();
 655             redir = false;
 656 
 657             if (c instanceof HttpURLConnection) {
 658                 HttpURLConnection http = (HttpURLConnection) c;
 659                 int stat = http.getResponseCode();
 660                 if (stat >= 300 && stat <= 307 && stat != 306 &&
 661                         stat != HttpURLConnection.HTTP_NOT_MODIFIED) {
 662                     URL base = http.getURL();
 663                     String loc = http.getHeaderField("Location");
 664                     URL target = null;
 665                     if (loc != null) {
 666                         target = new URL(base, loc);
 667                     }
 668                     http.disconnect();
 669                     if (target == null
 670                         || !base.getProtocol().equals(target.getProtocol())
 671                         || base.getPort() != target.getPort()
 672                         || !hostsEqual(base, target)
 673                         || redirects >= 5)
 674                     {
 675                         throw new SecurityException("illegal URL redirect");
 676                     }
 677                     redir = true;
 678                     c = target.openConnection();
 679                     redirects++;
 680                 }
 681             }
 682         } while (redir);
 683         return in;
 684     }
 685 
 686 
 687     //
 688     // Same as java.net.URL.hostsEqual
 689     //
 690     private static boolean hostsEqual(URL u1, URL u2) {
 691         final String h1 = u1.getHost();
 692         final String h2 = u2.getHost();
 693 
 694         if (h1 == null) {
 695             return h2 == null;
 696         } else if (h2 == null) {
 697             return false;
 698         } else if (h1.equalsIgnoreCase(h2)) {
 699             return true;
 700         }
 701         // Have to resolve addresses before comparing, otherwise
 702         // names like tachyon and tachyon.eng would compare different
 703         final boolean result[] = {false};
 704 
 705         java.security.AccessController.doPrivileged(
 706             new java.security.PrivilegedAction<Void>() {
 707                 public Void run() {
 708                 try {
 709                     InetAddress a1 = InetAddress.getByName(h1);
 710                     InetAddress a2 = InetAddress.getByName(h2);
 711                     result[0] = a1.equals(a2);
 712                 } catch(UnknownHostException e) {
 713                 } catch(SecurityException e) {
 714                 }
 715                 return null;
 716             }
 717         });
 718 
 719         return result[0];
 720     }
 721 
 722     // overridden in HTTPS subclass
 723 
 724     public void connect() throws IOException {
 725         plainConnect();
 726     }
 727 
 728     private boolean checkReuseConnection () {
 729         if (connected) {
 730             return true;
 731         }
 732         if (reuseClient != null) {
 733             http = reuseClient;
 734             http.setReadTimeout(getReadTimeout());
 735             http.reuse = false;
 736             reuseClient = null;
 737             connected = true;
 738             return true;
 739         }
 740         return false;
 741     }
 742 
 743     protected void plainConnect()  throws IOException {
 744         if (connected) {
 745             return;
 746         }
 747         // try to see if request can be served from local cache
 748         if (cacheHandler != null && getUseCaches()) {
 749             try {
 750                 URI uri = ParseUtil.toURI(url);
 751                 if (uri != null) {
 752                     cachedResponse = cacheHandler.get(uri, getRequestMethod(), requests.getHeaders(EXCLUDE_HEADERS));
 753                     if ("https".equalsIgnoreCase(uri.getScheme())
 754                         && !(cachedResponse instanceof SecureCacheResponse)) {
 755                         cachedResponse = null;
 756                     }
 757                     if (logger.isLoggable(PlatformLogger.FINEST)) {
 758                         logger.finest("Cache Request for " + uri + " / " + getRequestMethod());
 759                         logger.finest("From cache: " + (cachedResponse != null ? cachedResponse.toString() : "null"));
 760                     }
 761                     if (cachedResponse != null) {
 762                         cachedHeaders = mapToMessageHeader(cachedResponse.getHeaders());
 763                         cachedInputStream = cachedResponse.getBody();
 764                     }
 765                 }
 766             } catch (IOException ioex) {
 767                 // ignore and commence normal connection
 768             }
 769             if (cachedHeaders != null && cachedInputStream != null) {
 770                 connected = true;
 771                 return;
 772             } else {
 773                 cachedResponse = null;
 774             }
 775         }
 776         try {
 777             /* Try to open connections using the following scheme,
 778              * return on the first one that's successful:
 779              * 1) if (instProxy != null)
 780              *        connect to instProxy; raise exception if failed
 781              * 2) else use system default ProxySelector
 782              * 3) is 2) fails, make direct connection
 783              */
 784 
 785             if (instProxy == null) { // no instance Proxy is set
 786                 /**
 787                  * Do we have to use a proxy?
 788                  */
 789                 ProxySelector sel =
 790                     java.security.AccessController.doPrivileged(
 791                         new java.security.PrivilegedAction<ProxySelector>() {
 792                             public ProxySelector run() {
 793                                      return ProxySelector.getDefault();
 794                                  }
 795                              });
 796                 if (sel != null) {
 797                     URI uri = sun.net.www.ParseUtil.toURI(url);
 798                     if (logger.isLoggable(PlatformLogger.FINEST)) {
 799                         logger.finest("ProxySelector Request for " + uri);
 800                     }
 801                     Iterator<Proxy> it = sel.select(uri).iterator();
 802                     Proxy p;
 803                     while (it.hasNext()) {
 804                         p = it.next();
 805                         try {
 806                             if (!failedOnce) {
 807                                 http = getNewHttpClient(url, p, connectTimeout);
 808                                 http.setReadTimeout(readTimeout);
 809                             } else {
 810                                 // make sure to construct new connection if first
 811                                 // attempt failed
 812                                 http = getNewHttpClient(url, p, connectTimeout, false);
 813                                 http.setReadTimeout(readTimeout);
 814                             }
 815                             if (logger.isLoggable(PlatformLogger.FINEST)) {
 816                                 if (p != null) {
 817                                     logger.finest("Proxy used: " + p.toString());
 818                                 }
 819                             }
 820                             break;
 821                         } catch (IOException ioex) {
 822                             if (p != Proxy.NO_PROXY) {
 823                                 sel.connectFailed(uri, p.address(), ioex);
 824                                 if (!it.hasNext()) {
 825                                     // fallback to direct connection
 826                                     http = getNewHttpClient(url, null, connectTimeout, false);
 827                                     http.setReadTimeout(readTimeout);
 828                                     break;
 829                                 }
 830                             } else {
 831                                 throw ioex;
 832                             }
 833                             continue;
 834                         }
 835                     }
 836                 } else {
 837                     // No proxy selector, create http client with no proxy
 838                     if (!failedOnce) {
 839                         http = getNewHttpClient(url, null, connectTimeout);
 840                         http.setReadTimeout(readTimeout);
 841                     } else {
 842                         // make sure to construct new connection if first
 843                         // attempt failed
 844                         http = getNewHttpClient(url, null, connectTimeout, false);
 845                         http.setReadTimeout(readTimeout);
 846                     }
 847                 }
 848             } else {
 849                 if (!failedOnce) {
 850                     http = getNewHttpClient(url, instProxy, connectTimeout);
 851                     http.setReadTimeout(readTimeout);
 852                 } else {
 853                     // make sure to construct new connection if first
 854                     // attempt failed
 855                     http = getNewHttpClient(url, instProxy, connectTimeout, false);
 856                     http.setReadTimeout(readTimeout);
 857                 }
 858             }
 859 
 860             ps = (PrintStream)http.getOutputStream();
 861         } catch (IOException e) {
 862             throw e;
 863         }
 864         // constructor to HTTP client calls openserver
 865         connected = true;
 866     }
 867 
 868     // subclass HttpsClient will overwrite & return an instance of HttpsClient
 869     protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout)
 870         throws IOException {
 871         return HttpClient.New(url, p, connectTimeout);
 872     }
 873 
 874     // subclass HttpsClient will overwrite & return an instance of HttpsClient
 875     protected HttpClient getNewHttpClient(URL url, Proxy p,
 876                                           int connectTimeout, boolean useCache)
 877         throws IOException {
 878         return HttpClient.New(url, p, connectTimeout, useCache);
 879     }
 880 
 881     private void expect100Continue() throws IOException {
 882             // Expect: 100-Continue was set, so check the return code for
 883             // Acceptance
 884             int oldTimeout = http.getReadTimeout();
 885             boolean enforceTimeOut = false;
 886             boolean timedOut = false;
 887             if (oldTimeout <= 0) {
 888                 // 5s read timeout in case the server doesn't understand
 889                 // Expect: 100-Continue
 890                 http.setReadTimeout(5000);
 891                 enforceTimeOut = true;
 892             }
 893 
 894             try {
 895                 http.parseHTTP(responses, pi, this);
 896             } catch (SocketTimeoutException se) {
 897                 if (!enforceTimeOut) {
 898                     throw se;
 899                 }
 900                 timedOut = true;
 901                 http.setIgnoreContinue(true);
 902             }
 903             if (!timedOut) {
 904                 // Can't use getResponseCode() yet
 905                 String resp = responses.getValue(0);
 906                 // Parse the response which is of the form:
 907                 // HTTP/1.1 417 Expectation Failed
 908                 // HTTP/1.1 100 Continue
 909                 if (resp != null && resp.startsWith("HTTP/")) {
 910                     String[] sa = resp.split("\\s+");
 911                     responseCode = -1;
 912                     try {
 913                         // Response code is 2nd token on the line
 914                         if (sa.length > 1)
 915                             responseCode = Integer.parseInt(sa[1]);
 916                     } catch (NumberFormatException numberFormatException) {
 917                     }
 918                 }
 919                 if (responseCode != 100) {
 920                     throw new ProtocolException("Server rejected operation");
 921                 }
 922             }
 923             if (oldTimeout > 0) {
 924                 http.setReadTimeout(oldTimeout);
 925             }
 926             responseCode = -1;
 927             responses.reset();
 928             // Proceed
 929     }
 930 
 931     /*
 932      * Allowable input/output sequences:
 933      * [interpreted as POST/PUT]
 934      * - get output, [write output,] get input, [read input]
 935      * - get output, [write output]
 936      * [interpreted as GET]
 937      * - get input, [read input]
 938      * Disallowed:
 939      * - get input, [read input,] get output, [write output]
 940      */
 941 
 942     @Override
 943     public synchronized OutputStream getOutputStream() throws IOException {
 944 
 945         try {
 946             if (!doOutput) {
 947                 throw new ProtocolException("cannot write to a URLConnection"
 948                                + " if doOutput=false - call setDoOutput(true)");
 949             }
 950 
 951             if (method.equals("GET")) {
 952                 method = "POST"; // Backward compatibility
 953             }
 954             if (!"POST".equals(method) && !"PUT".equals(method) &&
 955                 "http".equals(url.getProtocol())) {
 956                 throw new ProtocolException("HTTP method " + method +
 957                                             " doesn't support output");
 958             }
 959 
 960             // if there's already an input stream open, throw an exception
 961             if (inputStream != null) {
 962                 throw new ProtocolException("Cannot write output after reading input.");
 963             }
 964 
 965             if (!checkReuseConnection())
 966                 connect();
 967 
 968             boolean expectContinue = false;
 969             String expects = requests.findValue("Expect");
 970             if ("100-Continue".equalsIgnoreCase(expects)) {
 971                 http.setIgnoreContinue(false);
 972                 expectContinue = true;
 973             }
 974 
 975             if (streaming() && strOutputStream == null) {
 976                 writeRequests();
 977             }
 978 
 979             if (expectContinue) {
 980                 expect100Continue();
 981             }
 982             ps = (PrintStream)http.getOutputStream();
 983             if (streaming()) {
 984                 if (strOutputStream == null) {
 985                     if (chunkLength != -1) { /* chunked */
 986                          strOutputStream = new StreamingOutputStream(
 987                                new ChunkedOutputStream(ps, chunkLength), -1L);
 988                     } else { /* must be fixed content length */
 989                         long length = 0L;
 990                         if (fixedContentLengthLong != -1) {
 991                             length = fixedContentLengthLong;
 992                         } else if (fixedContentLength != -1) {
 993                             length = fixedContentLength;
 994                         }
 995                         strOutputStream = new StreamingOutputStream(ps, length);
 996                     }
 997                 }
 998                 return strOutputStream;
 999             } else {
1000                 if (poster == null) {
1001                     poster = new PosterOutputStream();
1002                 }
1003                 return poster;
1004             }
1005         } catch (RuntimeException e) {
1006             disconnectInternal();
1007             throw e;
1008         } catch (ProtocolException e) {
1009             // Save the response code which may have been set while enforcing
1010             // the 100-continue. disconnectInternal() forces it to -1
1011             int i = responseCode;
1012             disconnectInternal();
1013             responseCode = i;
1014             throw e;
1015         } catch (IOException e) {
1016             disconnectInternal();
1017             throw e;
1018         }
1019     }
1020 
1021     private boolean streaming () {
1022         return (fixedContentLength != -1) || (fixedContentLengthLong != -1) ||
1023                (chunkLength != -1);
1024     }
1025 
1026     /*
1027      * get applicable cookies based on the uri and request headers
1028      * add them to the existing request headers
1029      */
1030     private void setCookieHeader() throws IOException {
1031         if (cookieHandler != null) {
1032             // we only want to capture the user defined Cookies once, as
1033             // they cannot be changed by user code after we are connected,
1034             // only internally.
1035             if (setUserCookies) {
1036                 int k = requests.getKey("Cookie");
1037                 if ( k != -1)
1038                     userCookies = requests.getValue(k);
1039                 setUserCookies = false;
1040             }
1041 
1042             // remove old Cookie header before setting new one.
1043             requests.remove("Cookie");
1044 
1045             URI uri = ParseUtil.toURI(url);
1046             if (uri != null) {
1047                 if (logger.isLoggable(PlatformLogger.FINEST)) {
1048                     logger.finest("CookieHandler request for " + uri);
1049                 }
1050                 Map<String, List<String>> cookies
1051                     = cookieHandler.get(
1052                         uri, requests.getHeaders(EXCLUDE_HEADERS));
1053                 if (!cookies.isEmpty()) {
1054                     if (logger.isLoggable(PlatformLogger.FINEST)) {
1055                         logger.finest("Cookies retrieved: " + cookies.toString());
1056                     }
1057                     for (Map.Entry<String, List<String>> entry :
1058                              cookies.entrySet()) {
1059                         String key = entry.getKey();
1060                         // ignore all entries that don't have "Cookie"
1061                         // or "Cookie2" as keys
1062                         if (!"Cookie".equalsIgnoreCase(key) &&
1063                             !"Cookie2".equalsIgnoreCase(key)) {
1064                             continue;
1065                         }
1066                         List<String> l = entry.getValue();
1067                         if (l != null && !l.isEmpty()) {
1068                             StringBuilder cookieValue = new StringBuilder();
1069                             for (String value : l) {
1070                                 cookieValue.append(value).append("; ");
1071                             }
1072                             // strip off the trailing '; '
1073                             try {
1074                                 requests.add(key, cookieValue.substring(0, cookieValue.length() - 2));
1075                             } catch (StringIndexOutOfBoundsException ignored) {
1076                                 // no-op
1077                             }
1078                         }
1079                     }
1080                 }
1081             }
1082             if (userCookies != null) {
1083                 int k;
1084                 if ((k = requests.getKey("Cookie")) != -1)
1085                     requests.set("Cookie", requests.getValue(k) + ";" + userCookies);
1086                 else
1087                     requests.set("Cookie", userCookies);
1088             }
1089 
1090         } // end of getting cookies
1091     }
1092 
1093     @Override
1094     @SuppressWarnings("empty-statement")
1095     public synchronized InputStream getInputStream() throws IOException {
1096 
1097         if (!doInput) {
1098             throw new ProtocolException("Cannot read from URLConnection"
1099                    + " if doInput=false (call setDoInput(true))");
1100         }
1101 
1102         if (rememberedException != null) {
1103             if (rememberedException instanceof RuntimeException)
1104                 throw new RuntimeException(rememberedException);
1105             else {
1106                 throw getChainedException((IOException)rememberedException);
1107             }
1108         }
1109 
1110         if (inputStream != null) {
1111             return inputStream;
1112         }
1113 
1114         if (streaming() ) {
1115             if (strOutputStream == null) {
1116                 getOutputStream();
1117             }
1118             /* make sure stream is closed */
1119             strOutputStream.close ();
1120             if (!strOutputStream.writtenOK()) {
1121                 throw new IOException ("Incomplete output stream");
1122             }
1123         }
1124 
1125         int redirects = 0;
1126         int respCode = 0;
1127         long cl = -1;
1128         AuthenticationInfo serverAuthentication = null;
1129         AuthenticationInfo proxyAuthentication = null;
1130         AuthenticationHeader srvHdr = null;
1131 
1132         /**
1133          * Failed Negotiate
1134          *
1135          * In some cases, the Negotiate auth is supported for the
1136          * remote host but the negotiate process still fails (For
1137          * example, if the web page is located on a backend server
1138          * and delegation is needed but fails). The authentication
1139          * process will start again, and we need to detect this
1140          * kind of failure and do proper fallback (say, to NTLM).
1141          *
1142          * In order to achieve this, the inNegotiate flag is set
1143          * when the first negotiate challenge is met (and reset
1144          * if authentication is finished). If a fresh new negotiate
1145          * challenge (no parameter) is found while inNegotiate is
1146          * set, we know there's a failed auth attempt recently.
1147          * Here we'll ignore the header line so that fallback
1148          * can be practiced.
1149          *
1150          * inNegotiateProxy is for proxy authentication.
1151          */
1152         boolean inNegotiate = false;
1153         boolean inNegotiateProxy = false;
1154 
1155         // If the user has set either of these headers then do not remove them
1156         isUserServerAuth = requests.getKey("Authorization") != -1;
1157         isUserProxyAuth = requests.getKey("Proxy-Authorization") != -1;
1158 
1159         try {
1160             do {
1161                 if (!checkReuseConnection())
1162                     connect();
1163 
1164                 if (cachedInputStream != null) {
1165                     return cachedInputStream;
1166                 }
1167 
1168                 // Check if URL should be metered
1169                 boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, method);
1170 
1171                 if (meteredInput)   {
1172                     pi = new ProgressSource(url, method);
1173                     pi.beginTracking();
1174                 }
1175 
1176                 /* REMIND: This exists to fix the HttpsURLConnection subclass.
1177                  * Hotjava needs to run on JDK1.1FCS.  Do proper fix once a
1178                  * proper solution for SSL can be found.
1179                  */
1180                 ps = (PrintStream)http.getOutputStream();
1181 
1182                 if (!streaming()) {
1183                     writeRequests();
1184                 }
1185                 http.parseHTTP(responses, pi, this);
1186                 if (logger.isLoggable(PlatformLogger.FINE)) {
1187                     logger.fine(responses.toString());
1188                 }
1189                 inputStream = http.getInputStream();
1190 
1191                 respCode = getResponseCode();
1192                 if (respCode == -1) {
1193                     disconnectInternal();
1194                     throw new IOException ("Invalid Http response");
1195                 }
1196                 if (respCode == HTTP_PROXY_AUTH) {
1197                     if (streaming()) {
1198                         disconnectInternal();
1199                         throw new HttpRetryException (
1200                             RETRY_MSG1, HTTP_PROXY_AUTH);
1201                     }
1202 
1203                     // Read comments labeled "Failed Negotiate" for details.
1204                     boolean dontUseNegotiate = false;
1205                     Iterator iter = responses.multiValueIterator("Proxy-Authenticate");
1206                     while (iter.hasNext()) {
1207                         String value = ((String)iter.next()).trim();
1208                         if (value.equalsIgnoreCase("Negotiate") ||
1209                                 value.equalsIgnoreCase("Kerberos")) {
1210                             if (!inNegotiateProxy) {
1211                                 inNegotiateProxy = true;
1212                             } else {
1213                                 dontUseNegotiate = true;
1214                                 doingNTLMp2ndStage = false;
1215                                 proxyAuthentication = null;
1216                             }
1217                             break;
1218                         }
1219                     }
1220 
1221                     // changes: add a 3rd parameter to the constructor of
1222                     // AuthenticationHeader, so that NegotiateAuthentication.
1223                     // isSupported can be tested.
1224                     // The other 2 appearances of "new AuthenticationHeader" is
1225                     // altered in similar ways.
1226 
1227                     AuthenticationHeader authhdr = new AuthenticationHeader (
1228                             "Proxy-Authenticate", responses,
1229                             new HttpCallerInfo(url, http.getProxyHostUsed(),
1230                                 http.getProxyPortUsed()),
1231                             dontUseNegotiate
1232                     );
1233 
1234                     if (!doingNTLMp2ndStage) {
1235                         proxyAuthentication =
1236                             resetProxyAuthentication(proxyAuthentication, authhdr);
1237                         if (proxyAuthentication != null) {
1238                             redirects++;
1239                             disconnectInternal();
1240                             continue;
1241                         }
1242                     } else {
1243                         /* in this case, only one header field will be present */
1244                         String raw = responses.findValue ("Proxy-Authenticate");
1245                         reset ();
1246                         if (!proxyAuthentication.setHeaders(this,
1247                                                         authhdr.headerParser(), raw)) {
1248                             disconnectInternal();
1249                             throw new IOException ("Authentication failure");
1250                         }
1251                         if (serverAuthentication != null && srvHdr != null &&
1252                                 !serverAuthentication.setHeaders(this,
1253                                                         srvHdr.headerParser(), raw)) {
1254                             disconnectInternal ();
1255                             throw new IOException ("Authentication failure");
1256                         }
1257                         authObj = null;
1258                         doingNTLMp2ndStage = false;
1259                         continue;
1260                     }
1261                 } else {
1262                     inNegotiateProxy = false;
1263                     doingNTLMp2ndStage = false;
1264                     if (!isUserProxyAuth)
1265                         requests.remove("Proxy-Authorization");
1266                 }
1267 
1268                 // cache proxy authentication info
1269                 if (proxyAuthentication != null) {
1270                     // cache auth info on success, domain header not relevant.
1271                     proxyAuthentication.addToCache();
1272                 }
1273 
1274                 if (respCode == HTTP_UNAUTHORIZED) {
1275                     if (streaming()) {
1276                         disconnectInternal();
1277                         throw new HttpRetryException (
1278                             RETRY_MSG2, HTTP_UNAUTHORIZED);
1279                     }
1280 
1281                     // Read comments labeled "Failed Negotiate" for details.
1282                     boolean dontUseNegotiate = false;
1283                     Iterator iter = responses.multiValueIterator("WWW-Authenticate");
1284                     while (iter.hasNext()) {
1285                         String value = ((String)iter.next()).trim();
1286                         if (value.equalsIgnoreCase("Negotiate") ||
1287                                 value.equalsIgnoreCase("Kerberos")) {
1288                             if (!inNegotiate) {
1289                                 inNegotiate = true;
1290                             } else {
1291                                 dontUseNegotiate = true;
1292                                 doingNTLM2ndStage = false;
1293                                 serverAuthentication = null;
1294                             }
1295                             break;
1296                         }
1297                     }
1298 
1299                     srvHdr = new AuthenticationHeader (
1300                             "WWW-Authenticate", responses,
1301                             new HttpCallerInfo(url),
1302                             dontUseNegotiate
1303                     );
1304 
1305                     String raw = srvHdr.raw();
1306                     if (!doingNTLM2ndStage) {
1307                         if ((serverAuthentication != null)&&
1308                             serverAuthentication.getAuthScheme() != NTLM) {
1309                             if (serverAuthentication.isAuthorizationStale (raw)) {
1310                                 /* we can retry with the current credentials */
1311                                 disconnectWeb();
1312                                 redirects++;
1313                                 requests.set(serverAuthentication.getHeaderName(),
1314                                             serverAuthentication.getHeaderValue(url, method));
1315                                 currentServerCredentials = serverAuthentication;
1316                                 setCookieHeader();
1317                                 continue;
1318                             } else {
1319                                 serverAuthentication.removeFromCache();
1320                             }
1321                         }
1322                         serverAuthentication = getServerAuthentication(srvHdr);
1323                         currentServerCredentials = serverAuthentication;
1324 
1325                         if (serverAuthentication != null) {
1326                             disconnectWeb();
1327                             redirects++; // don't let things loop ad nauseum
1328                             setCookieHeader();
1329                             continue;
1330                         }
1331                     } else {
1332                         reset ();
1333                         /* header not used for ntlm */
1334                         if (!serverAuthentication.setHeaders(this, null, raw)) {
1335                             disconnectWeb();
1336                             throw new IOException ("Authentication failure");
1337                         }
1338                         doingNTLM2ndStage = false;
1339                         authObj = null;
1340                         setCookieHeader();
1341                         continue;
1342                     }
1343                 }
1344                 // cache server authentication info
1345                 if (serverAuthentication != null) {
1346                     // cache auth info on success
1347                     if (!(serverAuthentication instanceof DigestAuthentication) ||
1348                         (domain == null)) {
1349                         if (serverAuthentication instanceof BasicAuthentication) {
1350                             // check if the path is shorter than the existing entry
1351                             String npath = AuthenticationInfo.reducePath (url.getPath());
1352                             String opath = serverAuthentication.path;
1353                             if (!opath.startsWith (npath) || npath.length() >= opath.length()) {
1354                                 /* npath is longer, there must be a common root */
1355                                 npath = BasicAuthentication.getRootPath (opath, npath);
1356                             }
1357                             // remove the entry and create a new one
1358                             BasicAuthentication a =
1359                                 (BasicAuthentication) serverAuthentication.clone();
1360                             serverAuthentication.removeFromCache();
1361                             a.path = npath;
1362                             serverAuthentication = a;
1363                         }
1364                         serverAuthentication.addToCache();
1365                     } else {
1366                         // what we cache is based on the domain list in the request
1367                         DigestAuthentication srv = (DigestAuthentication)
1368                             serverAuthentication;
1369                         StringTokenizer tok = new StringTokenizer (domain," ");
1370                         String realm = srv.realm;
1371                         PasswordAuthentication pw = srv.pw;
1372                         digestparams = srv.params;
1373                         while (tok.hasMoreTokens()) {
1374                             String path = tok.nextToken();
1375                             try {
1376                                 /* path could be an abs_path or a complete URI */
1377                                 URL u = new URL (url, path);
1378                                 DigestAuthentication d = new DigestAuthentication (
1379                                                    false, u, realm, "Digest", pw, digestparams);
1380                                 d.addToCache ();
1381                             } catch (Exception e) {}
1382                         }
1383                     }
1384                 }
1385 
1386                 // some flags should be reset to its initialized form so that
1387                 // even after a redirect the necessary checks can still be
1388                 // preformed.
1389                 inNegotiate = false;
1390                 inNegotiateProxy = false;
1391 
1392                 //serverAuthentication = null;
1393                 doingNTLMp2ndStage = false;
1394                 doingNTLM2ndStage = false;
1395                 if (!isUserServerAuth)
1396                     requests.remove("Authorization");
1397                 if (!isUserProxyAuth)
1398                     requests.remove("Proxy-Authorization");
1399 
1400                 if (respCode == HTTP_OK) {
1401                     checkResponseCredentials (false);
1402                 } else {
1403                     needToCheck = false;
1404                 }
1405 
1406                 // a flag need to clean
1407                 needToCheck = true;
1408 
1409                 if (followRedirect()) {
1410                     /* if we should follow a redirect, then the followRedirects()
1411                      * method will disconnect() and re-connect us to the new
1412                      * location
1413                      */
1414                     redirects++;
1415 
1416                     // redirecting HTTP response may have set cookie, so
1417                     // need to re-generate request header
1418                     setCookieHeader();
1419 
1420                     continue;
1421                 }
1422 
1423                 try {
1424                     cl = Long.parseLong(responses.findValue("content-length"));
1425                 } catch (Exception exc) { };
1426 
1427                 if (method.equals("HEAD") || cl == 0 ||
1428                     respCode == HTTP_NOT_MODIFIED ||
1429                     respCode == HTTP_NO_CONTENT) {
1430 
1431                     if (pi != null) {
1432                         pi.finishTracking();
1433                         pi = null;
1434                     }
1435                     http.finished();
1436                     http = null;
1437                     inputStream = new EmptyInputStream();
1438                     connected = false;
1439                 }
1440 
1441                 if (respCode == 200 || respCode == 203 || respCode == 206 ||
1442                     respCode == 300 || respCode == 301 || respCode == 410) {
1443                     if (cacheHandler != null) {
1444                         // give cache a chance to save response in cache
1445                         URI uri = ParseUtil.toURI(url);
1446                         if (uri != null) {
1447                             URLConnection uconn = this;
1448                             if ("https".equalsIgnoreCase(uri.getScheme())) {
1449                                 try {
1450                                 // use reflection to get to the public
1451                                 // HttpsURLConnection instance saved in
1452                                 // DelegateHttpsURLConnection
1453                                 uconn = (URLConnection)this.getClass().getField("httpsURLConnection").get(this);
1454                                 } catch (IllegalAccessException iae) {
1455                                     // ignored; use 'this'
1456                                 } catch (NoSuchFieldException nsfe) {
1457                                     // ignored; use 'this'
1458                                 }
1459                             }
1460                             CacheRequest cacheRequest =
1461                                 cacheHandler.put(uri, uconn);
1462                             if (cacheRequest != null && http != null) {
1463                                 http.setCacheRequest(cacheRequest);
1464                                 inputStream = new HttpInputStream(inputStream, cacheRequest);
1465                             }
1466                         }
1467                     }
1468                 }
1469 
1470                 if (!(inputStream instanceof HttpInputStream)) {
1471                     inputStream = new HttpInputStream(inputStream);
1472                 }
1473 
1474                 if (respCode >= 400) {
1475                     if (respCode == 404 || respCode == 410) {
1476                         throw new FileNotFoundException(url.toString());
1477                     } else {
1478                         throw new java.io.IOException("Server returned HTTP" +
1479                               " response code: " + respCode + " for URL: " +
1480                               url.toString());
1481                     }
1482                 }
1483                 poster = null;
1484                 strOutputStream = null;
1485                 return inputStream;
1486             } while (redirects < maxRedirects);
1487 
1488             throw new ProtocolException("Server redirected too many " +
1489                                         " times ("+ redirects + ")");
1490         } catch (RuntimeException e) {
1491             disconnectInternal();
1492             rememberedException = e;
1493             throw e;
1494         } catch (IOException e) {
1495             rememberedException = e;
1496 
1497             // buffer the error stream if bytes < 4k
1498             // and it can be buffered within 1 second
1499             String te = responses.findValue("Transfer-Encoding");
1500             if (http != null && http.isKeepingAlive() && enableESBuffer &&
1501                 (cl > 0 || (te != null && te.equalsIgnoreCase("chunked")))) {
1502                 errorStream = ErrorStream.getErrorStream(inputStream, cl, http);
1503             }
1504             throw e;
1505         } finally {
1506             if (respCode == HTTP_PROXY_AUTH && proxyAuthentication != null) {
1507                 proxyAuthentication.endAuthRequest();
1508             }
1509             else if (respCode == HTTP_UNAUTHORIZED && serverAuthentication != null) {
1510                 serverAuthentication.endAuthRequest();
1511             }
1512         }
1513     }
1514 
1515     /*
1516      * Creates a chained exception that has the same type as
1517      * original exception and with the same message. Right now,
1518      * there is no convenient APIs for doing so.
1519      */
1520     private IOException getChainedException(final IOException rememberedException) {
1521         try {
1522             final Object[] args = { rememberedException.getMessage() };
1523             IOException chainedException =
1524                 java.security.AccessController.doPrivileged(
1525                     new java.security.PrivilegedExceptionAction<IOException>() {
1526                         public IOException run() throws Exception {
1527                             return (IOException)
1528                                 rememberedException.getClass()
1529                                 .getConstructor(new Class[] { String.class })
1530                                 .newInstance(args);
1531                         }
1532                     });
1533             chainedException.initCause(rememberedException);
1534             return chainedException;
1535         } catch (Exception ignored) {
1536             return rememberedException;
1537         }
1538     }
1539 
1540     @Override
1541     public InputStream getErrorStream() {
1542         if (connected && responseCode >= 400) {
1543             // Client Error 4xx and Server Error 5xx
1544             if (errorStream != null) {
1545                 return errorStream;
1546             } else if (inputStream != null) {
1547                 return inputStream;
1548             }
1549         }
1550         return null;
1551     }
1552 
1553     /**
1554      * set or reset proxy authentication info in request headers
1555      * after receiving a 407 error. In the case of NTLM however,
1556      * receiving a 407 is normal and we just skip the stale check
1557      * because ntlm does not support this feature.
1558      */
1559     private AuthenticationInfo
1560         resetProxyAuthentication(AuthenticationInfo proxyAuthentication, AuthenticationHeader auth) throws IOException {
1561         if ((proxyAuthentication != null )&&
1562              proxyAuthentication.getAuthScheme() != NTLM) {
1563             String raw = auth.raw();
1564             if (proxyAuthentication.isAuthorizationStale (raw)) {
1565                 /* we can retry with the current credentials */
1566                 String value;
1567                 if (proxyAuthentication instanceof DigestAuthentication) {
1568                     DigestAuthentication digestProxy = (DigestAuthentication)
1569                         proxyAuthentication;
1570                     if (tunnelState() == TunnelState.SETUP) {
1571                         value = digestProxy.getHeaderValue(connectRequestURI(url), HTTP_CONNECT);
1572                     } else {
1573                         value = digestProxy.getHeaderValue(getRequestURI(), method);
1574                     }
1575                 } else {
1576                     value = proxyAuthentication.getHeaderValue(url, method);
1577                 }
1578                 requests.set(proxyAuthentication.getHeaderName(), value);
1579                 currentProxyCredentials = proxyAuthentication;
1580                 return proxyAuthentication;
1581             } else {
1582                 proxyAuthentication.removeFromCache();
1583             }
1584         }
1585         proxyAuthentication = getHttpProxyAuthentication(auth);
1586         currentProxyCredentials = proxyAuthentication;
1587         return  proxyAuthentication;
1588     }
1589 
1590     /**
1591      * Returns the tunnel state.
1592      *
1593      * @return  the state
1594      */
1595     TunnelState tunnelState() {
1596         return tunnelState;
1597     }
1598 
1599     /**
1600      * Set the tunneling status.
1601      *
1602      * @param  the state
1603      */
1604     void setTunnelState(TunnelState tunnelState) {
1605         this.tunnelState = tunnelState;
1606     }
1607 
1608     /**
1609      * establish a tunnel through proxy server
1610      */
1611     public synchronized void doTunneling() throws IOException {
1612         int retryTunnel = 0;
1613         String statusLine = "";
1614         int respCode = 0;
1615         AuthenticationInfo proxyAuthentication = null;
1616         String proxyHost = null;
1617         int proxyPort = -1;
1618 
1619         // save current requests so that they can be restored after tunnel is setup.
1620         MessageHeader savedRequests = requests;
1621         requests = new MessageHeader();
1622 
1623         // Read comments labeled "Failed Negotiate" for details.
1624         boolean inNegotiateProxy = false;
1625 
1626         try {
1627             /* Actively setting up a tunnel */
1628             setTunnelState(TunnelState.SETUP);
1629 
1630             do {
1631                 if (!checkReuseConnection()) {
1632                     proxiedConnect(url, proxyHost, proxyPort, false);
1633                 }
1634                 // send the "CONNECT" request to establish a tunnel
1635                 // through proxy server
1636                 sendCONNECTRequest();
1637                 responses.reset();
1638 
1639                 // There is no need to track progress in HTTP Tunneling,
1640                 // so ProgressSource is null.
1641                 http.parseHTTP(responses, null, this);
1642 
1643                 /* Log the response to the CONNECT */
1644                 if (logger.isLoggable(PlatformLogger.FINE)) {
1645                     logger.fine(responses.toString());
1646                 }
1647 
1648                 statusLine = responses.getValue(0);
1649                 StringTokenizer st = new StringTokenizer(statusLine);
1650                 st.nextToken();
1651                 respCode = Integer.parseInt(st.nextToken().trim());
1652                 if (respCode == HTTP_PROXY_AUTH) {
1653                     // Read comments labeled "Failed Negotiate" for details.
1654                     boolean dontUseNegotiate = false;
1655                     Iterator iter = responses.multiValueIterator("Proxy-Authenticate");
1656                     while (iter.hasNext()) {
1657                         String value = ((String)iter.next()).trim();
1658                         if (value.equalsIgnoreCase("Negotiate") ||
1659                                 value.equalsIgnoreCase("Kerberos")) {
1660                             if (!inNegotiateProxy) {
1661                                 inNegotiateProxy = true;
1662                             } else {
1663                                 dontUseNegotiate = true;
1664                                 doingNTLMp2ndStage = false;
1665                                 proxyAuthentication = null;
1666                             }
1667                             break;
1668                         }
1669                     }
1670 
1671                     AuthenticationHeader authhdr = new AuthenticationHeader (
1672                             "Proxy-Authenticate", responses,
1673                             new HttpCallerInfo(url, http.getProxyHostUsed(),
1674                                 http.getProxyPortUsed()),
1675                             dontUseNegotiate
1676                     );
1677                     if (!doingNTLMp2ndStage) {
1678                         proxyAuthentication =
1679                             resetProxyAuthentication(proxyAuthentication, authhdr);
1680                         if (proxyAuthentication != null) {
1681                             proxyHost = http.getProxyHostUsed();
1682                             proxyPort = http.getProxyPortUsed();
1683                             disconnectInternal();
1684                             retryTunnel++;
1685                             continue;
1686                         }
1687                     } else {
1688                         String raw = responses.findValue ("Proxy-Authenticate");
1689                         reset ();
1690                         if (!proxyAuthentication.setHeaders(this,
1691                                                 authhdr.headerParser(), raw)) {
1692                             disconnectInternal();
1693                             throw new IOException ("Authentication failure");
1694                         }
1695                         authObj = null;
1696                         doingNTLMp2ndStage = false;
1697                         continue;
1698                     }
1699                 }
1700                 // cache proxy authentication info
1701                 if (proxyAuthentication != null) {
1702                     // cache auth info on success, domain header not relevant.
1703                     proxyAuthentication.addToCache();
1704                 }
1705 
1706                 if (respCode == HTTP_OK) {
1707                     setTunnelState(TunnelState.TUNNELING);
1708                     break;
1709                 }
1710                 // we don't know how to deal with other response code
1711                 // so disconnect and report error
1712                 disconnectInternal();
1713                 setTunnelState(TunnelState.NONE);
1714                 break;
1715             } while (retryTunnel < maxRedirects);
1716 
1717             if (retryTunnel >= maxRedirects || (respCode != HTTP_OK)) {
1718                 throw new IOException("Unable to tunnel through proxy."+
1719                                       " Proxy returns \"" +
1720                                       statusLine + "\"");
1721             }
1722         } finally  {
1723             if (respCode == HTTP_PROXY_AUTH && proxyAuthentication != null) {
1724                 proxyAuthentication.endAuthRequest();
1725             }
1726         }
1727 
1728         // restore original request headers
1729         requests = savedRequests;
1730 
1731         // reset responses
1732         responses.reset();
1733     }
1734 
1735     static String connectRequestURI(URL url) {
1736         String host = url.getHost();
1737         int port = url.getPort();
1738         port = port != -1 ? port : url.getDefaultPort();
1739 
1740         return host + ":" + port;
1741     }
1742 
1743     /**
1744      * send a CONNECT request for establishing a tunnel to proxy server
1745      */
1746     private void sendCONNECTRequest() throws IOException {
1747         int port = url.getPort();
1748 
1749         // setRequests == true indicates the std. request headers
1750         // have been set in (previous) requests.
1751         // so the first one must be the http method (GET, etc.).
1752         // we need to set it to CONNECT soon, remove this one first.
1753         // otherwise, there may have 2 http methods in headers
1754         if (setRequests) requests.set(0, null, null);
1755 
1756         requests.prepend(HTTP_CONNECT + " " + connectRequestURI(url)
1757                          + " " + httpVersion, null);
1758         requests.setIfNotSet("User-Agent", userAgent);
1759 
1760         String host = url.getHost();
1761         if (port != -1 && port != url.getDefaultPort()) {
1762             host += ":" + String.valueOf(port);
1763         }
1764         requests.setIfNotSet("Host", host);
1765 
1766         // Not really necessary for a tunnel, but can't hurt
1767         requests.setIfNotSet("Accept", acceptString);
1768 
1769         setPreemptiveProxyAuthentication(requests);
1770 
1771          /* Log the CONNECT request */
1772         if (logger.isLoggable(PlatformLogger.FINE)) {
1773             logger.fine(requests.toString());
1774         }
1775 
1776         http.writeRequests(requests, null);
1777         // remove CONNECT header
1778         requests.set(0, null, null);
1779     }
1780 
1781     /**
1782      * Sets pre-emptive proxy authentication in header
1783      */
1784     private void setPreemptiveProxyAuthentication(MessageHeader requests) throws IOException {
1785         AuthenticationInfo pauth
1786             = AuthenticationInfo.getProxyAuth(http.getProxyHostUsed(),
1787                                               http.getProxyPortUsed());
1788         if (pauth != null && pauth.supportsPreemptiveAuthorization()) {
1789             String value;
1790             if (pauth instanceof DigestAuthentication) {
1791                 DigestAuthentication digestProxy = (DigestAuthentication) pauth;
1792                 if (tunnelState() == TunnelState.SETUP) {
1793                     value = digestProxy
1794                         .getHeaderValue(connectRequestURI(url), HTTP_CONNECT);
1795                 } else {
1796                     value = digestProxy.getHeaderValue(getRequestURI(), method);
1797                 }
1798             } else {
1799                 value = pauth.getHeaderValue(url, method);
1800             }
1801 
1802             // Sets "Proxy-authorization"
1803             requests.set(pauth.getHeaderName(), value);
1804             currentProxyCredentials = pauth;
1805         }
1806     }
1807 
1808     /**
1809      * Gets the authentication for an HTTP proxy, and applies it to
1810      * the connection.
1811      */
1812     private AuthenticationInfo getHttpProxyAuthentication (AuthenticationHeader authhdr) {
1813         /* get authorization from authenticator */
1814         AuthenticationInfo ret = null;
1815         String raw = authhdr.raw();
1816         String host = http.getProxyHostUsed();
1817         int port = http.getProxyPortUsed();
1818         if (host != null && authhdr.isPresent()) {
1819             HeaderParser p = authhdr.headerParser();
1820             String realm = p.findValue("realm");
1821             String scheme = authhdr.scheme();
1822             AuthScheme authScheme = UNKNOWN;
1823             if ("basic".equalsIgnoreCase(scheme)) {
1824                 authScheme = BASIC;
1825             } else if ("digest".equalsIgnoreCase(scheme)) {
1826                 authScheme = DIGEST;
1827             } else if ("ntlm".equalsIgnoreCase(scheme)) {
1828                 authScheme = NTLM;
1829                 doingNTLMp2ndStage = true;
1830             } else if ("Kerberos".equalsIgnoreCase(scheme)) {
1831                 authScheme = KERBEROS;
1832                 doingNTLMp2ndStage = true;
1833             } else if ("Negotiate".equalsIgnoreCase(scheme)) {
1834                 authScheme = NEGOTIATE;
1835                 doingNTLMp2ndStage = true;
1836             }
1837 
1838             if (realm == null)
1839                 realm = "";
1840             ret = AuthenticationInfo.getProxyAuth(host,
1841                                                   port,
1842                                                   realm,
1843                                                   authScheme);
1844             if (ret == null) {
1845                 switch (authScheme) {
1846                 case BASIC:
1847                     InetAddress addr = null;
1848                     try {
1849                         final String finalHost = host;
1850                         addr = 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.proxy.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.proxy.supportsTransparentAuth;
1887                         }
1888                         a = null;
1889                         if (tryTransparentNTLMProxy) {
1890                             logger.finest("Trying Transparent NTLM authentication");
1891                         } else {
1892                             a = privilegedRequestPasswordAuthentication(
1893                                                 host, null, port, url.getProtocol(),
1894                                                 "", scheme, url, RequestorType.PROXY);
1895                         }
1896                         /* If we are not trying transparent authentication then
1897                          * we need to have a PasswordAuthentication instance. For
1898                          * transparent authentication (Windows only) the username
1899                          * and password will be picked up from the current logged
1900                          * on users credentials.
1901                         */
1902                         if (tryTransparentNTLMProxy ||
1903                               (!tryTransparentNTLMProxy && a != null)) {
1904                             ret = NTLMAuthenticationProxy.proxy.create(true, host, port, a);
1905                         }
1906 
1907                         /* set to false so that we do not try again */
1908                         tryTransparentNTLMProxy = false;
1909                     }
1910                     break;
1911                 case NEGOTIATE:
1912                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
1913                     break;
1914                 case KERBEROS:
1915                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
1916                     break;
1917                 case UNKNOWN:
1918                     logger.finest("Unknown/Unsupported authentication scheme: " + scheme);
1919                 default:
1920                     throw new AssertionError("should not reach here");
1921                 }
1922             }
1923             // For backwards compatibility, we also try defaultAuth
1924             // REMIND:  Get rid of this for JDK2.0.
1925 
1926             if (ret == null && defaultAuth != null
1927                 && defaultAuth.schemeSupported(scheme)) {
1928                 try {
1929                     URL u = new URL("http", host, port, "/");
1930                     String a = defaultAuth.authString(u, scheme, realm);
1931                     if (a != null) {
1932                         ret = new BasicAuthentication (true, host, port, realm, a);
1933                         // not in cache by default - cache on success
1934                     }
1935                 } catch (java.net.MalformedURLException ignored) {
1936                 }
1937             }
1938             if (ret != null) {
1939                 if (!ret.setHeaders(this, p, raw)) {
1940                     ret = null;
1941                 }
1942             }
1943         }
1944         if (logger.isLoggable(PlatformLogger.FINER)) {
1945             logger.finer("Proxy Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
1946         }
1947         return ret;
1948     }
1949 
1950     /**
1951      * Gets the authentication for an HTTP server, and applies it to
1952      * the connection.
1953      * @param authHdr the AuthenticationHeader which tells what auth scheme is
1954      * prefered.
1955      */
1956     private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr) {
1957         /* get authorization from authenticator */
1958         AuthenticationInfo ret = null;
1959         String raw = authhdr.raw();
1960         /* When we get an NTLM auth from cache, don't set any special headers */
1961         if (authhdr.isPresent()) {
1962             HeaderParser p = authhdr.headerParser();
1963             String realm = p.findValue("realm");
1964             String scheme = authhdr.scheme();
1965             AuthScheme authScheme = UNKNOWN;
1966             if ("basic".equalsIgnoreCase(scheme)) {
1967                 authScheme = BASIC;
1968             } else if ("digest".equalsIgnoreCase(scheme)) {
1969                 authScheme = DIGEST;
1970             } else if ("ntlm".equalsIgnoreCase(scheme)) {
1971                 authScheme = NTLM;
1972                 doingNTLM2ndStage = true;
1973             } else if ("Kerberos".equalsIgnoreCase(scheme)) {
1974                 authScheme = KERBEROS;
1975                 doingNTLM2ndStage = true;
1976             } else if ("Negotiate".equalsIgnoreCase(scheme)) {
1977                 authScheme = NEGOTIATE;
1978                 doingNTLM2ndStage = true;
1979             }
1980 
1981             domain = p.findValue ("domain");
1982             if (realm == null)
1983                 realm = "";
1984             ret = AuthenticationInfo.getServerAuth(url, realm, authScheme);

1985             InetAddress addr = null;
1986             if (ret == null) {
1987                 try {
1988                     addr = InetAddress.getByName(url.getHost());
1989                 } catch (java.net.UnknownHostException ignored) {
1990                     // User will have addr = null
1991                 }
1992             }
1993             // replacing -1 with default port for a protocol
1994             int port = url.getPort();
1995             if (port == -1) {
1996                 port = url.getDefaultPort();
1997             }
1998             if (ret == null) {
1999                 switch(authScheme) {
2000                 case KERBEROS:
2001                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
2002                     break;
2003                 case NEGOTIATE:
2004                     ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
2005                     break;
2006                 case BASIC:
2007                     PasswordAuthentication a =
2008                         privilegedRequestPasswordAuthentication(
2009                             url.getHost(), addr, port, url.getProtocol(),
2010                             realm, scheme, url, RequestorType.SERVER);
2011                     if (a != null) {
2012                         ret = new BasicAuthentication(false, url, realm, a);
2013                     }
2014                     break;
2015                 case DIGEST:
2016                     a = privilegedRequestPasswordAuthentication(
2017                             url.getHost(), addr, port, url.getProtocol(),
2018                             realm, scheme, url, RequestorType.SERVER);
2019                     if (a != null) {
2020                         digestparams = new DigestAuthentication.Parameters();
2021                         ret = new DigestAuthentication(false, url, realm, scheme, a, digestparams);
2022                     }
2023                     break;
2024                 case NTLM:
2025                     if (NTLMAuthenticationProxy.proxy.supported) {
2026                         URL url1;
2027                         try {
2028                             url1 = new URL (url, "/"); /* truncate the path */
2029                         } catch (Exception e) {
2030                             url1 = url;
2031                         }
2032 
2033                         /* tryTransparentNTLMServer will always be true the first
2034                          * time around, but verify that the platform supports it
2035                          * otherwise don't try. */
2036                         if (tryTransparentNTLMServer) {
2037                             tryTransparentNTLMServer =
2038                                     NTLMAuthenticationProxy.proxy.supportsTransparentAuth;
2039                         }
2040                         a = null;
2041                         if (tryTransparentNTLMServer) {
2042                             logger.finest("Trying Transparent NTLM authentication");
2043                         } else {
2044                             a = privilegedRequestPasswordAuthentication(
2045                                 url.getHost(), addr, port, url.getProtocol(),
2046                                 "", scheme, url, RequestorType.SERVER);
2047                         }
2048 
2049                         /* If we are not trying transparent authentication then
2050                          * we need to have a PasswordAuthentication instance. For
2051                          * transparent authentication (Windows only) the username
2052                          * and password will be picked up from the current logged
2053                          * on users credentials.
2054                          */
2055                         if (tryTransparentNTLMServer ||
2056                               (!tryTransparentNTLMServer && a != null)) {
2057                             ret = NTLMAuthenticationProxy.proxy.create(false, url1, a);
2058                         }
2059 
2060                         /* set to false so that we do not try again */
2061                         tryTransparentNTLMServer = false;
2062                     }
2063                     break;
2064                 case UNKNOWN:
2065                     logger.finest("Unknown/Unsupported authentication scheme: " + scheme);
2066                 default:
2067                     throw new AssertionError("should not reach here");
2068                 }
2069             }
2070 
2071             // For backwards compatibility, we also try defaultAuth
2072             // REMIND:  Get rid of this for JDK2.0.
2073 
2074             if (ret == null && defaultAuth != null
2075                 && defaultAuth.schemeSupported(scheme)) {
2076                 String a = defaultAuth.authString(url, scheme, realm);
2077                 if (a != null) {
2078                     ret = new BasicAuthentication (false, url, realm, a);
2079                     // not in cache by default - cache on success
2080                 }
2081             }
2082 
2083             if (ret != null ) {
2084                 if (!ret.setHeaders(this, p, raw)) {
2085                     ret = null;
2086                 }
2087             }
2088         }
2089         if (logger.isLoggable(PlatformLogger.FINER)) {
2090             logger.finer("Server Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
2091         }
2092         return ret;
2093     }
2094 
2095     /* inclose will be true if called from close(), in which case we
2096      * force the call to check because this is the last chance to do so.
2097      * If not in close(), then the authentication info could arrive in a trailer
2098      * field, which we have not read yet.
2099      */
2100     private void checkResponseCredentials (boolean inClose) throws IOException {
2101         try {
2102             if (!needToCheck)
2103                 return;
2104             if ((validateProxy && currentProxyCredentials != null) &&
2105                 (currentProxyCredentials instanceof DigestAuthentication)) {
2106                 String raw = responses.findValue ("Proxy-Authentication-Info");
2107                 if (inClose || (raw != null)) {
2108                     DigestAuthentication da = (DigestAuthentication)
2109                         currentProxyCredentials;
2110                     da.checkResponse (raw, method, getRequestURI());
2111                     currentProxyCredentials = null;
2112                 }
2113             }
2114             if ((validateServer && currentServerCredentials != null) &&
2115                 (currentServerCredentials instanceof DigestAuthentication)) {
2116                 String raw = responses.findValue ("Authentication-Info");
2117                 if (inClose || (raw != null)) {
2118                     DigestAuthentication da = (DigestAuthentication)
2119                         currentServerCredentials;
2120                     da.checkResponse (raw, method, url);
2121                     currentServerCredentials = null;
2122                 }
2123             }
2124             if ((currentServerCredentials==null) && (currentProxyCredentials == null)) {
2125                 needToCheck = false;
2126             }
2127         } catch (IOException e) {
2128             disconnectInternal();
2129             connected = false;
2130             throw e;
2131         }
2132     }
2133 
2134    /* The request URI used in the request line for this request.
2135     * Also, needed for digest authentication
2136     */
2137 
2138     String requestURI = null;
2139 
2140     String getRequestURI() throws IOException {
2141         if (requestURI == null) {
2142             requestURI = http.getURLFile();
2143         }
2144         return requestURI;
2145     }
2146 
2147     /* Tells us whether to follow a redirect.  If so, it
2148      * closes the connection (break any keep-alive) and
2149      * resets the url, re-connects, and resets the request
2150      * property.
2151      */
2152     private boolean followRedirect() throws IOException {
2153         if (!getInstanceFollowRedirects()) {
2154             return false;
2155         }
2156 
2157         int stat = getResponseCode();
2158         if (stat < 300 || stat > 307 || stat == 306
2159                                 || stat == HTTP_NOT_MODIFIED) {
2160             return false;
2161         }
2162         String loc = getHeaderField("Location");
2163         if (loc == null) {
2164             /* this should be present - if not, we have no choice
2165              * but to go forward w/ the response we got
2166              */
2167             return false;
2168         }
2169         URL locUrl;
2170         try {
2171             locUrl = new URL(loc);
2172             if (!url.getProtocol().equalsIgnoreCase(locUrl.getProtocol())) {
2173                 return false;
2174             }
2175 
2176         } catch (MalformedURLException mue) {
2177           // treat loc as a relative URI to conform to popular browsers
2178           locUrl = new URL(url, loc);
2179         }
2180         disconnectInternal();
2181         if (streaming()) {
2182             throw new HttpRetryException (RETRY_MSG3, stat, loc);
2183         }
2184         if (logger.isLoggable(PlatformLogger.FINE)) {
2185             logger.fine("Redirected from " + url + " to " + locUrl);
2186         }
2187 
2188         // clear out old response headers!!!!
2189         responses = new MessageHeader();
2190         if (stat == HTTP_USE_PROXY) {
2191             /* This means we must re-request the resource through the
2192              * proxy denoted in the "Location:" field of the response.
2193              * Judging by the spec, the string in the Location header
2194              * _should_ denote a URL - let's hope for "http://my.proxy.org"
2195              * Make a new HttpClient to the proxy, using HttpClient's
2196              * Instance-specific proxy fields, but note we're still fetching
2197              * the same URL.
2198              */
2199             String proxyHost = locUrl.getHost();
2200             int proxyPort = locUrl.getPort();
2201 
2202             SecurityManager security = System.getSecurityManager();
2203             if (security != null) {
2204                 security.checkConnect(proxyHost, proxyPort);
2205             }
2206 
2207             setProxiedClient (url, proxyHost, proxyPort);
2208             requests.set(0, method + " " + getRequestURI()+" "  +
2209                              httpVersion, null);
2210             connected = true;
2211         } else {
2212             // maintain previous headers, just change the name
2213             // of the file we're getting
2214             url = locUrl;
2215             requestURI = null; // force it to be recalculated
2216             if (method.equals("POST") && !Boolean.getBoolean("http.strictPostRedirect") && (stat!=307)) {
2217                 /* The HTTP/1.1 spec says that a redirect from a POST
2218                  * *should not* be immediately turned into a GET, and
2219                  * that some HTTP/1.0 clients incorrectly did this.
2220                  * Correct behavior redirects a POST to another POST.
2221                  * Unfortunately, since most browsers have this incorrect
2222                  * behavior, the web works this way now.  Typical usage
2223                  * seems to be:
2224                  *   POST a login code or passwd to a web page.
2225                  *   after validation, the server redirects to another
2226                  *     (welcome) page
2227                  *   The second request is (erroneously) expected to be GET
2228                  *
2229                  * We will do the incorrect thing (POST-->GET) by default.
2230                  * We will provide the capability to do the "right" thing
2231                  * (POST-->POST) by a system property, "http.strictPostRedirect=true"
2232                  */
2233 
2234                 requests = new MessageHeader();
2235                 setRequests = false;
2236                 setRequestMethod("GET");
2237                 poster = null;
2238                 if (!checkReuseConnection())
2239                     connect();
2240             } else {
2241                 if (!checkReuseConnection())
2242                     connect();
2243                 /* Even after a connect() call, http variable still can be
2244                  * null, if a ResponseCache has been installed and it returns
2245                  * a non-null CacheResponse instance. So check nullity before using it.
2246                  *
2247                  * And further, if http is null, there's no need to do anything
2248                  * about request headers because successive http session will use
2249                  * cachedInputStream/cachedHeaders anyway, which is returned by
2250                  * CacheResponse.
2251                  */
2252                 if (http != null) {
2253                     requests.set(0, method + " " + getRequestURI()+" "  +
2254                                  httpVersion, null);
2255                     int port = url.getPort();
2256                     String host = url.getHost();
2257                     if (port != -1 && port != url.getDefaultPort()) {
2258                         host += ":" + String.valueOf(port);
2259                     }
2260                     requests.set("Host", host);
2261                 }
2262             }
2263         }
2264         return true;
2265     }
2266 
2267     /* dummy byte buffer for reading off socket prior to closing */
2268     byte[] cdata = new byte [128];
2269 
2270     /**
2271      * Reset (without disconnecting the TCP conn) in order to do another transaction with this instance
2272      */
2273     private void reset() throws IOException {
2274         http.reuse = true;
2275         /* must save before calling close */
2276         reuseClient = http;
2277         InputStream is = http.getInputStream();
2278         if (!method.equals("HEAD")) {
2279             try {
2280                 /* we want to read the rest of the response without using the
2281                  * hurry mechanism, because that would close the connection
2282                  * if everything is not available immediately
2283                  */
2284                 if ((is instanceof ChunkedInputStream) ||
2285                     (is instanceof MeteredStream)) {
2286                     /* reading until eof will not block */
2287                     while (is.read (cdata) > 0) {}
2288                 } else {
2289                     /* raw stream, which will block on read, so only read
2290                      * the expected number of bytes, probably 0
2291                      */
2292                     long cl = 0;
2293                     int n = 0;
2294                     String cls = responses.findValue ("Content-Length");
2295                     if (cls != null) {
2296                         try {
2297                             cl = Long.parseLong (cls);
2298                         } catch (NumberFormatException e) {
2299                             cl = 0;
2300                         }
2301                     }
2302                     for (long i=0; i<cl; ) {
2303                         if ((n = is.read (cdata)) == -1) {
2304                             break;
2305                         } else {
2306                             i+= n;
2307                         }
2308                     }
2309                 }
2310             } catch (IOException e) {
2311                 http.reuse = false;
2312                 reuseClient = null;
2313                 disconnectInternal();
2314                 return;
2315             }
2316             try {
2317                 if (is instanceof MeteredStream) {
2318                     is.close();
2319                 }
2320             } catch (IOException e) { }
2321         }
2322         responseCode = -1;
2323         responses = new MessageHeader();
2324         connected = false;
2325     }
2326 
2327     /**
2328      * Disconnect from the web server at the first 401 error. Do not
2329      * disconnect when using a proxy, a good proxy should have already
2330      * closed the connection to the web server.
2331      */
2332     private void disconnectWeb() throws IOException {
2333         if (usingProxy()) {
2334             responseCode = -1;
2335             // clean up, particularly, skip the content part
2336             // of a 401 error response
2337             reset();
2338         } else {
2339             disconnectInternal();
2340         }
2341     }
2342 
2343     /**
2344      * Disconnect from the server (for internal use)
2345      */
2346     private void disconnectInternal() {
2347         responseCode = -1;
2348         inputStream = null;
2349         if (pi != null) {
2350             pi.finishTracking();
2351             pi = null;
2352         }
2353         if (http != null) {
2354             http.closeServer();
2355             http = null;
2356             connected = false;
2357         }
2358     }
2359 
2360     /**
2361      * Disconnect from the server (public API)
2362      */
2363     public void disconnect() {
2364 
2365         responseCode = -1;
2366         if (pi != null) {
2367             pi.finishTracking();
2368             pi = null;
2369         }
2370 
2371         if (http != null) {
2372             /*
2373              * If we have an input stream this means we received a response
2374              * from the server. That stream may have been read to EOF and
2375              * dependening on the stream type may already be closed or the
2376              * the http client may be returned to the keep-alive cache.
2377              * If the http client has been returned to the keep-alive cache
2378              * it may be closed (idle timeout) or may be allocated to
2379              * another request.
2380              *
2381              * In other to avoid timing issues we close the input stream
2382              * which will either close the underlying connection or return
2383              * the client to the cache. If there's a possibility that the
2384              * client has been returned to the cache (ie: stream is a keep
2385              * alive stream or a chunked input stream) then we remove an
2386              * idle connection to the server. Note that this approach
2387              * can be considered an approximation in that we may close a
2388              * different idle connection to that used by the request.
2389              * Additionally it's possible that we close two connections
2390              * - the first becuase it wasn't an EOF (and couldn't be
2391              * hurried) - the second, another idle connection to the
2392              * same server. The is okay because "disconnect" is an
2393              * indication that the application doesn't intend to access
2394              * this http server for a while.
2395              */
2396 
2397             if (inputStream != null) {
2398                 HttpClient hc = http;
2399 
2400                 // un-synchronized
2401                 boolean ka = hc.isKeepingAlive();
2402 
2403                 try {
2404                     inputStream.close();
2405                 } catch (IOException ioe) { }
2406 
2407                 // if the connection is persistent it may have been closed
2408                 // or returned to the keep-alive cache. If it's been returned
2409                 // to the keep-alive cache then we would like to close it
2410                 // but it may have been allocated
2411 
2412                 if (ka) {
2413                     hc.closeIdleConnection();
2414                 }
2415 
2416 
2417             } else {
2418                 // We are deliberatly being disconnected so HttpClient
2419                 // should not try to resend the request no matter what stage
2420                 // of the connection we are in.
2421                 http.setDoNotRetry(true);
2422 
2423                 http.closeServer();
2424             }
2425 
2426             //      poster = null;
2427             http = null;
2428             connected = false;
2429         }
2430         cachedInputStream = null;
2431         if (cachedHeaders != null) {
2432             cachedHeaders.reset();
2433         }
2434     }
2435 
2436     public boolean usingProxy() {
2437         if (http != null) {
2438             return (http.getProxyHostUsed() != null);
2439         }
2440         return false;
2441     }
2442 
2443     /**
2444      * Gets a header field by name. Returns null if not known.
2445      * @param name the name of the header field
2446      */
2447     @Override
2448     public String getHeaderField(String name) {
2449         try {
2450             getInputStream();
2451         } catch (IOException e) {}
2452 
2453         if (cachedHeaders != null) {
2454             return cachedHeaders.findValue(name);
2455         }
2456 
2457         return responses.findValue(name);
2458     }
2459 
2460     /**
2461      * Returns an unmodifiable Map of the header fields.
2462      * The Map keys are Strings that represent the
2463      * response-header field names. Each Map value is an
2464      * unmodifiable List of Strings that represents
2465      * the corresponding field values.
2466      *
2467      * @return a Map of header fields
2468      * @since 1.4
2469      */
2470     @Override
2471     public Map<String, List<String>> getHeaderFields() {
2472         try {
2473             getInputStream();
2474         } catch (IOException e) {}
2475 
2476         if (cachedHeaders != null) {
2477             return cachedHeaders.getHeaders();
2478         }
2479 
2480         return responses.getHeaders();
2481     }
2482 
2483     /**
2484      * Gets a header field by index. Returns null if not known.
2485      * @param n the index of the header field
2486      */
2487     @Override
2488     public String getHeaderField(int n) {
2489         try {
2490             getInputStream();
2491         } catch (IOException e) {}
2492 
2493         if (cachedHeaders != null) {
2494            return cachedHeaders.getValue(n);
2495         }
2496         return responses.getValue(n);
2497     }
2498 
2499     /**
2500      * Gets a header field by index. Returns null if not known.
2501      * @param n the index of the header field
2502      */
2503     @Override
2504     public String getHeaderFieldKey(int n) {
2505         try {
2506             getInputStream();
2507         } catch (IOException e) {}
2508 
2509         if (cachedHeaders != null) {
2510             return cachedHeaders.getKey(n);
2511         }
2512 
2513         return responses.getKey(n);
2514     }
2515 
2516     /**
2517      * Sets request property. If a property with the key already
2518      * exists, overwrite its value with the new value.
2519      * @param value the value to be set
2520      */
2521     @Override
2522     public void setRequestProperty(String key, String value) {
2523         if (connected)
2524             throw new IllegalStateException("Already connected");
2525         if (key == null)
2526             throw new NullPointerException ("key is null");
2527 
2528         checkMessageHeader(key, value);
2529         requests.set(key, value);
2530     }
2531 
2532     /**
2533      * Adds a general request property specified by a
2534      * key-value pair.  This method will not overwrite
2535      * existing values associated with the same key.
2536      *
2537      * @param   key     the keyword by which the request is known
2538      *                  (e.g., "<code>accept</code>").
2539      * @param   value  the value associated with it.
2540      * @see #getRequestProperties(java.lang.String)
2541      * @since 1.4
2542      */
2543     @Override
2544     public void addRequestProperty(String key, String value) {
2545         if (connected)
2546             throw new IllegalStateException("Already connected");
2547         if (key == null)
2548             throw new NullPointerException ("key is null");
2549 
2550         checkMessageHeader(key, value);
2551         requests.add(key, value);
2552     }
2553 
2554     //
2555     // Set a property for authentication.  This can safely disregard
2556     // the connected test.
2557     //
2558     public void setAuthenticationProperty(String key, String value) {
2559         checkMessageHeader(key, value);
2560         requests.set(key, value);
2561     }
2562 
2563     @Override
2564     public String getRequestProperty (String key) {
2565         // don't return headers containing security sensitive information
2566         if (key != null) {
2567             for (int i=0; i < EXCLUDE_HEADERS.length; i++) {
2568                 if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
2569                     return null;
2570                 }
2571             }
2572         }
2573         return requests.findValue(key);
2574     }
2575 
2576     /**
2577      * Returns an unmodifiable Map of general request
2578      * properties for this connection. The Map keys
2579      * are Strings that represent the request-header
2580      * field names. Each Map value is a unmodifiable List
2581      * of Strings that represents the corresponding
2582      * field values.
2583      *
2584      * @return  a Map of the general request properties for this connection.
2585      * @throws IllegalStateException if already connected
2586      * @since 1.4
2587      */
2588     @Override
2589     public Map<String, List<String>> getRequestProperties() {
2590         if (connected)
2591             throw new IllegalStateException("Already connected");
2592 
2593         // exclude headers containing security-sensitive info
2594         return requests.getHeaders(EXCLUDE_HEADERS);
2595     }
2596 
2597     @Override
2598     public void setConnectTimeout(int timeout) {
2599         if (timeout < 0)
2600             throw new IllegalArgumentException("timeouts can't be negative");
2601         connectTimeout = timeout;
2602     }
2603 
2604 
2605     /**
2606      * Returns setting for connect timeout.
2607      * <p>
2608      * 0 return implies that the option is disabled
2609      * (i.e., timeout of infinity).
2610      *
2611      * @return an <code>int</code> that indicates the connect timeout
2612      *         value in milliseconds
2613      * @see java.net.URLConnection#setConnectTimeout(int)
2614      * @see java.net.URLConnection#connect()
2615      * @since 1.5
2616      */
2617     @Override
2618     public int getConnectTimeout() {
2619         return (connectTimeout < 0 ? 0 : connectTimeout);
2620     }
2621 
2622     /**
2623      * Sets the read timeout to a specified timeout, in
2624      * milliseconds. A non-zero value specifies the timeout when
2625      * reading from Input stream when a connection is established to a
2626      * resource. If the timeout expires before there is data available
2627      * for read, a java.net.SocketTimeoutException is raised. A
2628      * timeout of zero is interpreted as an infinite timeout.
2629      *
2630      * <p> Some non-standard implementation of this method ignores the
2631      * specified timeout. To see the read timeout set, please call
2632      * getReadTimeout().
2633      *
2634      * @param timeout an <code>int</code> that specifies the timeout
2635      * value to be used in milliseconds
2636      * @throws IllegalArgumentException if the timeout parameter is negative
2637      *
2638      * @see java.net.URLConnectiongetReadTimeout()
2639      * @see java.io.InputStream#read()
2640      * @since 1.5
2641      */
2642     @Override
2643     public void setReadTimeout(int timeout) {
2644         if (timeout < 0)
2645             throw new IllegalArgumentException("timeouts can't be negative");
2646         readTimeout = timeout;
2647     }
2648 
2649     /**
2650      * Returns setting for read timeout. 0 return implies that the
2651      * option is disabled (i.e., timeout of infinity).
2652      *
2653      * @return an <code>int</code> that indicates the read timeout
2654      *         value in milliseconds
2655      *
2656      * @see java.net.URLConnection#setReadTimeout(int)
2657      * @see java.io.InputStream#read()
2658      * @since 1.5
2659      */
2660     @Override
2661     public int getReadTimeout() {
2662         return readTimeout < 0 ? 0 : readTimeout;
2663     }
2664 
2665     String getMethod() {
2666         return method;
2667     }
2668 
2669     private MessageHeader mapToMessageHeader(Map<String, List<String>> map) {
2670         MessageHeader headers = new MessageHeader();
2671         if (map == null || map.isEmpty()) {
2672             return headers;
2673         }
2674         for (Map.Entry<String, List<String>> entry : map.entrySet()) {
2675             String key = entry.getKey();
2676             List<String> values = entry.getValue();
2677             for (String value : values) {
2678                 if (key == null) {
2679                     headers.prepend(key, value);
2680                 } else {
2681                     headers.add(key, value);
2682                 }
2683             }
2684         }
2685         return headers;
2686     }
2687 
2688     /* The purpose of this wrapper is just to capture the close() call
2689      * so we can check authentication information that may have
2690      * arrived in a Trailer field
2691      */
2692     class HttpInputStream extends FilterInputStream {
2693         private CacheRequest cacheRequest;
2694         private OutputStream outputStream;
2695         private boolean marked = false;
2696         private int inCache = 0;
2697         private int markCount = 0;
2698 
2699         public HttpInputStream (InputStream is) {
2700             super (is);
2701             this.cacheRequest = null;
2702             this.outputStream = null;
2703         }
2704 
2705         public HttpInputStream (InputStream is, CacheRequest cacheRequest) {
2706             super (is);
2707             this.cacheRequest = cacheRequest;
2708             try {
2709                 this.outputStream = cacheRequest.getBody();
2710             } catch (IOException ioex) {
2711                 this.cacheRequest.abort();
2712                 this.cacheRequest = null;
2713                 this.outputStream = null;
2714             }
2715         }
2716 
2717         /**
2718          * Marks the current position in this input stream. A subsequent
2719          * call to the <code>reset</code> method repositions this stream at
2720          * the last marked position so that subsequent reads re-read the same
2721          * bytes.
2722          * <p>
2723          * The <code>readlimit</code> argument tells this input stream to
2724          * allow that many bytes to be read before the mark position gets
2725          * invalidated.
2726          * <p>
2727          * This method simply performs <code>in.mark(readlimit)</code>.
2728          *
2729          * @param   readlimit   the maximum limit of bytes that can be read before
2730          *                      the mark position becomes invalid.
2731          * @see     java.io.FilterInputStream#in
2732          * @see     java.io.FilterInputStream#reset()
2733          */
2734         @Override
2735         public synchronized void mark(int readlimit) {
2736             super.mark(readlimit);
2737             if (cacheRequest != null) {
2738                 marked = true;
2739                 markCount = 0;
2740             }
2741         }
2742 
2743         /**
2744          * Repositions this stream to the position at the time the
2745          * <code>mark</code> method was last called on this input stream.
2746          * <p>
2747          * This method
2748          * simply performs <code>in.reset()</code>.
2749          * <p>
2750          * Stream marks are intended to be used in
2751          * situations where you need to read ahead a little to see what's in
2752          * the stream. Often this is most easily done by invoking some
2753          * general parser. If the stream is of the type handled by the
2754          * parse, it just chugs along happily. If the stream is not of
2755          * that type, the parser should toss an exception when it fails.
2756          * If this happens within readlimit bytes, it allows the outer
2757          * code to reset the stream and try another parser.
2758          *
2759          * @exception  IOException  if the stream has not been marked or if the
2760          *               mark has been invalidated.
2761          * @see        java.io.FilterInputStream#in
2762          * @see        java.io.FilterInputStream#mark(int)
2763          */
2764         @Override
2765         public synchronized void reset() throws IOException {
2766             super.reset();
2767             if (cacheRequest != null) {
2768                 marked = false;
2769                 inCache += markCount;
2770             }
2771         }
2772 
2773         @Override
2774         public int read() throws IOException {
2775             try {
2776                 byte[] b = new byte[1];
2777                 int ret = read(b);
2778                 return (ret == -1? ret : (b[0] & 0x00FF));
2779             } catch (IOException ioex) {
2780                 if (cacheRequest != null) {
2781                     cacheRequest.abort();
2782                 }
2783                 throw ioex;
2784             }
2785         }
2786 
2787         @Override
2788         public int read(byte[] b) throws IOException {
2789             return read(b, 0, b.length);
2790         }
2791 
2792         @Override
2793         public int read(byte[] b, int off, int len) throws IOException {
2794             try {
2795                 int newLen = super.read(b, off, len);
2796                 int nWrite;
2797                 // write to cache
2798                 if (inCache > 0) {
2799                     if (inCache >= newLen) {
2800                         inCache -= newLen;
2801                         nWrite = 0;
2802                     } else {
2803                         nWrite = newLen - inCache;
2804                         inCache = 0;
2805                     }
2806                 } else {
2807                     nWrite = newLen;
2808                 }
2809                 if (nWrite > 0 && outputStream != null)
2810                     outputStream.write(b, off + (newLen-nWrite), nWrite);
2811                 if (marked) {
2812                     markCount += newLen;
2813                 }
2814                 return newLen;
2815             } catch (IOException ioex) {
2816                 if (cacheRequest != null) {
2817                     cacheRequest.abort();
2818                 }
2819                 throw ioex;
2820             }
2821         }
2822 
2823         @Override
2824         public void close () throws IOException {
2825             try {
2826                 if (outputStream != null) {
2827                     if (read() != -1) {
2828                         cacheRequest.abort();
2829                     } else {
2830                         outputStream.close();
2831                     }
2832                 }
2833                 super.close ();
2834             } catch (IOException ioex) {
2835                 if (cacheRequest != null) {
2836                     cacheRequest.abort();
2837                 }
2838                 throw ioex;
2839             } finally {
2840                 HttpURLConnection.this.http = null;
2841                 checkResponseCredentials (true);
2842             }
2843         }
2844     }
2845 
2846     class StreamingOutputStream extends FilterOutputStream {
2847 
2848         long expected;
2849         long written;
2850         boolean closed;
2851         boolean error;
2852         IOException errorExcp;
2853 
2854         /**
2855          * expectedLength == -1 if the stream is chunked
2856          * expectedLength > 0 if the stream is fixed content-length
2857          *    In the 2nd case, we make sure the expected number of
2858          *    of bytes are actually written
2859          */
2860         StreamingOutputStream (OutputStream os, long expectedLength) {
2861             super (os);
2862             expected = expectedLength;
2863             written = 0L;
2864             closed = false;
2865             error = false;
2866         }
2867 
2868         @Override
2869         public void write (int b) throws IOException {
2870             checkError();
2871             written ++;
2872             if (expected != -1L && written > expected) {
2873                 throw new IOException ("too many bytes written");
2874             }
2875             out.write (b);
2876         }
2877 
2878         @Override
2879         public void write (byte[] b) throws IOException {
2880             write (b, 0, b.length);
2881         }
2882 
2883         @Override
2884         public void write (byte[] b, int off, int len) throws IOException {
2885             checkError();
2886             written += len;
2887             if (expected != -1L && written > expected) {
2888                 out.close ();
2889                 throw new IOException ("too many bytes written");
2890             }
2891             out.write (b, off, len);
2892         }
2893 
2894         void checkError () throws IOException {
2895             if (closed) {
2896                 throw new IOException ("Stream is closed");
2897             }
2898             if (error) {
2899                 throw errorExcp;
2900             }
2901             if (((PrintStream)out).checkError()) {
2902                 throw new IOException("Error writing request body to server");
2903             }
2904         }
2905 
2906         /* this is called to check that all the bytes
2907          * that were supposed to be written were written
2908          * and that the stream is now closed().
2909          */
2910         boolean writtenOK () {
2911             return closed && ! error;
2912         }
2913 
2914         @Override
2915         public void close () throws IOException {
2916             if (closed) {
2917                 return;
2918             }
2919             closed = true;
2920             if (expected != -1L) {
2921                 /* not chunked */
2922                 if (written != expected) {
2923                     error = true;
2924                     errorExcp = new IOException ("insufficient data written");
2925                     out.close ();
2926                     throw errorExcp;
2927                 }
2928                 super.flush(); /* can't close the socket */
2929             } else {
2930                 /* chunked */
2931                 super.close (); /* force final chunk to be written */
2932                 /* trailing \r\n */
2933                 OutputStream o = http.getOutputStream();
2934                 o.write ('\r');
2935                 o.write ('\n');
2936                 o.flush();
2937             }
2938         }
2939     }
2940 
2941 
2942     static class ErrorStream extends InputStream {
2943         ByteBuffer buffer;
2944         InputStream is;
2945 
2946         private ErrorStream(ByteBuffer buf) {
2947             buffer = buf;
2948             is = null;
2949         }
2950 
2951         private ErrorStream(ByteBuffer buf, InputStream is) {
2952             buffer = buf;
2953             this.is = is;
2954         }
2955 
2956         // when this method is called, it's either the case that cl > 0, or
2957         // if chunk-encoded, cl = -1; in other words, cl can't be 0
2958         public static InputStream getErrorStream(InputStream is, long cl, HttpClient http) {
2959 
2960             // cl can't be 0; this following is here for extra precaution
2961             if (cl == 0) {
2962                 return null;
2963             }
2964 
2965             try {
2966                 // set SO_TIMEOUT to 1/5th of the total timeout
2967                 // remember the old timeout value so that we can restore it
2968                 int oldTimeout = http.getReadTimeout();
2969                 http.setReadTimeout(timeout4ESBuffer/5);
2970 
2971                 long expected = 0;
2972                 boolean isChunked = false;
2973                 // the chunked case
2974                 if (cl < 0) {
2975                     expected = bufSize4ES;
2976                     isChunked = true;
2977                 } else {
2978                     expected = cl;
2979                 }
2980                 if (expected <= bufSize4ES) {
2981                     int exp = (int) expected;
2982                     byte[] buffer = new byte[exp];
2983                     int count = 0, time = 0, len = 0;
2984                     do {
2985                         try {
2986                             len = is.read(buffer, count,
2987                                              buffer.length - count);
2988                             if (len < 0) {
2989                                 if (isChunked) {
2990                                     // chunked ended
2991                                     // if chunked ended prematurely,
2992                                     // an IOException would be thrown
2993                                     break;
2994                                 }
2995                                 // the server sends less than cl bytes of data
2996                                 throw new IOException("the server closes"+
2997                                                       " before sending "+cl+
2998                                                       " bytes of data");
2999                             }
3000                             count += len;
3001                         } catch (SocketTimeoutException ex) {
3002                             time += timeout4ESBuffer/5;
3003                         }
3004                     } while (count < exp && time < timeout4ESBuffer);
3005 
3006                     // reset SO_TIMEOUT to old value
3007                     http.setReadTimeout(oldTimeout);
3008 
3009                     // if count < cl at this point, we will not try to reuse
3010                     // the connection
3011                     if (count == 0) {
3012                         // since we haven't read anything,
3013                         // we will return the underlying
3014                         // inputstream back to the application
3015                         return null;
3016                     }  else if ((count == expected && !(isChunked)) || (isChunked && len <0)) {
3017                         // put the connection into keep-alive cache
3018                         // the inputstream will try to do the right thing
3019                         is.close();
3020                         return new ErrorStream(ByteBuffer.wrap(buffer, 0, count));
3021                     } else {
3022                         // we read part of the response body
3023                         return new ErrorStream(
3024                                       ByteBuffer.wrap(buffer, 0, count), is);
3025                     }
3026                 }
3027                 return null;
3028             } catch (IOException ioex) {
3029                 // ioex.printStackTrace();
3030                 return null;
3031             }
3032         }
3033 
3034         @Override
3035         public int available() throws IOException {
3036             if (is == null) {
3037                 return buffer.remaining();
3038             } else {
3039                 return buffer.remaining()+is.available();
3040             }
3041         }
3042 
3043         public int read() throws IOException {
3044             byte[] b = new byte[1];
3045             int ret = read(b);
3046             return (ret == -1? ret : (b[0] & 0x00FF));
3047         }
3048 
3049         @Override
3050         public int read(byte[] b) throws IOException {
3051             return read(b, 0, b.length);
3052         }
3053 
3054         @Override
3055         public int read(byte[] b, int off, int len) throws IOException {
3056             int rem = buffer.remaining();
3057             if (rem > 0) {
3058                 int ret = rem < len? rem : len;
3059                 buffer.get(b, off, ret);
3060                 return ret;
3061             } else {
3062                 if (is == null) {
3063                     return -1;
3064                 } else {
3065                     return is.read(b, off, len);
3066                 }
3067             }
3068         }
3069 
3070         @Override
3071         public void close() throws IOException {
3072             buffer = null;
3073             if (is != null) {
3074                 is.close();
3075             }
3076         }
3077     }
3078 }
3079 
3080 /** An input stream that just returns EOF.  This is for
3081  * HTTP URLConnections that are KeepAlive && use the
3082  * HEAD method - i.e., stream not dead, but nothing to be read.
3083  */
3084 
3085 class EmptyInputStream extends InputStream {
3086 
3087     @Override
3088     public int available() {
3089         return 0;
3090     }
3091 
3092     public int read() {
3093         return -1;
3094     }
3095 }
--- EOF ---