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

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