Save This Page
Home » openjdk-7 » sun » net » www » http » [javadoc | source]
    1   /*
    2    * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package sun.net.www.http;
   27   
   28   import java.io;
   29   import java.net;
   30   import java.util.Locale;
   31   import sun.net.NetworkClient;
   32   import sun.net.ProgressSource;
   33   import sun.net.www.MessageHeader;
   34   import sun.net.www.HeaderParser;
   35   import sun.net.www.MeteredStream;
   36   import sun.net.www.ParseUtil;
   37   import sun.net.www.protocol.http.HttpURLConnection;
   38   import sun.util.logging.PlatformLogger;
   39   
   40   /**
   41    * @author Herb Jellinek
   42    * @author Dave Brown
   43    */
   44   public class HttpClient extends NetworkClient {
   45       // whether this httpclient comes from the cache
   46       protected boolean cachedHttpClient = false;
   47   
   48       private boolean inCache;
   49   
   50       protected CookieHandler cookieHandler;
   51   
   52       // Http requests we send
   53       MessageHeader requests;
   54   
   55       // Http data we send with the headers
   56       PosterOutputStream poster = null;
   57   
   58       // true if we are in streaming mode (fixed length or chunked)
   59       boolean streaming;
   60   
   61       // if we've had one io error
   62       boolean failedOnce = false;
   63   
   64       /** Response code for CONTINUE */
   65       private boolean ignoreContinue = true;
   66       private static final int    HTTP_CONTINUE = 100;
   67   
   68       /** Default port number for http daemons. REMIND: make these private */
   69       static final int    httpPortNumber = 80;
   70   
   71       /** return default port number (subclasses may override) */
   72       protected int getDefaultPort () { return httpPortNumber; }
   73   
   74       static private int getDefaultPort(String proto) {
   75           if ("http".equalsIgnoreCase(proto))
   76               return 80;
   77           if ("https".equalsIgnoreCase(proto))
   78               return 443;
   79           return -1;
   80       }
   81   
   82       /* All proxying (generic as well as instance-specific) may be
   83        * disabled through use of this flag
   84        */
   85       protected boolean proxyDisabled;
   86   
   87       // are we using proxy in this instance?
   88       public boolean usingProxy = false;
   89       // target host, port for the URL
   90       protected String host;
   91       protected int port;
   92   
   93       /* where we cache currently open, persistent connections */
   94       protected static KeepAliveCache kac = new KeepAliveCache();
   95   
   96       private static boolean keepAliveProp = true;
   97   
   98       // retryPostProp is true by default so as to preserve behavior
   99       // from previous releases.
  100       private static boolean retryPostProp = true;
  101   
  102       volatile boolean keepingAlive = false;     /* this is a keep-alive connection */
  103       int keepAliveConnections = -1;    /* number of keep-alives left */
  104   
  105       /**Idle timeout value, in milliseconds. Zero means infinity,
  106        * iff keepingAlive=true.
  107        * Unfortunately, we can't always believe this one.  If I'm connected
  108        * through a Netscape proxy to a server that sent me a keep-alive
  109        * time of 15 sec, the proxy unilaterally terminates my connection
  110        * after 5 sec.  So we have to hard code our effective timeout to
  111        * 4 sec for the case where we're using a proxy. *SIGH*
  112        */
  113       int keepAliveTimeout = 0;
  114   
  115       /** whether the response is to be cached */
  116       private CacheRequest cacheRequest = null;
  117   
  118       /** Url being fetched. */
  119       protected URL       url;
  120   
  121       /* if set, the client will be reused and must not be put in cache */
  122       public boolean reuse = false;
  123   
  124       // Traffic capture tool, if configured. See HttpCapture class for info
  125        private HttpCapture capture = null;
  126   
  127       /**
  128        * A NOP method kept for backwards binary compatibility
  129        * @deprecated -- system properties are no longer cached.
  130        */
  131       @Deprecated
  132       public static synchronized void resetProperties() {
  133       }
  134   
  135       int getKeepAliveTimeout() {
  136           return keepAliveTimeout;
  137       }
  138   
  139       static {
  140           String keepAlive = java.security.AccessController.doPrivileged(
  141               new sun.security.action.GetPropertyAction("http.keepAlive"));
  142   
  143           String retryPost = java.security.AccessController.doPrivileged(
  144               new sun.security.action.GetPropertyAction("sun.net.http.retryPost"));
  145   
  146           if (keepAlive != null) {
  147               keepAliveProp = Boolean.valueOf(keepAlive).booleanValue();
  148           } else {
  149               keepAliveProp = true;
  150           }
  151   
  152           if (retryPost != null) {
  153               retryPostProp = Boolean.valueOf(retryPost).booleanValue();
  154           } else
  155               retryPostProp = true;
  156   
  157       }
  158   
  159       /**
  160        * @return true iff http keep alive is set (i.e. enabled).  Defaults
  161        *          to true if the system property http.keepAlive isn't set.
  162        */
  163       public boolean getHttpKeepAliveSet() {
  164           return keepAliveProp;
  165       }
  166   
  167   
  168       protected HttpClient() {
  169       }
  170   
  171       private HttpClient(URL url)
  172       throws IOException {
  173           this(url, (String)null, -1, false);
  174       }
  175   
  176       protected HttpClient(URL url,
  177                            boolean proxyDisabled) throws IOException {
  178           this(url, null, -1, proxyDisabled);
  179       }
  180   
  181       /* This package-only CTOR should only be used for FTP piggy-backed on HTTP
  182        * HTTP URL's that use this won't take advantage of keep-alive.
  183        * Additionally, this constructor may be used as a last resort when the
  184        * first HttpClient gotten through New() failed (probably b/c of a
  185        * Keep-Alive mismatch).
  186        *
  187        * XXX That documentation is wrong ... it's not package-private any more
  188        */
  189       public HttpClient(URL url, String proxyHost, int proxyPort)
  190       throws IOException {
  191           this(url, proxyHost, proxyPort, false);
  192       }
  193   
  194       protected HttpClient(URL url, Proxy p, int to) throws IOException {
  195           proxy = (p == null) ? Proxy.NO_PROXY : p;
  196           this.host = url.getHost();
  197           this.url = url;
  198           port = url.getPort();
  199           if (port == -1) {
  200               port = getDefaultPort();
  201           }
  202           setConnectTimeout(to);
  203   
  204           // get the cookieHandler if there is any
  205           cookieHandler = java.security.AccessController.doPrivileged(
  206               new java.security.PrivilegedAction<CookieHandler>() {
  207                   public CookieHandler run() {
  208                       return CookieHandler.getDefault();
  209                   }
  210               });
  211   
  212           capture = HttpCapture.getCapture(url);
  213           openServer();
  214       }
  215   
  216       static protected Proxy newHttpProxy(String proxyHost, int proxyPort,
  217                                         String proto) {
  218           if (proxyHost == null || proto == null)
  219               return Proxy.NO_PROXY;
  220           int pport = proxyPort < 0 ? getDefaultPort(proto) : proxyPort;
  221           InetSocketAddress saddr = InetSocketAddress.createUnresolved(proxyHost, pport);
  222           return new Proxy(Proxy.Type.HTTP, saddr);
  223       }
  224   
  225       /*
  226        * This constructor gives "ultimate" flexibility, including the ability
  227        * to bypass implicit proxying.  Sometimes we need to be using tunneling
  228        * (transport or network level) instead of proxying (application level),
  229        * for example when we don't want the application level data to become
  230        * visible to third parties.
  231        *
  232        * @param url               the URL to which we're connecting
  233        * @param proxy             proxy to use for this URL (e.g. forwarding)
  234        * @param proxyPort         proxy port to use for this URL
  235        * @param proxyDisabled     true to disable default proxying
  236        */
  237       private HttpClient(URL url, String proxyHost, int proxyPort,
  238                          boolean proxyDisabled)
  239           throws IOException {
  240           this(url, proxyDisabled ? Proxy.NO_PROXY :
  241                newHttpProxy(proxyHost, proxyPort, "http"), -1);
  242       }
  243   
  244       public HttpClient(URL url, String proxyHost, int proxyPort,
  245                          boolean proxyDisabled, int to)
  246           throws IOException {
  247           this(url, proxyDisabled ? Proxy.NO_PROXY :
  248                newHttpProxy(proxyHost, proxyPort, "http"), to);
  249       }
  250   
  251       /* This class has no public constructor for HTTP.  This method is used to
  252        * get an HttpClient to the specifed URL.  If there's currently an
  253        * active HttpClient to that server/port, you'll get that one.
  254        */
  255       public static HttpClient New(URL url)
  256       throws IOException {
  257           return HttpClient.New(url, Proxy.NO_PROXY, -1, true);
  258       }
  259   
  260       public static HttpClient New(URL url, boolean useCache)
  261           throws IOException {
  262           return HttpClient.New(url, Proxy.NO_PROXY, -1, useCache);
  263       }
  264   
  265       public static HttpClient New(URL url, Proxy p, int to, boolean useCache)
  266           throws IOException {
  267           if (p == null) {
  268               p = Proxy.NO_PROXY;
  269           }
  270           HttpClient ret = null;
  271           /* see if one's already around */
  272           if (useCache) {
  273               ret = kac.get(url, null);
  274               if (ret != null) {
  275                   if ((ret.proxy != null && ret.proxy.equals(p)) ||
  276                       (ret.proxy == null && p == null)) {
  277                       synchronized (ret) {
  278                           ret.cachedHttpClient = true;
  279                           assert ret.inCache;
  280                           ret.inCache = false;
  281                           PlatformLogger logger = HttpURLConnection.getHttpLogger();
  282                           if (logger.isLoggable(PlatformLogger.FINEST)) {
  283                               logger.finest("KeepAlive stream retrieved from the cache, " + ret);
  284                           }
  285                       }
  286                   } else {
  287                       // We cannot return this connection to the cache as it's
  288                       // KeepAliveTimeout will get reset. We simply close the connection.
  289                       // This should be fine as it is very rare that a connection
  290                       // to the same host will not use the same proxy.
  291                       synchronized(ret) {
  292                           ret.inCache = false;
  293                           ret.closeServer();
  294                       }
  295                       ret = null;
  296                   }
  297               }
  298           }
  299           if (ret == null) {
  300               ret = new HttpClient(url, p, to);
  301           } else {
  302               SecurityManager security = System.getSecurityManager();
  303               if (security != null) {
  304                   if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {
  305                       security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());
  306                   } else {
  307                       security.checkConnect(url.getHost(), url.getPort());
  308                   }
  309               }
  310               ret.url = url;
  311           }
  312           return ret;
  313       }
  314   
  315       public static HttpClient New(URL url, Proxy p, int to) throws IOException {
  316           return New(url, p, to, true);
  317       }
  318   
  319       public static HttpClient New(URL url, String proxyHost, int proxyPort,
  320                                    boolean useCache)
  321           throws IOException {
  322           return New(url, newHttpProxy(proxyHost, proxyPort, "http"), -1, useCache);
  323       }
  324   
  325       public static HttpClient New(URL url, String proxyHost, int proxyPort,
  326                                    boolean useCache, int to)
  327           throws IOException {
  328           return New(url, newHttpProxy(proxyHost, proxyPort, "http"), to, useCache);
  329       }
  330   
  331       /* return it to the cache as still usable, if:
  332        * 1) It's keeping alive, AND
  333        * 2) It still has some connections left, AND
  334        * 3) It hasn't had a error (PrintStream.checkError())
  335        * 4) It hasn't timed out
  336        *
  337        * If this client is not keepingAlive, it should have been
  338        * removed from the cache in the parseHeaders() method.
  339        */
  340   
  341       public void finished() {
  342           if (reuse) /* will be reused */
  343               return;
  344           keepAliveConnections--;
  345           poster = null;
  346           if (keepAliveConnections > 0 && isKeepingAlive() &&
  347                  !(serverOutput.checkError())) {
  348               /* This connection is keepingAlive && still valid.
  349                * Return it to the cache.
  350                */
  351               putInKeepAliveCache();
  352           } else {
  353               closeServer();
  354           }
  355       }
  356   
  357       protected synchronized void putInKeepAliveCache() {
  358           if (inCache) {
  359               assert false : "Duplicate put to keep alive cache";
  360               return;
  361           }
  362           inCache = true;
  363           kac.put(url, null, this);
  364       }
  365   
  366       protected synchronized boolean isInKeepAliveCache() {
  367           return inCache;
  368       }
  369   
  370       /*
  371        * Close an idle connection to this URL (if it exists in the
  372        * cache).
  373        */
  374       public void closeIdleConnection() {
  375           HttpClient http = kac.get(url, null);
  376           if (http != null) {
  377               http.closeServer();
  378           }
  379       }
  380   
  381       /* We're very particular here about what our InputStream to the server
  382        * looks like for reasons that are apparent if you can decipher the
  383        * method parseHTTP().  That's why this method is overidden from the
  384        * superclass.
  385        */
  386       @Override
  387       public void openServer(String server, int port) throws IOException {
  388           serverSocket = doConnect(server, port);
  389           try {
  390               OutputStream out = serverSocket.getOutputStream();
  391               if (capture != null) {
  392                   out = new HttpCaptureOutputStream(out, capture);
  393               }
  394               serverOutput = new PrintStream(
  395                   new BufferedOutputStream(out),
  396                                            false, encoding);
  397           } catch (UnsupportedEncodingException e) {
  398               throw new InternalError(encoding+" encoding not found");
  399           }
  400           serverSocket.setTcpNoDelay(true);
  401       }
  402   
  403       /*
  404        * Returns true if the http request should be tunneled through proxy.
  405        * An example where this is the case is Https.
  406        */
  407       public boolean needsTunneling() {
  408           return false;
  409       }
  410   
  411       /*
  412        * Returns true if this httpclient is from cache
  413        */
  414       public synchronized boolean isCachedConnection() {
  415           return cachedHttpClient;
  416       }
  417   
  418       /*
  419        * Finish any work left after the socket connection is
  420        * established.  In the normal http case, it's a NO-OP. Subclass
  421        * may need to override this. An example is Https, where for
  422        * direct connection to the origin server, ssl handshake needs to
  423        * be done; for proxy tunneling, the socket needs to be converted
  424        * into an SSL socket before ssl handshake can take place.
  425        */
  426       public void afterConnect() throws IOException, UnknownHostException {
  427           // NO-OP. Needs to be overwritten by HttpsClient
  428       }
  429   
  430       /*
  431        * call openServer in a privileged block
  432        */
  433       private synchronized void privilegedOpenServer(final InetSocketAddress server)
  434            throws IOException
  435       {
  436           try {
  437               java.security.AccessController.doPrivileged(
  438                   new java.security.PrivilegedExceptionAction<Void>() {
  439                       public Void run() throws IOException {
  440                       openServer(server.getHostString(), server.getPort());
  441                       return null;
  442                   }
  443               });
  444           } catch (java.security.PrivilegedActionException pae) {
  445               throw (IOException) pae.getException();
  446           }
  447       }
  448   
  449       /*
  450        * call super.openServer
  451        */
  452       private void superOpenServer(final String proxyHost,
  453                                    final int proxyPort)
  454           throws IOException, UnknownHostException
  455       {
  456           super.openServer(proxyHost, proxyPort);
  457       }
  458   
  459       /*
  460        */
  461       protected synchronized void openServer() throws IOException {
  462   
  463           SecurityManager security = System.getSecurityManager();
  464   
  465           if (security != null) {
  466               security.checkConnect(host, port);
  467           }
  468   
  469           if (keepingAlive) { // already opened
  470               return;
  471           }
  472   
  473           if (url.getProtocol().equals("http") ||
  474               url.getProtocol().equals("https") ) {
  475   
  476               if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
  477                   sun.net.www.URLConnection.setProxiedHost(host);
  478                   privilegedOpenServer((InetSocketAddress) proxy.address());
  479                   usingProxy = true;
  480                   return;
  481               } else {
  482                   // make direct connection
  483                   openServer(host, port);
  484                   usingProxy = false;
  485                   return;
  486               }
  487   
  488           } else {
  489               /* we're opening some other kind of url, most likely an
  490                * ftp url.
  491                */
  492               if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {
  493                   sun.net.www.URLConnection.setProxiedHost(host);
  494                   privilegedOpenServer((InetSocketAddress) proxy.address());
  495                   usingProxy = true;
  496                   return;
  497               } else {
  498                   // make direct connection
  499                   super.openServer(host, port);
  500                   usingProxy = false;
  501                   return;
  502               }
  503           }
  504       }
  505   
  506       public String getURLFile() throws IOException {
  507   
  508           String fileName = url.getFile();
  509           if ((fileName == null) || (fileName.length() == 0))
  510               fileName = "/";
  511   
  512           /**
  513            * proxyDisabled is set by subclass HttpsClient!
  514            */
  515           if (usingProxy && !proxyDisabled) {
  516               // Do not use URLStreamHandler.toExternalForm as the fragment
  517               // should not be part of the RequestURI. It should be an
  518               // absolute URI which does not have a fragment part.
  519               StringBuffer result = new StringBuffer(128);
  520               result.append(url.getProtocol());
  521               result.append(":");
  522               if (url.getAuthority() != null && url.getAuthority().length() > 0) {
  523                   result.append("//");
  524                   result.append(url.getAuthority());
  525               }
  526               if (url.getPath() != null) {
  527                   result.append(url.getPath());
  528               }
  529               if (url.getQuery() != null) {
  530                   result.append('?');
  531                   result.append(url.getQuery());
  532               }
  533   
  534               fileName =  result.toString();
  535           }
  536           if (fileName.indexOf('\n') == -1)
  537               return fileName;
  538           else
  539               throw new java.net.MalformedURLException("Illegal character in URL");
  540       }
  541   
  542       /**
  543        * @deprecated
  544        */
  545       @Deprecated
  546       public void writeRequests(MessageHeader head) {
  547           requests = head;
  548           requests.print(serverOutput);
  549           serverOutput.flush();
  550       }
  551   
  552       public void writeRequests(MessageHeader head,
  553                                 PosterOutputStream pos) throws IOException {
  554           requests = head;
  555           requests.print(serverOutput);
  556           poster = pos;
  557           if (poster != null)
  558               poster.writeTo(serverOutput);
  559           serverOutput.flush();
  560       }
  561   
  562       public void writeRequests(MessageHeader head,
  563                                 PosterOutputStream pos,
  564                                 boolean streaming) throws IOException {
  565           this.streaming = streaming;
  566           writeRequests(head, pos);
  567       }
  568   
  569       /** Parse the first line of the HTTP request.  It usually looks
  570           something like: "HTTP/1.0 <number> comment\r\n". */
  571   
  572       public boolean parseHTTP(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc)
  573       throws IOException {
  574           /* If "HTTP/*" is found in the beginning, return true.  Let
  575            * HttpURLConnection parse the mime header itself.
  576            *
  577            * If this isn't valid HTTP, then we don't try to parse a header
  578            * out of the beginning of the response into the responses,
  579            * and instead just queue up the output stream to it's very beginning.
  580            * This seems most reasonable, and is what the NN browser does.
  581            */
  582   
  583           try {
  584               serverInput = serverSocket.getInputStream();
  585               if (capture != null) {
  586                   serverInput = new HttpCaptureInputStream(serverInput, capture);
  587               }
  588               serverInput = new BufferedInputStream(serverInput);
  589               return (parseHTTPHeader(responses, pi, httpuc));
  590           } catch (SocketTimeoutException stex) {
  591               // We don't want to retry the request when the app. sets a timeout
  592               // but don't close the server if timeout while waiting for 100-continue
  593               if (ignoreContinue) {
  594                   closeServer();
  595               }
  596               throw stex;
  597           } catch (IOException e) {
  598               closeServer();
  599               cachedHttpClient = false;
  600               if (!failedOnce && requests != null) {
  601                   failedOnce = true;
  602                   if (httpuc.getRequestMethod().equals("POST") && (!retryPostProp || streaming)) {
  603                       // do not retry the request
  604                   }  else {
  605                       // try once more
  606                       openServer();
  607                       if (needsTunneling()) {
  608                           httpuc.doTunneling();
  609                       }
  610                       afterConnect();
  611                       writeRequests(requests, poster);
  612                       return parseHTTP(responses, pi, httpuc);
  613                   }
  614               }
  615               throw e;
  616           }
  617   
  618       }
  619   
  620       private boolean parseHTTPHeader(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc)
  621       throws IOException {
  622           /* If "HTTP/*" is found in the beginning, return true.  Let
  623            * HttpURLConnection parse the mime header itself.
  624            *
  625            * If this isn't valid HTTP, then we don't try to parse a header
  626            * out of the beginning of the response into the responses,
  627            * and instead just queue up the output stream to it's very beginning.
  628            * This seems most reasonable, and is what the NN browser does.
  629            */
  630   
  631           keepAliveConnections = -1;
  632           keepAliveTimeout = 0;
  633   
  634           boolean ret = false;
  635           byte[] b = new byte[8];
  636   
  637           try {
  638               int nread = 0;
  639               serverInput.mark(10);
  640               while (nread < 8) {
  641                   int r = serverInput.read(b, nread, 8 - nread);
  642                   if (r < 0) {
  643                       break;
  644                   }
  645                   nread += r;
  646               }
  647               String keep=null;
  648               ret = b[0] == 'H' && b[1] == 'T'
  649                       && b[2] == 'T' && b[3] == 'P' && b[4] == '/' &&
  650                   b[5] == '1' && b[6] == '.';
  651               serverInput.reset();
  652               if (ret) { // is valid HTTP - response started w/ "HTTP/1."
  653                   responses.parseHeader(serverInput);
  654   
  655                   // we've finished parsing http headers
  656                   // check if there are any applicable cookies to set (in cache)
  657                   if (cookieHandler != null) {
  658                       URI uri = ParseUtil.toURI(url);
  659                       // NOTE: That cast from Map shouldn't be necessary but
  660                       // a bug in javac is triggered under certain circumstances
  661                       // So we do put the cast in as a workaround until
  662                       // it is resolved.
  663                       if (uri != null)
  664                           cookieHandler.put(uri, responses.getHeaders());
  665                   }
  666   
  667                   /* decide if we're keeping alive:
  668                    * This is a bit tricky.  There's a spec, but most current
  669                    * servers (10/1/96) that support this differ in dialects.
  670                    * If the server/client misunderstand each other, the
  671                    * protocol should fall back onto HTTP/1.0, no keep-alive.
  672                    */
  673                   if (usingProxy) { // not likely a proxy will return this
  674                       keep = responses.findValue("Proxy-Connection");
  675                   }
  676                   if (keep == null) {
  677                       keep = responses.findValue("Connection");
  678                   }
  679                   if (keep != null && keep.toLowerCase(Locale.US).equals("keep-alive")) {
  680                       /* some servers, notably Apache1.1, send something like:
  681                        * "Keep-Alive: timeout=15, max=1" which we should respect.
  682                        */
  683                       HeaderParser p = new HeaderParser(
  684                               responses.findValue("Keep-Alive"));
  685                       if (p != null) {
  686                           /* default should be larger in case of proxy */
  687                           keepAliveConnections = p.findInt("max", usingProxy?50:5);
  688                           keepAliveTimeout = p.findInt("timeout", usingProxy?60:5);
  689                       }
  690                   } else if (b[7] != '0') {
  691                       /*
  692                        * We're talking 1.1 or later. Keep persistent until
  693                        * the server says to close.
  694                        */
  695                       if (keep != null) {
  696                           /*
  697                            * The only Connection token we understand is close.
  698                            * Paranoia: if there is any Connection header then
  699                            * treat as non-persistent.
  700                            */
  701                           keepAliveConnections = 1;
  702                       } else {
  703                           keepAliveConnections = 5;
  704                       }
  705                   }
  706               } else if (nread != 8) {
  707                   if (!failedOnce && requests != null) {
  708                       failedOnce = true;
  709                       if (httpuc.getRequestMethod().equals("POST") && (!retryPostProp || streaming)) {
  710                           // do not retry the request
  711                       } else {
  712                           closeServer();
  713                           cachedHttpClient = false;
  714                           openServer();
  715                           if (needsTunneling()) {
  716                               httpuc.doTunneling();
  717                           }
  718                           afterConnect();
  719                           writeRequests(requests, poster);
  720                           return parseHTTP(responses, pi, httpuc);
  721                       }
  722                   }
  723                   throw new SocketException("Unexpected end of file from server");
  724               } else {
  725                   // we can't vouche for what this is....
  726                   responses.set("Content-type", "unknown/unknown");
  727               }
  728           } catch (IOException e) {
  729               throw e;
  730           }
  731   
  732           int code = -1;
  733           try {
  734               String resp;
  735               resp = responses.getValue(0);
  736               /* should have no leading/trailing LWS
  737                * expedite the typical case by assuming it has
  738                * form "HTTP/1.x <WS> 2XX <mumble>"
  739                */
  740               int ind;
  741               ind = resp.indexOf(' ');
  742               while(resp.charAt(ind) == ' ')
  743                   ind++;
  744               code = Integer.parseInt(resp.substring(ind, ind + 3));
  745           } catch (Exception e) {}
  746   
  747           if (code == HTTP_CONTINUE && ignoreContinue) {
  748               responses.reset();
  749               return parseHTTPHeader(responses, pi, httpuc);
  750           }
  751   
  752           long cl = -1;
  753   
  754           /*
  755            * Set things up to parse the entity body of the reply.
  756            * We should be smarter about avoid pointless work when
  757            * the HTTP method and response code indicate there will be
  758            * no entity body to parse.
  759            */
  760           String te = responses.findValue("Transfer-Encoding");
  761           if (te != null && te.equalsIgnoreCase("chunked")) {
  762               serverInput = new ChunkedInputStream(serverInput, this, responses);
  763   
  764               /*
  765                * If keep alive not specified then close after the stream
  766                * has completed.
  767                */
  768               if (keepAliveConnections <= 1) {
  769                   keepAliveConnections = 1;
  770                   keepingAlive = false;
  771               } else {
  772                   keepingAlive = true;
  773               }
  774               failedOnce = false;
  775           } else {
  776   
  777               /*
  778                * If it's a keep alive connection then we will keep
  779                * (alive if :-
  780                * 1. content-length is specified, or
  781                * 2. "Not-Modified" or "No-Content" responses - RFC 2616 states that
  782                *    204 or 304 response must not include a message body.
  783                */
  784               String cls = responses.findValue("content-length");
  785               if (cls != null) {
  786                   try {
  787                       cl = Long.parseLong(cls);
  788                   } catch (NumberFormatException e) {
  789                       cl = -1;
  790                   }
  791               }
  792               String requestLine = requests.getKey(0);
  793   
  794               if ((requestLine != null &&
  795                    (requestLine.startsWith("HEAD"))) ||
  796                   code == HttpURLConnection.HTTP_NOT_MODIFIED ||
  797                   code == HttpURLConnection.HTTP_NO_CONTENT) {
  798                   cl = 0;
  799               }
  800   
  801               if (keepAliveConnections > 1 &&
  802                   (cl >= 0 ||
  803                    code == HttpURLConnection.HTTP_NOT_MODIFIED ||
  804                    code == HttpURLConnection.HTTP_NO_CONTENT)) {
  805                   keepingAlive = true;
  806                   failedOnce = false;
  807               } else if (keepingAlive) {
  808                   /* Previously we were keeping alive, and now we're not.  Remove
  809                    * this from the cache (but only here, once) - otherwise we get
  810                    * multiple removes and the cache count gets messed up.
  811                    */
  812                   keepingAlive=false;
  813               }
  814           }
  815   
  816           /* wrap a KeepAliveStream/MeteredStream around it if appropriate */
  817   
  818           if (cl > 0) {
  819               // In this case, content length is well known, so it is okay
  820               // to wrap the input stream with KeepAliveStream/MeteredStream.
  821   
  822               if (pi != null) {
  823                   // Progress monitor is enabled
  824                   pi.setContentType(responses.findValue("content-type"));
  825               }
  826   
  827               if (isKeepingAlive())   {
  828                   // Wrap KeepAliveStream if keep alive is enabled.
  829                   PlatformLogger logger = HttpURLConnection.getHttpLogger();
  830                   if (logger.isLoggable(PlatformLogger.FINEST)) {
  831                       logger.finest("KeepAlive stream used: " + url);
  832                   }
  833                   serverInput = new KeepAliveStream(serverInput, pi, cl, this);
  834                   failedOnce = false;
  835               }
  836               else        {
  837                   serverInput = new MeteredStream(serverInput, pi, cl);
  838               }
  839           }
  840           else if (cl == -1)  {
  841               // In this case, content length is unknown - the input
  842               // stream would simply be a regular InputStream or
  843               // ChunkedInputStream.
  844   
  845               if (pi != null) {
  846                   // Progress monitoring is enabled.
  847   
  848                   pi.setContentType(responses.findValue("content-type"));
  849   
  850                   // Wrap MeteredStream for tracking indeterministic
  851                   // progress, even if the input stream is ChunkedInputStream.
  852                   serverInput = new MeteredStream(serverInput, pi, cl);
  853               }
  854               else    {
  855                   // Progress monitoring is disabled, and there is no
  856                   // need to wrap an unknown length input stream.
  857   
  858                   // ** This is an no-op **
  859               }
  860           }
  861           else    {
  862               if (pi != null)
  863                   pi.finishTracking();
  864           }
  865   
  866           return ret;
  867       }
  868   
  869       public synchronized InputStream getInputStream() {
  870           return serverInput;
  871       }
  872   
  873       public OutputStream getOutputStream() {
  874           return serverOutput;
  875       }
  876   
  877       @Override
  878       public String toString() {
  879           return getClass().getName()+"("+url+")";
  880       }
  881   
  882       public final boolean isKeepingAlive() {
  883           return getHttpKeepAliveSet() && keepingAlive;
  884       }
  885   
  886       public void setCacheRequest(CacheRequest cacheRequest) {
  887           this.cacheRequest = cacheRequest;
  888       }
  889   
  890       CacheRequest getCacheRequest() {
  891           return cacheRequest;
  892       }
  893   
  894       @Override
  895       protected void finalize() throws Throwable {
  896           // This should do nothing.  The stream finalizer will
  897           // close the fd.
  898       }
  899   
  900       public void setDoNotRetry(boolean value) {
  901           // failedOnce is used to determine if a request should be retried.
  902           failedOnce = value;
  903       }
  904   
  905       public void setIgnoreContinue(boolean value) {
  906           ignoreContinue = value;
  907       }
  908   
  909       /* Use only on connections in error. */
  910       @Override
  911       public void closeServer() {
  912           try {
  913               keepingAlive = false;
  914               serverSocket.close();
  915           } catch (Exception e) {}
  916       }
  917   
  918       /**
  919        * @return the proxy host being used for this client, or null
  920        *          if we're not going through a proxy
  921        */
  922       public String getProxyHostUsed() {
  923           if (!usingProxy) {
  924               return null;
  925           } else {
  926               return ((InetSocketAddress)proxy.address()).getHostString();
  927           }
  928       }
  929   
  930       /**
  931        * @return the proxy port being used for this client.  Meaningless
  932        *          if getProxyHostUsed() gives null.
  933        */
  934       public int getProxyPortUsed() {
  935           if (usingProxy)
  936               return ((InetSocketAddress)proxy.address()).getPort();
  937           return -1;
  938       }
  939   }

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