3300 lines · 126.1 KB · Java
RawDownload
/* * Copyright (c) 1995, 2011, 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.protocol.http; import java.net.URL;import java.net.URLConnection;import java.net.ProtocolException;import java.net.HttpRetryException;import java.net.PasswordAuthentication;import java.net.Authenticator;import java.net.InetAddress;import java.net.UnknownHostException;import java.net.SocketTimeoutException;import java.net.Proxy;import java.net.ProxySelector;import java.net.URI;import java.net.InetSocketAddress;import java.net.CookieHandler;import java.net.ResponseCache;import java.net.CacheResponse;import java.net.SecureCacheResponse;import java.net.CacheRequest;import java.net.Authenticator.RequestorType;import java.io.*;import java.util.Date;import java.util.Map;import java.util.List;import java.util.Locale;import java.util.StringTokenizer;import java.util.Iterator;import java.util.HashSet;import java.util.HashMap;import java.util.Set;import sun.net.*;import sun.net.www.*;import sun.net.www.http.HttpClient;import sun.net.www.http.PosterOutputStream;import sun.net.www.http.ChunkedInputStream;import sun.net.www.http.ChunkedOutputStream;import sun.util.logging.PlatformLogger;import java.text.SimpleDateFormat;import java.util.TimeZone;import java.net.MalformedURLException;import java.nio.ByteBuffer;import static sun.net.www.protocol.http.AuthScheme.BASIC;import static sun.net.www.protocol.http.AuthScheme.DIGEST;import static sun.net.www.protocol.http.AuthScheme.NTLM;import static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;import static sun.net.www.protocol.http.AuthScheme.KERBEROS;import static sun.net.www.protocol.http.AuthScheme.UNKNOWN; /** * A class to represent an HTTP connection to a remote object. */  public class HttpURLConnection extends java.net.HttpURLConnection {     static String HTTP_CONNECT = "CONNECT";     static final String version;    public static final String userAgent;     /* max # of allowed re-directs */    static final int defaultmaxRedirects = 20;    static final int maxRedirects;     /* Not all servers support the (Proxy)-Authentication-Info headers.     * By default, we don't require them to be sent     */    static final boolean validateProxy;    static final boolean validateServer;     private StreamingOutputStream strOutputStream;    private final static String RETRY_MSG1 =        "cannot retry due to proxy authentication, in streaming mode";    private final static String RETRY_MSG2 =        "cannot retry due to server authentication, in streaming mode";    private final static String RETRY_MSG3 =        "cannot retry due to redirection, in streaming mode";     /*     * System properties related to error stream handling:     *     * sun.net.http.errorstream.enableBuffering = <boolean>     *     * With the above system property set to true (default is false),     * when the response code is >=400, the HTTP handler will try to     * buffer the response body (up to a certain amount and within a     * time limit). Thus freeing up the underlying socket connection     * for reuse. The rationale behind this is that usually when the     * server responds with a >=400 error (client error or server     * error, such as 404 file not found), the server will send a     * small response body to explain who to contact and what to do to     * recover. With this property set to true, even if the     * application doesn't call getErrorStream(), read the response     * body, and then call close(), the underlying socket connection     * can still be kept-alive and reused. The following two system     * properties provide further control to the error stream     * buffering behaviour.     *     * sun.net.http.errorstream.timeout = <int>     *     the timeout (in millisec) waiting the error stream     *     to be buffered; default is 300 ms     *     * sun.net.http.errorstream.bufferSize = <int>     *     the size (in bytes) to use for the buffering the error stream;     *     default is 4k     */      /* Should we enable buffering of error streams? */    private static boolean enableESBuffer = false;     /* timeout waiting for read for buffered error stream;     */    private static int timeout4ESBuffer = 0;     /* buffer size for buffered error stream;    */    private static int bufSize4ES = 0;     /*     * Restrict setting of request headers through the public api     * consistent with JavaScript XMLHttpRequest2 with a few     * exceptions. Disallowed headers are silently ignored for     * backwards compatibility reasons rather than throwing a     * SecurityException. For example, some applets set the     * Host header since old JREs did not implement HTTP 1.1.     * Additionally, any header starting with Sec- is     * disallowed.     *     * The following headers are allowed for historical reasons:     *     * Accept-Charset, Accept-Encoding, Cookie, Cookie2, Date,     * Referer, TE, User-Agent, headers beginning with Proxy-.     *     * The following headers are allowed in a limited form:     *     * Connection: close     *     * See http://www.w3.org/TR/XMLHttpRequest2.     */    private static final boolean allowRestrictedHeaders;    private static final Set<String> restrictedHeaderSet;    private static final String[] restrictedHeaders = {        /* Restricted by XMLHttpRequest2 */        //"Accept-Charset",        //"Accept-Encoding",        "Access-Control-Request-Headers",        "Access-Control-Request-Method",        "Connection", /* close is allowed */        "Content-Length",        //"Cookie",        //"Cookie2",        "Content-Transfer-Encoding",        //"Date",        //"Expect",        "Host",        "Keep-Alive",        "Origin",        // "Referer",        // "TE",        "Trailer",        "Transfer-Encoding",        "Upgrade",        //"User-Agent",        "Via"    };     static {        maxRedirects = java.security.AccessController.doPrivileged(            new sun.security.action.GetIntegerAction(                "http.maxRedirects", defaultmaxRedirects)).intValue();        version = java.security.AccessController.doPrivileged(                    new sun.security.action.GetPropertyAction("java.version"));        String agent = java.security.AccessController.doPrivileged(                    new sun.security.action.GetPropertyAction("http.agent"));        if (agent == null) {            agent = "Java/"+version;        } else {            agent = agent + " Java/"+version;        }        userAgent = agent;        validateProxy = java.security.AccessController.doPrivileged(                new sun.security.action.GetBooleanAction(                    "http.auth.digest.validateProxy")).booleanValue();        validateServer = java.security.AccessController.doPrivileged(                new sun.security.action.GetBooleanAction(                    "http.auth.digest.validateServer")).booleanValue();         enableESBuffer = java.security.AccessController.doPrivileged(                new sun.security.action.GetBooleanAction(                    "sun.net.http.errorstream.enableBuffering")).booleanValue();        timeout4ESBuffer = java.security.AccessController.doPrivileged(                new sun.security.action.GetIntegerAction(                    "sun.net.http.errorstream.timeout", 300)).intValue();        if (timeout4ESBuffer <= 0) {            timeout4ESBuffer = 300; // use the default        }         bufSize4ES = java.security.AccessController.doPrivileged(                new sun.security.action.GetIntegerAction(                    "sun.net.http.errorstream.bufferSize", 4096)).intValue();        if (bufSize4ES <= 0) {            bufSize4ES = 4096; // use the default        }         allowRestrictedHeaders = ((Boolean)java.security.AccessController.doPrivileged(                new sun.security.action.GetBooleanAction(                    "sun.net.http.allowRestrictedHeaders"))).booleanValue();        if (!allowRestrictedHeaders) {            restrictedHeaderSet = new HashSet<String>(restrictedHeaders.length);            for (int i=0; i < restrictedHeaders.length; i++) {                restrictedHeaderSet.add(restrictedHeaders[i].toLowerCase());            }        } else {            restrictedHeaderSet = null;        }    }     static final String httpVersion = "HTTP/1.1";    static final String acceptString =        "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";     // the following http request headers should NOT have their values    // returned for security reasons.    private static final String[] EXCLUDE_HEADERS = {            "Proxy-Authorization",            "Authorization"    };     // also exclude system cookies when any might be set    private static final String[] EXCLUDE_HEADERS2= {            "Proxy-Authorization",            "Authorization",            "Cookie",            "Cookie2"    };     protected HttpClient http;    protected Handler handler;    protected Proxy instProxy;     private CookieHandler cookieHandler;    private ResponseCache cacheHandler;     // the cached response, and cached response headers and body    protected CacheResponse cachedResponse;    private MessageHeader cachedHeaders;    private InputStream cachedInputStream;     /* output stream to server */    protected PrintStream ps = null;      /* buffered error stream */    private InputStream errorStream = null;     /* User set Cookies */    private boolean setUserCookies = true;    private String userCookies = null;    private String userCookies2 = null;     /* We only have a single static authenticator for now.     * REMIND:  backwards compatibility with JDK 1.1.  Should be     * eliminated for JDK 2.0.     */    private static HttpAuthenticator defaultAuth;     /* all the headers we send     * NOTE: do *NOT* dump out the content of 'requests' in the     * output or stacktrace since it may contain security-sensitive     * headers such as those defined in EXCLUDE_HEADERS.     */    private MessageHeader requests;     /* The following two fields are only used with Digest Authentication */    String domain;      /* The list of authentication domains */    DigestAuthentication.Parameters digestparams;     /* Current credentials in use */    AuthenticationInfo  currentProxyCredentials = null;    AuthenticationInfo  currentServerCredentials = null;    boolean             needToCheck = true;    private boolean doingNTLM2ndStage = false; /* doing the 2nd stage of an NTLM server authentication */    private boolean doingNTLMp2ndStage = false; /* doing the 2nd stage of an NTLM proxy authentication */     /* try auth without calling Authenticator. Used for transparent NTLM authentication */    private boolean tryTransparentNTLMServer = true;    private boolean tryTransparentNTLMProxy = true;     /* Used by Windows specific code */    private Object authObj;     /* Set if the user is manually setting the Authorization or Proxy-Authorization headers */    boolean isUserServerAuth;    boolean isUserProxyAuth;     String serverAuthKey, proxyAuthKey;     /* Progress source */    protected ProgressSource pi;     /* all the response headers we get back */    private MessageHeader responses;    /* the stream _from_ the server */    private InputStream inputStream = null;    /* post stream _to_ the server, if any */    private PosterOutputStream poster = null;     /* Indicates if the std. request headers have been set in requests. */    private boolean setRequests=false;     /* Indicates whether a request has already failed or not */    private boolean failedOnce=false;     /* Remembered Exception, we will throw it again if somebody       calls getInputStream after disconnect */    private Exception rememberedException = null;     /* If we decide we want to reuse a client, we put it here */    private HttpClient reuseClient = null;     /* Tunnel states */    enum TunnelState {        /* No tunnel */        NONE,         /* Setting up a tunnel */        SETUP,         /* Tunnel has been successfully setup */        TUNNELING    }     private TunnelState tunnelState = TunnelState.NONE;     /* Redefine timeouts from java.net.URLConnection as we need -1 to mean     * not set. This is to ensure backward compatibility.     */    private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT;    private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT;     /* Logging support */    private static final PlatformLogger logger =            PlatformLogger.getLogger("sun.net.www.protocol.http.HttpURLConnection");     /*     * privileged request password authentication     *     */    private static PasswordAuthentication    privilegedRequestPasswordAuthentication(                            final String host,                            final InetAddress addr,                            final int port,                            final String protocol,                            final String prompt,                            final String scheme,                            final URL url,                            final RequestorType authType) {        return java.security.AccessController.doPrivileged(            new java.security.PrivilegedAction<PasswordAuthentication>() {                public PasswordAuthentication run() {                    if (logger.isLoggable(PlatformLogger.FINEST)) {                        logger.finest("Requesting Authentication: host =" + host + " url = " + url);                    }                    PasswordAuthentication pass = Authenticator.requestPasswordAuthentication(                        host, addr, port, protocol,                        prompt, scheme, url, authType);                    if (logger.isLoggable(PlatformLogger.FINEST)) {                        logger.finest("Authentication returned: " + (pass != null ? pass.toString() : "null"));                    }                    return pass;                }            });    }     private boolean isRestrictedHeader(String key, String value) {        if (allowRestrictedHeaders) {            return false;        }         key = key.toLowerCase();        if (restrictedHeaderSet.contains(key)) {            /*             * Exceptions to restricted headers:             *             * Allow "Connection: close".             */            if (key.equals("connection") && value.equalsIgnoreCase("close")) {                return false;            }            return true;        } else if (key.startsWith("sec-")) {            return true;        }        return false;    }     /*     * Checks the validity of http message header and whether the header     * is restricted and throws IllegalArgumentException if invalid or     * restricted.     */    private boolean isExternalMessageHeaderAllowed(String key, String value) {        checkMessageHeader(key, value);        if (!isRestrictedHeader(key, value)) {            return true;        }        return false;    }     /* Logging support */    public static PlatformLogger getHttpLogger() {        return logger;    }     /* Used for Windows NTLM implementation */    public Object authObj() {        return authObj;    }     public void authObj(Object authObj) {        this.authObj = authObj;    }     /*     * checks the validity of http message header and throws     * IllegalArgumentException if invalid.     */    private void checkMessageHeader(String key, String value) {        char LF = '\n';        int index = key.indexOf(LF);        if (index != -1) {            throw new IllegalArgumentException(                "Illegal character(s) in message header field: " + key);        }        else {            if (value == null) {                return;            }             index = value.indexOf(LF);            while (index != -1) {                index++;                if (index < value.length()) {                    char c = value.charAt(index);                    if ((c==' ') || (c=='\t')) {                        // ok, check the next occurrence                        index = value.indexOf(LF, index);                        continue;                    }                }                throw new IllegalArgumentException(                    "Illegal character(s) in message header value: " + value);            }        }    }     /* adds the standard key/val pairs to reqests if necessary & write to     * given PrintStream     */    private void writeRequests() throws IOException {        /* print all message headers in the MessageHeader         * onto the wire - all the ones we've set and any         * others that have been set         */        // send any pre-emptive authentication        if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) {            setPreemptiveProxyAuthentication(requests);        }        if (!setRequests) {             /* We're very particular about the order in which we             * set the request headers here.  The order should not

Lines 1–500 of 3300.