939 lines · 32.8 KB · Java
RawDownload
/* * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.net.www.http; import java.io.*;import java.net.*;import java.util.Locale;import sun.net.NetworkClient;import sun.net.ProgressSource;import sun.net.www.MessageHeader;import sun.net.www.HeaderParser;import sun.net.www.MeteredStream;import sun.net.www.ParseUtil;import sun.net.www.protocol.http.HttpURLConnection;import sun.util.logging.PlatformLogger; /** * @author Herb Jellinek * @author Dave Brown */public class HttpClient extends NetworkClient {    // whether this httpclient comes from the cache    protected boolean cachedHttpClient = false;     private boolean inCache;     protected CookieHandler cookieHandler;     // Http requests we send    MessageHeader requests;     // Http data we send with the headers    PosterOutputStream poster = null;     // true if we are in streaming mode (fixed length or chunked)    boolean streaming;     // if we've had one io error    boolean failedOnce = false;     /** Response code for CONTINUE */    private boolean ignoreContinue = true;    private static final int    HTTP_CONTINUE = 100;     /** Default port number for http daemons. REMIND: make these private */    static final int    httpPortNumber = 80;     /** return default port number (subclasses may override) */    protected int getDefaultPort () { return httpPortNumber; }     static private int getDefaultPort(String proto) {        if ("http".equalsIgnoreCase(proto))            return 80;        if ("https".equalsIgnoreCase(proto))            return 443;        return -1;    }     /* All proxying (generic as well as instance-specific) may be     * disabled through use of this flag     */    protected boolean proxyDisabled;     // are we using proxy in this instance?    public boolean usingProxy = false;    // target host, port for the URL    protected String host;    protected int port;     /* where we cache currently open, persistent connections */    protected static KeepAliveCache kac = new KeepAliveCache();     private static boolean keepAliveProp = true;     // retryPostProp is true by default so as to preserve behavior    // from previous releases.    private static boolean retryPostProp = true;     volatile boolean keepingAlive = false;     /* this is a keep-alive connection */    int keepAliveConnections = -1;    /* number of keep-alives left */     /**Idle timeout value, in milliseconds. Zero means infinity,     * iff keepingAlive=true.     * Unfortunately, we can't always believe this one.  If I'm connected     * through a Netscape proxy to a server that sent me a keep-alive     * time of 15 sec, the proxy unilaterally terminates my connection     * after 5 sec.  So we have to hard code our effective timeout to     * 4 sec for the case where we're using a proxy. *SIGH*     */    int keepAliveTimeout = 0;     /** whether the response is to be cached */    private CacheRequest cacheRequest = null;     /** Url being fetched. */    protected URL       url;     /* if set, the client will be reused and must not be put in cache */    public boolean reuse = false;     // Traffic capture tool, if configured. See HttpCapture class for info     private HttpCapture capture = null;     /**     * A NOP method kept for backwards binary compatibility     * @deprecated -- system properties are no longer cached.     */    @Deprecated    public static synchronized void resetProperties() {    }     int getKeepAliveTimeout() {        return keepAliveTimeout;    }     static {        String keepAlive = java.security.AccessController.doPrivileged(            new sun.security.action.GetPropertyAction("http.keepAlive"));         String retryPost = java.security.AccessController.doPrivileged(            new sun.security.action.GetPropertyAction("sun.net.http.retryPost"));         if (keepAlive != null) {            keepAliveProp = Boolean.valueOf(keepAlive).booleanValue();        } else {            keepAliveProp = true;        }         if (retryPost != null) {            retryPostProp = Boolean.valueOf(retryPost).booleanValue();        } else            retryPostProp = true;     }     /**     * @return true iff http keep alive is set (i.e. enabled).  Defaults     *          to true if the system property http.keepAlive isn't set.     */    public boolean getHttpKeepAliveSet() {        return keepAliveProp;    }      protected HttpClient() {    }     private HttpClient(URL url)    throws IOException {        this(url, (String)null, -1, false);    }     protected HttpClient(URL url,                         boolean proxyDisabled) throws IOException {        this(url, null, -1, proxyDisabled);    }     /* This package-only CTOR should only be used for FTP piggy-backed on HTTP     * HTTP URL's that use this won't take advantage of keep-alive.     * Additionally, this constructor may be used as a last resort when the     * first HttpClient gotten through New() failed (probably b/c of a     * Keep-Alive mismatch).     *     * XXX That documentation is wrong ... it's not package-private any more     */    public HttpClient(URL url, String proxyHost, int proxyPort)    throws IOException {        this(url, proxyHost, proxyPort, false);    }     protected HttpClient(URL url, Proxy p, int to) throws IOException {        proxy = (p == null) ? Proxy.NO_PROXY : p;        this.host = url.getHost();        this.url = url;        port = url.getPort();        if (port == -1) {            port = getDefaultPort();        }        setConnectTimeout(to);         // get the cookieHandler if there is any        cookieHandler = java.security.AccessController.doPrivileged(            new java.security.PrivilegedAction<CookieHandler>() {                public CookieHandler run() {                    return CookieHandler.getDefault();                }            });         capture = HttpCapture.getCapture(url);        openServer();    }     static protected Proxy newHttpProxy(String proxyHost, int proxyPort,                                      String proto) {        if (proxyHost == null || proto == null)            return Proxy.NO_PROXY;        int pport = proxyPort < 0 ? getDefaultPort(proto) : proxyPort;        InetSocketAddress saddr = InetSocketAddress.createUnresolved(proxyHost, pport);        return new Proxy(Proxy.Type.HTTP, saddr);    }     /*     * This constructor gives "ultimate" flexibility, including the ability     * to bypass implicit proxying.  Sometimes we need to be using tunneling     * (transport or network level) instead of proxying (application level),     * for example when we don't want the application level data to become     * visible to third parties.     *     * @param url               the URL to which we're connecting     * @param proxy             proxy to use for this URL (e.g. forwarding)     * @param proxyPort         proxy port to use for this URL     * @param proxyDisabled     true to disable default proxying     */    private HttpClient(URL url, String proxyHost, int proxyPort,                       boolean proxyDisabled)        throws IOException {        this(url, proxyDisabled ? Proxy.NO_PROXY :             newHttpProxy(proxyHost, proxyPort, "http"), -1);    }     public HttpClient(URL url, String proxyHost, int proxyPort,                       boolean proxyDisabled, int to)        throws IOException {        this(url, proxyDisabled ? Proxy.NO_PROXY :             newHttpProxy(proxyHost, proxyPort, "http"), to);    }     /* This class has no public constructor for HTTP.  This method is used to     * get an HttpClient to the specifed URL.  If there's currently an     * active HttpClient to that server/port, you'll get that one.     */    public static HttpClient New(URL url)    throws IOException {        return HttpClient.New(url, Proxy.NO_PROXY, -1, true);    }     public static HttpClient New(URL url, boolean useCache)        throws IOException {        return HttpClient.New(url, Proxy.NO_PROXY, -1, useCache);    }     public static HttpClient New(URL url, Proxy p, int to, boolean useCache)        throws IOException {        if (p == null) {            p = Proxy.NO_PROXY;        }        HttpClient ret = null;        /* see if one's already around */        if (useCache) {            ret = kac.get(url, null);            if (ret != null) {                if ((ret.proxy != null && ret.proxy.equals(p)) ||                    (ret.proxy == null && p == null)) {                    synchronized (ret) {                        ret.cachedHttpClient = true;                        assert ret.inCache;                        ret.inCache = false;                        PlatformLogger logger = HttpURLConnection.getHttpLogger();                        if (logger.isLoggable(PlatformLogger.FINEST)) {                            logger.finest("KeepAlive stream retrieved from the cache, " + ret);                        }                    }                } else {                    // We cannot return this connection to the cache as it's                    // KeepAliveTimeout will get reset. We simply close the connection.                    // This should be fine as it is very rare that a connection                    // to the same host will not use the same proxy.                    synchronized(ret) {                        ret.inCache = false;                        ret.closeServer();                    }                    ret = null;                }            }        }        if (ret == null) {            ret = new HttpClient(url, p, to);        } else {            SecurityManager security = System.getSecurityManager();            if (security != null) {                if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {                    security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());                } else {                    security.checkConnect(url.getHost(), url.getPort());                }            }            ret.url = url;        }        return ret;    }     public static HttpClient New(URL url, Proxy p, int to) throws IOException {        return New(url, p, to, true);    }     public static HttpClient New(URL url, String proxyHost, int proxyPort,                                 boolean useCache)        throws IOException {        return New(url, newHttpProxy(proxyHost, proxyPort, "http"), -1, useCache);    }     public static HttpClient New(URL url, String proxyHost, int proxyPort,                                 boolean useCache, int to)        throws IOException {        return New(url, newHttpProxy(proxyHost, proxyPort, "http"), to, useCache);    }     /* return it to the cache as still usable, if:     * 1) It's keeping alive, AND     * 2) It still has some connections left, AND     * 3) It hasn't had a error (PrintStream.checkError())     * 4) It hasn't timed out     *     * If this client is not keepingAlive, it should have been     * removed from the cache in the parseHeaders() method.     */     public void finished() {        if (reuse) /* will be reused */            return;        keepAliveConnections--;        poster = null;        if (keepAliveConnections > 0 && isKeepingAlive() &&               !(serverOutput.checkError())) {            /* This connection is keepingAlive && still valid.             * Return it to the cache.             */            putInKeepAliveCache();        } else {            closeServer();        }    }     protected synchronized void putInKeepAliveCache() {        if (inCache) {            assert false : "Duplicate put to keep alive cache";            return;        }        inCache = true;        kac.put(url, null, this);    }     protected synchronized boolean isInKeepAliveCache() {        return inCache;    }     /*     * Close an idle connection to this URL (if it exists in the     * cache).     */    public void closeIdleConnection() {        HttpClient http = kac.get(url, null);        if (http != null) {            http.closeServer();        }    }     /* We're very particular here about what our InputStream to the server     * looks like for reasons that are apparent if you can decipher the     * method parseHTTP().  That's why this method is overidden from the     * superclass.     */    @Override    public void openServer(String server, int port) throws IOException {        serverSocket = doConnect(server, port);        try {            OutputStream out = serverSocket.getOutputStream();            if (capture != null) {                out = new HttpCaptureOutputStream(out, capture);            }            serverOutput = new PrintStream(                new BufferedOutputStream(out),                                         false, encoding);        } catch (UnsupportedEncodingException e) {            throw new InternalError(encoding+" encoding not found");        }        serverSocket.setTcpNoDelay(true);    }     /*     * Returns true if the http request should be tunneled through proxy.     * An example where this is the case is Https.     */    public boolean needsTunneling() {        return false;    }     /*     * Returns true if this httpclient is from cache     */    public synchronized boolean isCachedConnection() {        return cachedHttpClient;    }     /*     * Finish any work left after the socket connection is     * established.  In the normal http case, it's a NO-OP. Subclass     * may need to override this. An example is Https, where for     * direct connection to the origin server, ssl handshake needs to     * be done; for proxy tunneling, the socket needs to be converted     * into an SSL socket before ssl handshake can take place.     */    public void afterConnect() throws IOException, UnknownHostException {        // NO-OP. Needs to be overwritten by HttpsClient    }     /*     * call openServer in a privileged block     */    private synchronized void privilegedOpenServer(final InetSocketAddress server)         throws IOException    {        try {            java.security.AccessController.doPrivileged(                new java.security.PrivilegedExceptionAction<Void>() {                    public Void run() throws IOException {                    openServer(server.getHostString(), server.getPort());                    return null;                }            });        } catch (java.security.PrivilegedActionException pae) {            throw (IOException) pae.getException();        }    }     /*     * call super.openServer     */    private void superOpenServer(final String proxyHost,                                 final int proxyPort)        throws IOException, UnknownHostException    {        super.openServer(proxyHost, proxyPort);    }     /*     */    protected synchronized void openServer() throws IOException {         SecurityManager security = System.getSecurityManager();         if (security != null) {            security.checkConnect(host, port);        }         if (keepingAlive) { // already opened            return;        }         if (url.getProtocol().equals("http") ||            url.getProtocol().equals("https") ) {             if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {                sun.net.www.URLConnection.setProxiedHost(host);                privilegedOpenServer((InetSocketAddress) proxy.address());                usingProxy = true;                return;            } else {                // make direct connection                openServer(host, port);                usingProxy = false;                return;            }         } else {            /* we're opening some other kind of url, most likely an             * ftp url.             */            if ((proxy != null) && (proxy.type() == Proxy.Type.HTTP)) {                sun.net.www.URLConnection.setProxiedHost(host);                privilegedOpenServer((InetSocketAddress) proxy.address());                usingProxy = true;                return;            } else {                // make direct connection                super.openServer(host, port);                usingProxy = false;

Lines 1–500 of 939.