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

Save This Page
Home » openjdk-7 » sun.net.www.protocol » http » [javadoc | source]