0N/A/*
5839N/A * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.net.www.protocol.http;
0N/A
0N/Aimport java.net.URL;
0N/Aimport java.net.URLConnection;
0N/Aimport java.net.ProtocolException;
0N/Aimport java.net.HttpRetryException;
0N/Aimport java.net.PasswordAuthentication;
0N/Aimport java.net.Authenticator;
4566N/Aimport java.net.HttpCookie;
0N/Aimport java.net.InetAddress;
0N/Aimport java.net.UnknownHostException;
0N/Aimport java.net.SocketTimeoutException;
0N/Aimport java.net.Proxy;
0N/Aimport java.net.ProxySelector;
0N/Aimport java.net.URI;
0N/Aimport java.net.InetSocketAddress;
0N/Aimport java.net.CookieHandler;
0N/Aimport java.net.ResponseCache;
0N/Aimport java.net.CacheResponse;
0N/Aimport java.net.SecureCacheResponse;
0N/Aimport java.net.CacheRequest;
0N/Aimport java.net.Authenticator.RequestorType;
0N/Aimport java.io.*;
4566N/Aimport java.util.ArrayList;
4566N/Aimport java.util.Collections;
0N/Aimport java.util.Date;
0N/Aimport java.util.Map;
0N/Aimport java.util.List;
0N/Aimport java.util.Locale;
0N/Aimport java.util.StringTokenizer;
0N/Aimport java.util.Iterator;
2910N/Aimport java.util.HashSet;
2910N/Aimport java.util.HashMap;
2910N/Aimport java.util.Set;
0N/Aimport sun.net.*;
0N/Aimport sun.net.www.*;
0N/Aimport sun.net.www.http.HttpClient;
0N/Aimport sun.net.www.http.PosterOutputStream;
0N/Aimport sun.net.www.http.ChunkedInputStream;
0N/Aimport sun.net.www.http.ChunkedOutputStream;
1677N/Aimport sun.util.logging.PlatformLogger;
0N/Aimport java.text.SimpleDateFormat;
0N/Aimport java.util.TimeZone;
0N/Aimport java.net.MalformedURLException;
0N/Aimport java.nio.ByteBuffer;
1670N/Aimport static sun.net.www.protocol.http.AuthScheme.BASIC;
1670N/Aimport static sun.net.www.protocol.http.AuthScheme.DIGEST;
1670N/Aimport static sun.net.www.protocol.http.AuthScheme.NTLM;
1670N/Aimport static sun.net.www.protocol.http.AuthScheme.NEGOTIATE;
1670N/Aimport static sun.net.www.protocol.http.AuthScheme.KERBEROS;
1670N/Aimport static sun.net.www.protocol.http.AuthScheme.UNKNOWN;
0N/A
0N/A/**
0N/A * A class to represent an HTTP connection to a remote object.
0N/A */
0N/A
0N/A
0N/Apublic class HttpURLConnection extends java.net.HttpURLConnection {
0N/A
256N/A static String HTTP_CONNECT = "CONNECT";
256N/A
0N/A static final String version;
0N/A public static final String userAgent;
0N/A
0N/A /* max # of allowed re-directs */
0N/A static final int defaultmaxRedirects = 20;
0N/A static final int maxRedirects;
0N/A
0N/A /* Not all servers support the (Proxy)-Authentication-Info headers.
0N/A * By default, we don't require them to be sent
0N/A */
0N/A static final boolean validateProxy;
0N/A static final boolean validateServer;
0N/A
0N/A private StreamingOutputStream strOutputStream;
0N/A private final static String RETRY_MSG1 =
0N/A "cannot retry due to proxy authentication, in streaming mode";
0N/A private final static String RETRY_MSG2 =
0N/A "cannot retry due to server authentication, in streaming mode";
0N/A private final static String RETRY_MSG3 =
0N/A "cannot retry due to redirection, in streaming mode";
0N/A
0N/A /*
0N/A * System properties related to error stream handling:
0N/A *
0N/A * sun.net.http.errorstream.enableBuffering = <boolean>
0N/A *
0N/A * With the above system property set to true (default is false),
0N/A * when the response code is >=400, the HTTP handler will try to
0N/A * buffer the response body (up to a certain amount and within a
0N/A * time limit). Thus freeing up the underlying socket connection
0N/A * for reuse. The rationale behind this is that usually when the
0N/A * server responds with a >=400 error (client error or server
0N/A * error, such as 404 file not found), the server will send a
0N/A * small response body to explain who to contact and what to do to
0N/A * recover. With this property set to true, even if the
0N/A * application doesn't call getErrorStream(), read the response
0N/A * body, and then call close(), the underlying socket connection
0N/A * can still be kept-alive and reused. The following two system
0N/A * properties provide further control to the error stream
0N/A * buffering behaviour.
0N/A *
0N/A * sun.net.http.errorstream.timeout = <int>
0N/A * the timeout (in millisec) waiting the error stream
0N/A * to be buffered; default is 300 ms
0N/A *
0N/A * sun.net.http.errorstream.bufferSize = <int>
0N/A * the size (in bytes) to use for the buffering the error stream;
0N/A * default is 4k
0N/A */
0N/A
0N/A
0N/A /* Should we enable buffering of error streams? */
0N/A private static boolean enableESBuffer = false;
0N/A
0N/A /* timeout waiting for read for buffered error stream;
0N/A */
0N/A private static int timeout4ESBuffer = 0;
0N/A
0N/A /* buffer size for buffered error stream;
0N/A */
0N/A private static int bufSize4ES = 0;
0N/A
2910N/A /*
2910N/A * Restrict setting of request headers through the public api
2910N/A * consistent with JavaScript XMLHttpRequest2 with a few
2910N/A * exceptions. Disallowed headers are silently ignored for
2910N/A * backwards compatibility reasons rather than throwing a
2910N/A * SecurityException. For example, some applets set the
2910N/A * Host header since old JREs did not implement HTTP 1.1.
2910N/A * Additionally, any header starting with Sec- is
2910N/A * disallowed.
2910N/A *
2910N/A * The following headers are allowed for historical reasons:
2910N/A *
2910N/A * Accept-Charset, Accept-Encoding, Cookie, Cookie2, Date,
2910N/A * Referer, TE, User-Agent, headers beginning with Proxy-.
2910N/A *
2910N/A * The following headers are allowed in a limited form:
2910N/A *
2910N/A * Connection: close
2910N/A *
2910N/A * See http://www.w3.org/TR/XMLHttpRequest2.
2910N/A */
2910N/A private static final boolean allowRestrictedHeaders;
2910N/A private static final Set<String> restrictedHeaderSet;
2910N/A private static final String[] restrictedHeaders = {
2910N/A /* Restricted by XMLHttpRequest2 */
2910N/A //"Accept-Charset",
2910N/A //"Accept-Encoding",
2910N/A "Access-Control-Request-Headers",
2910N/A "Access-Control-Request-Method",
2910N/A "Connection", /* close is allowed */
2910N/A "Content-Length",
2910N/A //"Cookie",
2910N/A //"Cookie2",
2910N/A "Content-Transfer-Encoding",
2910N/A //"Date",
2910N/A //"Expect",
2910N/A "Host",
2910N/A "Keep-Alive",
2910N/A "Origin",
2910N/A // "Referer",
2910N/A // "TE",
2910N/A "Trailer",
2910N/A "Transfer-Encoding",
2910N/A "Upgrade",
2910N/A //"User-Agent",
2910N/A "Via"
2910N/A };
2910N/A
0N/A static {
0N/A maxRedirects = java.security.AccessController.doPrivileged(
28N/A new sun.security.action.GetIntegerAction(
28N/A "http.maxRedirects", defaultmaxRedirects)).intValue();
0N/A version = java.security.AccessController.doPrivileged(
0N/A new sun.security.action.GetPropertyAction("java.version"));
0N/A String agent = java.security.AccessController.doPrivileged(
0N/A new sun.security.action.GetPropertyAction("http.agent"));
0N/A if (agent == null) {
0N/A agent = "Java/"+version;
0N/A } else {
0N/A agent = agent + " Java/"+version;
0N/A }
0N/A userAgent = agent;
0N/A validateProxy = java.security.AccessController.doPrivileged(
0N/A new sun.security.action.GetBooleanAction(
0N/A "http.auth.digest.validateProxy")).booleanValue();
0N/A validateServer = java.security.AccessController.doPrivileged(
0N/A new sun.security.action.GetBooleanAction(
0N/A "http.auth.digest.validateServer")).booleanValue();
0N/A
0N/A enableESBuffer = java.security.AccessController.doPrivileged(
0N/A new sun.security.action.GetBooleanAction(
0N/A "sun.net.http.errorstream.enableBuffering")).booleanValue();
0N/A timeout4ESBuffer = java.security.AccessController.doPrivileged(
0N/A new sun.security.action.GetIntegerAction(
0N/A "sun.net.http.errorstream.timeout", 300)).intValue();
0N/A if (timeout4ESBuffer <= 0) {
0N/A timeout4ESBuffer = 300; // use the default
0N/A }
0N/A
0N/A bufSize4ES = java.security.AccessController.doPrivileged(
0N/A new sun.security.action.GetIntegerAction(
0N/A "sun.net.http.errorstream.bufferSize", 4096)).intValue();
0N/A if (bufSize4ES <= 0) {
0N/A bufSize4ES = 4096; // use the default
0N/A }
0N/A
2910N/A allowRestrictedHeaders = ((Boolean)java.security.AccessController.doPrivileged(
2910N/A new sun.security.action.GetBooleanAction(
2910N/A "sun.net.http.allowRestrictedHeaders"))).booleanValue();
2910N/A if (!allowRestrictedHeaders) {
2910N/A restrictedHeaderSet = new HashSet<String>(restrictedHeaders.length);
2910N/A for (int i=0; i < restrictedHeaders.length; i++) {
2910N/A restrictedHeaderSet.add(restrictedHeaders[i].toLowerCase());
2910N/A }
2910N/A } else {
2910N/A restrictedHeaderSet = null;
2910N/A }
0N/A }
0N/A
0N/A static final String httpVersion = "HTTP/1.1";
0N/A static final String acceptString =
0N/A "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";
0N/A
0N/A // the following http request headers should NOT have their values
0N/A // returned for security reasons.
0N/A private static final String[] EXCLUDE_HEADERS = {
0N/A "Proxy-Authorization",
0N/A "Authorization"
0N/A };
2910N/A
2910N/A // also exclude system cookies when any might be set
2910N/A private static final String[] EXCLUDE_HEADERS2= {
2910N/A "Proxy-Authorization",
2910N/A "Authorization",
2910N/A "Cookie",
2910N/A "Cookie2"
2910N/A };
2910N/A
0N/A protected HttpClient http;
0N/A protected Handler handler;
0N/A protected Proxy instProxy;
0N/A
0N/A private CookieHandler cookieHandler;
0N/A private ResponseCache cacheHandler;
0N/A
0N/A // the cached response, and cached response headers and body
0N/A protected CacheResponse cachedResponse;
0N/A private MessageHeader cachedHeaders;
0N/A private InputStream cachedInputStream;
0N/A
0N/A /* output stream to server */
0N/A protected PrintStream ps = null;
0N/A
0N/A
0N/A /* buffered error stream */
0N/A private InputStream errorStream = null;
0N/A
0N/A /* User set Cookies */
0N/A private boolean setUserCookies = true;
0N/A private String userCookies = null;
2910N/A private String userCookies2 = null;
0N/A
0N/A /* We only have a single static authenticator for now.
0N/A * REMIND: backwards compatibility with JDK 1.1. Should be
0N/A * eliminated for JDK 2.0.
0N/A */
0N/A private static HttpAuthenticator defaultAuth;
0N/A
0N/A /* all the headers we send
0N/A * NOTE: do *NOT* dump out the content of 'requests' in the
0N/A * output or stacktrace since it may contain security-sensitive
0N/A * headers such as those defined in EXCLUDE_HEADERS.
0N/A */
0N/A private MessageHeader requests;
0N/A
0N/A /* The following two fields are only used with Digest Authentication */
0N/A String domain; /* The list of authentication domains */
0N/A DigestAuthentication.Parameters digestparams;
0N/A
0N/A /* Current credentials in use */
0N/A AuthenticationInfo currentProxyCredentials = null;
0N/A AuthenticationInfo currentServerCredentials = null;
0N/A boolean needToCheck = true;
0N/A private boolean doingNTLM2ndStage = false; /* doing the 2nd stage of an NTLM server authentication */
0N/A private boolean doingNTLMp2ndStage = false; /* doing the 2nd stage of an NTLM proxy authentication */
1670N/A
1670N/A /* try auth without calling Authenticator. Used for transparent NTLM authentication */
1670N/A private boolean tryTransparentNTLMServer = true;
1670N/A private boolean tryTransparentNTLMProxy = true;
1670N/A
1315N/A /* Used by Windows specific code */
1791N/A private Object authObj;
0N/A
0N/A /* Set if the user is manually setting the Authorization or Proxy-Authorization headers */
0N/A boolean isUserServerAuth;
0N/A boolean isUserProxyAuth;
0N/A
2278N/A String serverAuthKey, proxyAuthKey;
2278N/A
0N/A /* Progress source */
0N/A protected ProgressSource pi;
0N/A
0N/A /* all the response headers we get back */
0N/A private MessageHeader responses;
0N/A /* the stream _from_ the server */
0N/A private InputStream inputStream = null;
0N/A /* post stream _to_ the server, if any */
0N/A private PosterOutputStream poster = null;
0N/A
0N/A /* Indicates if the std. request headers have been set in requests. */
0N/A private boolean setRequests=false;
0N/A
0N/A /* Indicates whether a request has already failed or not */
0N/A private boolean failedOnce=false;
0N/A
0N/A /* Remembered Exception, we will throw it again if somebody
0N/A calls getInputStream after disconnect */
0N/A private Exception rememberedException = null;
0N/A
0N/A /* If we decide we want to reuse a client, we put it here */
0N/A private HttpClient reuseClient = null;
0N/A
256N/A /* Tunnel states */
5365N/A public enum TunnelState {
256N/A /* No tunnel */
256N/A NONE,
256N/A
256N/A /* Setting up a tunnel */
256N/A SETUP,
256N/A
256N/A /* Tunnel has been successfully setup */
256N/A TUNNELING
256N/A }
256N/A
256N/A private TunnelState tunnelState = TunnelState.NONE;
256N/A
2981N/A /* Redefine timeouts from java.net.URLConnection as we need -1 to mean
0N/A * not set. This is to ensure backward compatibility.
0N/A */
2981N/A private int connectTimeout = NetworkClient.DEFAULT_CONNECT_TIMEOUT;
2981N/A private int readTimeout = NetworkClient.DEFAULT_READ_TIMEOUT;
0N/A
1677N/A /* Logging support */
1677N/A private static final PlatformLogger logger =
1677N/A PlatformLogger.getLogger("sun.net.www.protocol.http.HttpURLConnection");
1677N/A
0N/A /*
0N/A * privileged request password authentication
0N/A *
0N/A */
0N/A private static PasswordAuthentication
0N/A privilegedRequestPasswordAuthentication(
0N/A final String host,
0N/A final InetAddress addr,
0N/A final int port,
0N/A final String protocol,
0N/A final String prompt,
0N/A final String scheme,
0N/A final URL url,
0N/A final RequestorType authType) {
28N/A return java.security.AccessController.doPrivileged(
28N/A new java.security.PrivilegedAction<PasswordAuthentication>() {
28N/A public PasswordAuthentication run() {
1677N/A if (logger.isLoggable(PlatformLogger.FINEST)) {
1677N/A logger.finest("Requesting Authentication: host =" + host + " url = " + url);
1313N/A }
1313N/A PasswordAuthentication pass = Authenticator.requestPasswordAuthentication(
0N/A host, addr, port, protocol,
0N/A prompt, scheme, url, authType);
1677N/A if (logger.isLoggable(PlatformLogger.FINEST)) {
1677N/A logger.finest("Authentication returned: " + (pass != null ? pass.toString() : "null"));
1313N/A }
1313N/A return pass;
0N/A }
0N/A });
0N/A }
0N/A
2910N/A private boolean isRestrictedHeader(String key, String value) {
2910N/A if (allowRestrictedHeaders) {
2910N/A return false;
2910N/A }
2910N/A
2910N/A key = key.toLowerCase();
2910N/A if (restrictedHeaderSet.contains(key)) {
2910N/A /*
2910N/A * Exceptions to restricted headers:
2910N/A *
2910N/A * Allow "Connection: close".
2910N/A */
2910N/A if (key.equals("connection") && value.equalsIgnoreCase("close")) {
2910N/A return false;
2910N/A }
2910N/A return true;
2910N/A } else if (key.startsWith("sec-")) {
2910N/A return true;
2910N/A }
2910N/A return false;
2910N/A }
2910N/A
2910N/A /*
2910N/A * Checks the validity of http message header and whether the header
2910N/A * is restricted and throws IllegalArgumentException if invalid or
2910N/A * restricted.
2910N/A */
2910N/A private boolean isExternalMessageHeaderAllowed(String key, String value) {
2910N/A checkMessageHeader(key, value);
2910N/A if (!isRestrictedHeader(key, value)) {
2910N/A return true;
2910N/A }
2910N/A return false;
2910N/A }
2910N/A
1677N/A /* Logging support */
1677N/A public static PlatformLogger getHttpLogger() {
1677N/A return logger;
1677N/A }
1677N/A
1791N/A /* Used for Windows NTLM implementation */
1791N/A public Object authObj() {
1791N/A return authObj;
1791N/A }
1791N/A
1791N/A public void authObj(Object authObj) {
1791N/A this.authObj = authObj;
1791N/A }
1791N/A
0N/A /*
0N/A * checks the validity of http message header and throws
0N/A * IllegalArgumentException if invalid.
0N/A */
0N/A private void checkMessageHeader(String key, String value) {
0N/A char LF = '\n';
0N/A int index = key.indexOf(LF);
0N/A if (index != -1) {
0N/A throw new IllegalArgumentException(
0N/A "Illegal character(s) in message header field: " + key);
0N/A }
0N/A else {
0N/A if (value == null) {
0N/A return;
0N/A }
0N/A
0N/A index = value.indexOf(LF);
0N/A while (index != -1) {
0N/A index++;
0N/A if (index < value.length()) {
0N/A char c = value.charAt(index);
0N/A if ((c==' ') || (c=='\t')) {
0N/A // ok, check the next occurrence
0N/A index = value.indexOf(LF, index);
0N/A continue;
0N/A }
0N/A }
0N/A throw new IllegalArgumentException(
0N/A "Illegal character(s) in message header value: " + value);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /* adds the standard key/val pairs to reqests if necessary & write to
0N/A * given PrintStream
0N/A */
0N/A private void writeRequests() throws IOException {
0N/A /* print all message headers in the MessageHeader
0N/A * onto the wire - all the ones we've set and any
0N/A * others that have been set
0N/A */
0N/A // send any pre-emptive authentication
256N/A if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) {
0N/A setPreemptiveProxyAuthentication(requests);
0N/A }
0N/A if (!setRequests) {
0N/A
0N/A /* We're very particular about the order in which we
0N/A * set the request headers here. The order should not
0N/A * matter, but some careless CGI programs have been
0N/A * written to expect a very particular order of the
0N/A * standard headers. To name names, the order in which
0N/A * Navigator3.0 sends them. In particular, we make *sure*
0N/A * to send Content-type: <> and Content-length:<> second
0N/A * to last and last, respectively, in the case of a POST
0N/A * request.
0N/A */
0N/A if (!failedOnce)
1710N/A requests.prepend(method + " " + getRequestURI()+" " +
0N/A httpVersion, null);
0N/A if (!getUseCaches()) {
0N/A requests.setIfNotSet ("Cache-Control", "no-cache");
0N/A requests.setIfNotSet ("Pragma", "no-cache");
0N/A }
0N/A requests.setIfNotSet("User-Agent", userAgent);
0N/A int port = url.getPort();
0N/A String host = url.getHost();
0N/A if (port != -1 && port != url.getDefaultPort()) {
0N/A host += ":" + String.valueOf(port);
0N/A }
0N/A requests.setIfNotSet("Host", host);
0N/A requests.setIfNotSet("Accept", acceptString);
0N/A
0N/A /*
0N/A * For HTTP/1.1 the default behavior is to keep connections alive.
0N/A * However, we may be talking to a 1.0 server so we should set
0N/A * keep-alive just in case, except if we have encountered an error
0N/A * or if keep alive is disabled via a system property
0N/A */
0N/A
0N/A // Try keep-alive only on first attempt
0N/A if (!failedOnce && http.getHttpKeepAliveSet()) {
2163N/A if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) {
0N/A requests.setIfNotSet("Proxy-Connection", "keep-alive");
0N/A } else {
0N/A requests.setIfNotSet("Connection", "keep-alive");
0N/A }
0N/A } else {
0N/A /*
0N/A * RFC 2616 HTTP/1.1 section 14.10 says:
0N/A * HTTP/1.1 applications that do not support persistent
0N/A * connections MUST include the "close" connection option
0N/A * in every message
0N/A */
0N/A requests.setIfNotSet("Connection", "close");
0N/A }
0N/A // Set modified since if necessary
0N/A long modTime = getIfModifiedSince();
0N/A if (modTime != 0 ) {
0N/A Date date = new Date(modTime);
0N/A //use the preferred date format according to RFC 2068(HTTP1.1),
0N/A // RFC 822 and RFC 1123
0N/A SimpleDateFormat fo =
0N/A new SimpleDateFormat ("EEE, dd MMM yyyy HH:mm:ss 'GMT'", Locale.US);
0N/A fo.setTimeZone(TimeZone.getTimeZone("GMT"));
0N/A requests.setIfNotSet("If-Modified-Since", fo.format(date));
0N/A }
0N/A // check for preemptive authorization
0N/A AuthenticationInfo sauth = AuthenticationInfo.getServerAuth(url);
0N/A if (sauth != null && sauth.supportsPreemptiveAuthorization() ) {
0N/A // Sets "Authorization"
0N/A requests.setIfNotSet(sauth.getHeaderName(), sauth.getHeaderValue(url,method));
0N/A currentServerCredentials = sauth;
0N/A }
0N/A
0N/A if (!method.equals("PUT") && (poster != null || streaming())) {
0N/A requests.setIfNotSet ("Content-type",
0N/A "application/x-www-form-urlencoded");
0N/A }
0N/A
2907N/A boolean chunked = false;
2907N/A
0N/A if (streaming()) {
0N/A if (chunkLength != -1) {
0N/A requests.set ("Transfer-Encoding", "chunked");
2907N/A chunked = true;
705N/A } else { /* fixed content length */
705N/A if (fixedContentLengthLong != -1) {
705N/A requests.set ("Content-Length",
705N/A String.valueOf(fixedContentLengthLong));
705N/A } else if (fixedContentLength != -1) {
705N/A requests.set ("Content-Length",
705N/A String.valueOf(fixedContentLength));
705N/A }
0N/A }
0N/A } else if (poster != null) {
0N/A /* add Content-Length & POST/PUT data */
0N/A synchronized (poster) {
0N/A /* close it, so no more data can be added */
0N/A poster.close();
0N/A requests.set("Content-Length",
0N/A String.valueOf(poster.size()));
0N/A }
0N/A }
0N/A
2907N/A if (!chunked) {
2907N/A if (requests.findValue("Transfer-Encoding") != null) {
2907N/A requests.remove("Transfer-Encoding");
2907N/A if (logger.isLoggable(PlatformLogger.WARNING)) {
2907N/A logger.warning(
2907N/A "use streaming mode for chunked encoding");
2907N/A }
2907N/A }
2907N/A }
2907N/A
0N/A // get applicable cookies based on the uri and request headers
0N/A // add them to the existing request headers
0N/A setCookieHeader();
0N/A
0N/A setRequests=true;
0N/A }
1677N/A if (logger.isLoggable(PlatformLogger.FINE)) {
1677N/A logger.fine(requests.toString());
0N/A }
2804N/A http.writeRequests(requests, poster, streaming());
0N/A if (ps.checkError()) {
0N/A String proxyHost = http.getProxyHostUsed();
0N/A int proxyPort = http.getProxyPortUsed();
0N/A disconnectInternal();
0N/A if (failedOnce) {
0N/A throw new IOException("Error writing to server");
0N/A } else { // try once more
0N/A failedOnce=true;
0N/A if (proxyHost != null) {
0N/A setProxiedClient(url, proxyHost, proxyPort);
0N/A } else {
0N/A setNewClient (url);
0N/A }
0N/A ps = (PrintStream) http.getOutputStream();
0N/A connected=true;
0N/A responses = new MessageHeader();
0N/A setRequests=false;
0N/A writeRequests();
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Create a new HttpClient object, bypassing the cache of
0N/A * HTTP client objects/connections.
0N/A *
0N/A * @param url the URL being accessed
0N/A */
0N/A protected void setNewClient (URL url)
0N/A throws IOException {
0N/A setNewClient(url, false);
0N/A }
0N/A
0N/A /**
0N/A * Obtain a HttpsClient object. Use the cached copy if specified.
0N/A *
0N/A * @param url the URL being accessed
0N/A * @param useCache whether the cached connection should be used
0N/A * if present
0N/A */
0N/A protected void setNewClient (URL url, boolean useCache)
0N/A throws IOException {
5364N/A http = HttpClient.New(url, null, -1, useCache, connectTimeout, this);
0N/A http.setReadTimeout(readTimeout);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Create a new HttpClient object, set up so that it uses
0N/A * per-instance proxying to the given HTTP proxy. This
0N/A * bypasses the cache of HTTP client objects/connections.
0N/A *
0N/A * @param url the URL being accessed
0N/A * @param proxyHost the proxy host to use
0N/A * @param proxyPort the proxy port to use
0N/A */
0N/A protected void setProxiedClient (URL url, String proxyHost, int proxyPort)
0N/A throws IOException {
0N/A setProxiedClient(url, proxyHost, proxyPort, false);
0N/A }
0N/A
0N/A /**
0N/A * Obtain a HttpClient object, set up so that it uses per-instance
0N/A * proxying to the given HTTP proxy. Use the cached copy of HTTP
0N/A * client objects/connections if specified.
0N/A *
0N/A * @param url the URL being accessed
0N/A * @param proxyHost the proxy host to use
0N/A * @param proxyPort the proxy port to use
0N/A * @param useCache whether the cached connection should be used
0N/A * if present
0N/A */
0N/A protected void setProxiedClient (URL url,
0N/A String proxyHost, int proxyPort,
0N/A boolean useCache)
0N/A throws IOException {
0N/A proxiedConnect(url, proxyHost, proxyPort, useCache);
0N/A }
0N/A
0N/A protected void proxiedConnect(URL url,
0N/A String proxyHost, int proxyPort,
0N/A boolean useCache)
0N/A throws IOException {
5364N/A http = HttpClient.New (url, proxyHost, proxyPort, useCache,
5364N/A connectTimeout, this);
0N/A http.setReadTimeout(readTimeout);
0N/A }
0N/A
0N/A protected HttpURLConnection(URL u, Handler handler)
0N/A throws IOException {
0N/A // we set proxy == null to distinguish this case with the case
0N/A // when per connection proxy is set
0N/A this(u, null, handler);
0N/A }
0N/A
0N/A public HttpURLConnection(URL u, String host, int port) {
0N/A this(u, new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(host, port)));
0N/A }
0N/A
0N/A /** this constructor is used by other protocol handlers such as ftp
0N/A that want to use http to fetch urls on their behalf.*/
0N/A public HttpURLConnection(URL u, Proxy p) {
0N/A this(u, p, new Handler());
0N/A }
0N/A
0N/A protected HttpURLConnection(URL u, Proxy p, Handler handler) {
0N/A super(u);
0N/A requests = new MessageHeader();
0N/A responses = new MessageHeader();
0N/A this.handler = handler;
0N/A instProxy = p;
1503N/A if (instProxy instanceof sun.net.ApplicationProxy) {
1503N/A /* Application set Proxies should not have access to cookies
1503N/A * in a secure environment unless explicitly allowed. */
1503N/A try {
1503N/A cookieHandler = CookieHandler.getDefault();
1503N/A } catch (SecurityException se) { /* swallow exception */ }
1503N/A } else {
1503N/A cookieHandler = java.security.AccessController.doPrivileged(
1503N/A new java.security.PrivilegedAction<CookieHandler>() {
28N/A public CookieHandler run() {
1503N/A return CookieHandler.getDefault();
1503N/A }
1503N/A });
1503N/A }
28N/A cacheHandler = java.security.AccessController.doPrivileged(
28N/A new java.security.PrivilegedAction<ResponseCache>() {
28N/A public ResponseCache run() {
0N/A return ResponseCache.getDefault();
0N/A }
0N/A });
0N/A }
0N/A
0N/A /**
0N/A * @deprecated. Use java.net.Authenticator.setDefault() instead.
0N/A */
0N/A public static void setDefaultAuthenticator(HttpAuthenticator a) {
0N/A defaultAuth = a;
0N/A }
0N/A
0N/A /**
0N/A * opens a stream allowing redirects only to the same host.
0N/A */
0N/A public static InputStream openConnectionCheckRedirects(URLConnection c)
0N/A throws IOException
0N/A {
0N/A boolean redir;
0N/A int redirects = 0;
1313N/A InputStream in;
0N/A
0N/A do {
0N/A if (c instanceof HttpURLConnection) {
0N/A ((HttpURLConnection) c).setInstanceFollowRedirects(false);
0N/A }
0N/A
0N/A // We want to open the input stream before
0N/A // getting headers, because getHeaderField()
0N/A // et al swallow IOExceptions.
0N/A in = c.getInputStream();
0N/A redir = false;
0N/A
0N/A if (c instanceof HttpURLConnection) {
0N/A HttpURLConnection http = (HttpURLConnection) c;
0N/A int stat = http.getResponseCode();
0N/A if (stat >= 300 && stat <= 307 && stat != 306 &&
0N/A stat != HttpURLConnection.HTTP_NOT_MODIFIED) {
0N/A URL base = http.getURL();
0N/A String loc = http.getHeaderField("Location");
0N/A URL target = null;
0N/A if (loc != null) {
0N/A target = new URL(base, loc);
0N/A }
0N/A http.disconnect();
0N/A if (target == null
0N/A || !base.getProtocol().equals(target.getProtocol())
0N/A || base.getPort() != target.getPort()
0N/A || !hostsEqual(base, target)
0N/A || redirects >= 5)
0N/A {
0N/A throw new SecurityException("illegal URL redirect");
0N/A }
0N/A redir = true;
0N/A c = target.openConnection();
0N/A redirects++;
0N/A }
0N/A }
0N/A } while (redir);
0N/A return in;
0N/A }
0N/A
0N/A
0N/A //
0N/A // Same as java.net.URL.hostsEqual
0N/A //
0N/A private static boolean hostsEqual(URL u1, URL u2) {
0N/A final String h1 = u1.getHost();
0N/A final String h2 = u2.getHost();
0N/A
0N/A if (h1 == null) {
0N/A return h2 == null;
0N/A } else if (h2 == null) {
0N/A return false;
0N/A } else if (h1.equalsIgnoreCase(h2)) {
0N/A return true;
0N/A }
0N/A // Have to resolve addresses before comparing, otherwise
0N/A // names like tachyon and tachyon.eng would compare different
0N/A final boolean result[] = {false};
0N/A
0N/A java.security.AccessController.doPrivileged(
28N/A new java.security.PrivilegedAction<Void>() {
28N/A public Void run() {
0N/A try {
0N/A InetAddress a1 = InetAddress.getByName(h1);
0N/A InetAddress a2 = InetAddress.getByName(h2);
0N/A result[0] = a1.equals(a2);
0N/A } catch(UnknownHostException e) {
0N/A } catch(SecurityException e) {
0N/A }
0N/A return null;
0N/A }
0N/A });
0N/A
0N/A return result[0];
0N/A }
0N/A
0N/A // overridden in HTTPS subclass
0N/A
0N/A public void connect() throws IOException {
0N/A plainConnect();
0N/A }
0N/A
0N/A private boolean checkReuseConnection () {
0N/A if (connected) {
0N/A return true;
0N/A }
0N/A if (reuseClient != null) {
0N/A http = reuseClient;
0N/A http.setReadTimeout(getReadTimeout());
0N/A http.reuse = false;
0N/A reuseClient = null;
0N/A connected = true;
0N/A return true;
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A protected void plainConnect() throws IOException {
0N/A if (connected) {
0N/A return;
0N/A }
0N/A // try to see if request can be served from local cache
0N/A if (cacheHandler != null && getUseCaches()) {
0N/A try {
0N/A URI uri = ParseUtil.toURI(url);
0N/A if (uri != null) {
0N/A cachedResponse = cacheHandler.get(uri, getRequestMethod(), requests.getHeaders(EXCLUDE_HEADERS));
0N/A if ("https".equalsIgnoreCase(uri.getScheme())
0N/A && !(cachedResponse instanceof SecureCacheResponse)) {
0N/A cachedResponse = null;
0N/A }
1677N/A if (logger.isLoggable(PlatformLogger.FINEST)) {
1677N/A logger.finest("Cache Request for " + uri + " / " + getRequestMethod());
1677N/A logger.finest("From cache: " + (cachedResponse != null ? cachedResponse.toString() : "null"));
1313N/A }
0N/A if (cachedResponse != null) {
0N/A cachedHeaders = mapToMessageHeader(cachedResponse.getHeaders());
0N/A cachedInputStream = cachedResponse.getBody();
0N/A }
0N/A }
0N/A } catch (IOException ioex) {
0N/A // ignore and commence normal connection
0N/A }
0N/A if (cachedHeaders != null && cachedInputStream != null) {
0N/A connected = true;
0N/A return;
0N/A } else {
0N/A cachedResponse = null;
0N/A }
0N/A }
0N/A try {
0N/A /* Try to open connections using the following scheme,
0N/A * return on the first one that's successful:
0N/A * 1) if (instProxy != null)
0N/A * connect to instProxy; raise exception if failed
0N/A * 2) else use system default ProxySelector
0N/A * 3) is 2) fails, make direct connection
0N/A */
0N/A
0N/A if (instProxy == null) { // no instance Proxy is set
0N/A /**
0N/A * Do we have to use a proxy?
0N/A */
28N/A ProxySelector sel =
0N/A java.security.AccessController.doPrivileged(
28N/A new java.security.PrivilegedAction<ProxySelector>() {
28N/A public ProxySelector run() {
0N/A return ProxySelector.getDefault();
0N/A }
0N/A });
0N/A if (sel != null) {
0N/A URI uri = sun.net.www.ParseUtil.toURI(url);
1677N/A if (logger.isLoggable(PlatformLogger.FINEST)) {
1677N/A logger.finest("ProxySelector Request for " + uri);
1313N/A }
0N/A Iterator<Proxy> it = sel.select(uri).iterator();
1313N/A Proxy p;
0N/A while (it.hasNext()) {
0N/A p = it.next();
0N/A try {
0N/A if (!failedOnce) {
0N/A http = getNewHttpClient(url, p, connectTimeout);
0N/A http.setReadTimeout(readTimeout);
0N/A } else {
0N/A // make sure to construct new connection if first
0N/A // attempt failed
0N/A http = getNewHttpClient(url, p, connectTimeout, false);
0N/A http.setReadTimeout(readTimeout);
0N/A }
1677N/A if (logger.isLoggable(PlatformLogger.FINEST)) {
1313N/A if (p != null) {
1677N/A logger.finest("Proxy used: " + p.toString());
1313N/A }
1313N/A }
0N/A break;
0N/A } catch (IOException ioex) {
0N/A if (p != Proxy.NO_PROXY) {
0N/A sel.connectFailed(uri, p.address(), ioex);
0N/A if (!it.hasNext()) {
0N/A // fallback to direct connection
0N/A http = getNewHttpClient(url, null, connectTimeout, false);
0N/A http.setReadTimeout(readTimeout);
0N/A break;
0N/A }
0N/A } else {
0N/A throw ioex;
0N/A }
0N/A continue;
0N/A }
0N/A }
0N/A } else {
0N/A // No proxy selector, create http client with no proxy
0N/A if (!failedOnce) {
0N/A http = getNewHttpClient(url, null, connectTimeout);
0N/A http.setReadTimeout(readTimeout);
0N/A } else {
0N/A // make sure to construct new connection if first
0N/A // attempt failed
0N/A http = getNewHttpClient(url, null, connectTimeout, false);
0N/A http.setReadTimeout(readTimeout);
0N/A }
0N/A }
0N/A } else {
0N/A if (!failedOnce) {
0N/A http = getNewHttpClient(url, instProxy, connectTimeout);
0N/A http.setReadTimeout(readTimeout);
0N/A } else {
0N/A // make sure to construct new connection if first
0N/A // attempt failed
0N/A http = getNewHttpClient(url, instProxy, connectTimeout, false);
0N/A http.setReadTimeout(readTimeout);
0N/A }
0N/A }
0N/A
0N/A ps = (PrintStream)http.getOutputStream();
0N/A } catch (IOException e) {
0N/A throw e;
0N/A }
0N/A // constructor to HTTP client calls openserver
0N/A connected = true;
0N/A }
0N/A
0N/A // subclass HttpsClient will overwrite & return an instance of HttpsClient
0N/A protected HttpClient getNewHttpClient(URL url, Proxy p, int connectTimeout)
0N/A throws IOException {
5364N/A return HttpClient.New(url, p, connectTimeout, this);
0N/A }
0N/A
0N/A // subclass HttpsClient will overwrite & return an instance of HttpsClient
0N/A protected HttpClient getNewHttpClient(URL url, Proxy p,
0N/A int connectTimeout, boolean useCache)
0N/A throws IOException {
5364N/A return HttpClient.New(url, p, connectTimeout, useCache, this);
0N/A }
0N/A
1252N/A private void expect100Continue() throws IOException {
1252N/A // Expect: 100-Continue was set, so check the return code for
1252N/A // Acceptance
1252N/A int oldTimeout = http.getReadTimeout();
1252N/A boolean enforceTimeOut = false;
1252N/A boolean timedOut = false;
1252N/A if (oldTimeout <= 0) {
1252N/A // 5s read timeout in case the server doesn't understand
1252N/A // Expect: 100-Continue
1252N/A http.setReadTimeout(5000);
1252N/A enforceTimeOut = true;
1252N/A }
1252N/A
1252N/A try {
1252N/A http.parseHTTP(responses, pi, this);
1252N/A } catch (SocketTimeoutException se) {
1252N/A if (!enforceTimeOut) {
1252N/A throw se;
1252N/A }
1252N/A timedOut = true;
1252N/A http.setIgnoreContinue(true);
1252N/A }
1252N/A if (!timedOut) {
1252N/A // Can't use getResponseCode() yet
1252N/A String resp = responses.getValue(0);
1252N/A // Parse the response which is of the form:
1252N/A // HTTP/1.1 417 Expectation Failed
1252N/A // HTTP/1.1 100 Continue
1252N/A if (resp != null && resp.startsWith("HTTP/")) {
1252N/A String[] sa = resp.split("\\s+");
1252N/A responseCode = -1;
1252N/A try {
1252N/A // Response code is 2nd token on the line
1252N/A if (sa.length > 1)
1252N/A responseCode = Integer.parseInt(sa[1]);
1252N/A } catch (NumberFormatException numberFormatException) {
1252N/A }
1252N/A }
1252N/A if (responseCode != 100) {
1252N/A throw new ProtocolException("Server rejected operation");
1252N/A }
1252N/A }
2981N/A
2981N/A http.setReadTimeout(oldTimeout);
2981N/A
1252N/A responseCode = -1;
1252N/A responses.reset();
1252N/A // Proceed
1252N/A }
1252N/A
0N/A /*
0N/A * Allowable input/output sequences:
0N/A * [interpreted as POST/PUT]
0N/A * - get output, [write output,] get input, [read input]
0N/A * - get output, [write output]
0N/A * [interpreted as GET]
0N/A * - get input, [read input]
0N/A * Disallowed:
0N/A * - get input, [read input,] get output, [write output]
0N/A */
0N/A
51N/A @Override
0N/A public synchronized OutputStream getOutputStream() throws IOException {
0N/A
0N/A try {
0N/A if (!doOutput) {
0N/A throw new ProtocolException("cannot write to a URLConnection"
0N/A + " if doOutput=false - call setDoOutput(true)");
0N/A }
0N/A
0N/A if (method.equals("GET")) {
0N/A method = "POST"; // Backward compatibility
0N/A }
0N/A if (!"POST".equals(method) && !"PUT".equals(method) &&
0N/A "http".equals(url.getProtocol())) {
0N/A throw new ProtocolException("HTTP method " + method +
0N/A " doesn't support output");
0N/A }
0N/A
0N/A // if there's already an input stream open, throw an exception
0N/A if (inputStream != null) {
0N/A throw new ProtocolException("Cannot write output after reading input.");
0N/A }
0N/A
0N/A if (!checkReuseConnection())
0N/A connect();
0N/A
1252N/A boolean expectContinue = false;
1252N/A String expects = requests.findValue("Expect");
1252N/A if ("100-Continue".equalsIgnoreCase(expects)) {
1252N/A http.setIgnoreContinue(false);
1252N/A expectContinue = true;
1252N/A }
0N/A
0N/A if (streaming() && strOutputStream == null) {
0N/A writeRequests();
0N/A }
1252N/A
1252N/A if (expectContinue) {
1252N/A expect100Continue();
1252N/A }
0N/A ps = (PrintStream)http.getOutputStream();
0N/A if (streaming()) {
0N/A if (strOutputStream == null) {
705N/A if (chunkLength != -1) { /* chunked */
705N/A strOutputStream = new StreamingOutputStream(
705N/A new ChunkedOutputStream(ps, chunkLength), -1L);
705N/A } else { /* must be fixed content length */
705N/A long length = 0L;
705N/A if (fixedContentLengthLong != -1) {
705N/A length = fixedContentLengthLong;
705N/A } else if (fixedContentLength != -1) {
705N/A length = fixedContentLength;
705N/A }
705N/A strOutputStream = new StreamingOutputStream(ps, length);
0N/A }
0N/A }
0N/A return strOutputStream;
0N/A } else {
0N/A if (poster == null) {
0N/A poster = new PosterOutputStream();
0N/A }
0N/A return poster;
0N/A }
0N/A } catch (RuntimeException e) {
0N/A disconnectInternal();
0N/A throw e;
1252N/A } catch (ProtocolException e) {
1252N/A // Save the response code which may have been set while enforcing
1252N/A // the 100-continue. disconnectInternal() forces it to -1
1252N/A int i = responseCode;
1252N/A disconnectInternal();
1252N/A responseCode = i;
1252N/A throw e;
0N/A } catch (IOException e) {
0N/A disconnectInternal();
0N/A throw e;
0N/A }
0N/A }
0N/A
5364N/A public boolean streaming () {
705N/A return (fixedContentLength != -1) || (fixedContentLengthLong != -1) ||
705N/A (chunkLength != -1);
0N/A }
0N/A
0N/A /*
0N/A * get applicable cookies based on the uri and request headers
0N/A * add them to the existing request headers
0N/A */
0N/A private void setCookieHeader() throws IOException {
0N/A if (cookieHandler != null) {
0N/A // we only want to capture the user defined Cookies once, as
0N/A // they cannot be changed by user code after we are connected,
0N/A // only internally.
2910N/A synchronized (this) {
2910N/A if (setUserCookies) {
2910N/A int k = requests.getKey("Cookie");
2913N/A if (k != -1)
2910N/A userCookies = requests.getValue(k);
2910N/A k = requests.getKey("Cookie2");
2913N/A if (k != -1)
2910N/A userCookies2 = requests.getValue(k);
2910N/A setUserCookies = false;
2910N/A }
0N/A }
0N/A
0N/A // remove old Cookie header before setting new one.
0N/A requests.remove("Cookie");
2910N/A requests.remove("Cookie2");
0N/A
0N/A URI uri = ParseUtil.toURI(url);
0N/A if (uri != null) {
1677N/A if (logger.isLoggable(PlatformLogger.FINEST)) {
1677N/A logger.finest("CookieHandler request for " + uri);
1313N/A }
28N/A Map<String, List<String>> cookies
28N/A = cookieHandler.get(
28N/A uri, requests.getHeaders(EXCLUDE_HEADERS));
0N/A if (!cookies.isEmpty()) {
1677N/A if (logger.isLoggable(PlatformLogger.FINEST)) {
1677N/A logger.finest("Cookies retrieved: " + cookies.toString());
1313N/A }
28N/A for (Map.Entry<String, List<String>> entry :
28N/A cookies.entrySet()) {
28N/A String key = entry.getKey();
0N/A // ignore all entries that don't have "Cookie"
0N/A // or "Cookie2" as keys
0N/A if (!"Cookie".equalsIgnoreCase(key) &&
0N/A !"Cookie2".equalsIgnoreCase(key)) {
0N/A continue;
0N/A }
28N/A List<String> l = entry.getValue();
0N/A if (l != null && !l.isEmpty()) {
0N/A StringBuilder cookieValue = new StringBuilder();
28N/A for (String value : l) {
51N/A cookieValue.append(value).append("; ");
0N/A }
51N/A // strip off the trailing '; '
0N/A try {
51N/A requests.add(key, cookieValue.substring(0, cookieValue.length() - 2));
0N/A } catch (StringIndexOutOfBoundsException ignored) {
0N/A // no-op
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A if (userCookies != null) {
0N/A int k;
0N/A if ((k = requests.getKey("Cookie")) != -1)
0N/A requests.set("Cookie", requests.getValue(k) + ";" + userCookies);
0N/A else
0N/A requests.set("Cookie", userCookies);
0N/A }
2910N/A if (userCookies2 != null) {
2910N/A int k;
2911N/A if ((k = requests.getKey("Cookie2")) != -1)
2910N/A requests.set("Cookie2", requests.getValue(k) + ";" + userCookies2);
2910N/A else
2910N/A requests.set("Cookie2", userCookies2);
2910N/A }
0N/A
0N/A } // end of getting cookies
0N/A }
0N/A
51N/A @Override
51N/A @SuppressWarnings("empty-statement")
0N/A public synchronized InputStream getInputStream() throws IOException {
0N/A
0N/A if (!doInput) {
0N/A throw new ProtocolException("Cannot read from URLConnection"
0N/A + " if doInput=false (call setDoInput(true))");
0N/A }
0N/A
0N/A if (rememberedException != null) {
0N/A if (rememberedException instanceof RuntimeException)
0N/A throw new RuntimeException(rememberedException);
0N/A else {
0N/A throw getChainedException((IOException)rememberedException);
0N/A }
0N/A }
0N/A
0N/A if (inputStream != null) {
0N/A return inputStream;
0N/A }
0N/A
0N/A if (streaming() ) {
0N/A if (strOutputStream == null) {
0N/A getOutputStream();
0N/A }
0N/A /* make sure stream is closed */
0N/A strOutputStream.close ();
0N/A if (!strOutputStream.writtenOK()) {
0N/A throw new IOException ("Incomplete output stream");
0N/A }
0N/A }
0N/A
0N/A int redirects = 0;
0N/A int respCode = 0;
0N/A long cl = -1;
0N/A AuthenticationInfo serverAuthentication = null;
0N/A AuthenticationInfo proxyAuthentication = null;
0N/A AuthenticationHeader srvHdr = null;
0N/A
0N/A /**
0N/A * Failed Negotiate
0N/A *
0N/A * In some cases, the Negotiate auth is supported for the
0N/A * remote host but the negotiate process still fails (For
0N/A * example, if the web page is located on a backend server
0N/A * and delegation is needed but fails). The authentication
0N/A * process will start again, and we need to detect this
0N/A * kind of failure and do proper fallback (say, to NTLM).
0N/A *
0N/A * In order to achieve this, the inNegotiate flag is set
0N/A * when the first negotiate challenge is met (and reset
0N/A * if authentication is finished). If a fresh new negotiate
0N/A * challenge (no parameter) is found while inNegotiate is
0N/A * set, we know there's a failed auth attempt recently.
0N/A * Here we'll ignore the header line so that fallback
0N/A * can be practiced.
0N/A *
0N/A * inNegotiateProxy is for proxy authentication.
0N/A */
0N/A boolean inNegotiate = false;
0N/A boolean inNegotiateProxy = false;
0N/A
0N/A // If the user has set either of these headers then do not remove them
0N/A isUserServerAuth = requests.getKey("Authorization") != -1;
0N/A isUserProxyAuth = requests.getKey("Proxy-Authorization") != -1;
0N/A
0N/A try {
0N/A do {
0N/A if (!checkReuseConnection())
0N/A connect();
0N/A
0N/A if (cachedInputStream != null) {
0N/A return cachedInputStream;
0N/A }
0N/A
0N/A // Check if URL should be metered
0N/A boolean meteredInput = ProgressMonitor.getDefault().shouldMeterInput(url, method);
0N/A
0N/A if (meteredInput) {
0N/A pi = new ProgressSource(url, method);
0N/A pi.beginTracking();
0N/A }
0N/A
0N/A /* REMIND: This exists to fix the HttpsURLConnection subclass.
0N/A * Hotjava needs to run on JDK1.1FCS. Do proper fix once a
0N/A * proper solution for SSL can be found.
0N/A */
0N/A ps = (PrintStream)http.getOutputStream();
0N/A
0N/A if (!streaming()) {
0N/A writeRequests();
0N/A }
0N/A http.parseHTTP(responses, pi, this);
1677N/A if (logger.isLoggable(PlatformLogger.FINE)) {
1677N/A logger.fine(responses.toString());
0N/A }
5608N/A
5608N/A boolean b1 = responses.filterNTLMResponses("WWW-Authenticate");
5608N/A boolean b2 = responses.filterNTLMResponses("Proxy-Authenticate");
5608N/A if (b1 || b2) {
5608N/A if (logger.isLoggable(PlatformLogger.FINE)) {
5608N/A logger.fine(">>>> Headers are filtered");
5608N/A logger.fine(responses.toString());
5608N/A }
5608N/A }
5608N/A
0N/A inputStream = http.getInputStream();
0N/A
0N/A respCode = getResponseCode();
1754N/A if (respCode == -1) {
1754N/A disconnectInternal();
1754N/A throw new IOException ("Invalid Http response");
1754N/A }
0N/A if (respCode == HTTP_PROXY_AUTH) {
0N/A if (streaming()) {
0N/A disconnectInternal();
0N/A throw new HttpRetryException (
0N/A RETRY_MSG1, HTTP_PROXY_AUTH);
0N/A }
0N/A
0N/A // Read comments labeled "Failed Negotiate" for details.
0N/A boolean dontUseNegotiate = false;
0N/A Iterator iter = responses.multiValueIterator("Proxy-Authenticate");
0N/A while (iter.hasNext()) {
0N/A String value = ((String)iter.next()).trim();
0N/A if (value.equalsIgnoreCase("Negotiate") ||
0N/A value.equalsIgnoreCase("Kerberos")) {
0N/A if (!inNegotiateProxy) {
0N/A inNegotiateProxy = true;
0N/A } else {
0N/A dontUseNegotiate = true;
0N/A doingNTLMp2ndStage = false;
0N/A proxyAuthentication = null;
0N/A }
0N/A break;
0N/A }
0N/A }
0N/A
0N/A // changes: add a 3rd parameter to the constructor of
0N/A // AuthenticationHeader, so that NegotiateAuthentication.
0N/A // isSupported can be tested.
0N/A // The other 2 appearances of "new AuthenticationHeader" is
0N/A // altered in similar ways.
0N/A
0N/A AuthenticationHeader authhdr = new AuthenticationHeader (
0N/A "Proxy-Authenticate", responses,
1266N/A new HttpCallerInfo(url, http.getProxyHostUsed(),
1266N/A http.getProxyPortUsed()),
1266N/A dontUseNegotiate
0N/A );
0N/A
0N/A if (!doingNTLMp2ndStage) {
0N/A proxyAuthentication =
0N/A resetProxyAuthentication(proxyAuthentication, authhdr);
0N/A if (proxyAuthentication != null) {
0N/A redirects++;
0N/A disconnectInternal();
0N/A continue;
0N/A }
0N/A } else {
0N/A /* in this case, only one header field will be present */
0N/A String raw = responses.findValue ("Proxy-Authenticate");
0N/A reset ();
0N/A if (!proxyAuthentication.setHeaders(this,
0N/A authhdr.headerParser(), raw)) {
0N/A disconnectInternal();
0N/A throw new IOException ("Authentication failure");
0N/A }
0N/A if (serverAuthentication != null && srvHdr != null &&
0N/A !serverAuthentication.setHeaders(this,
0N/A srvHdr.headerParser(), raw)) {
0N/A disconnectInternal ();
0N/A throw new IOException ("Authentication failure");
0N/A }
1315N/A authObj = null;
0N/A doingNTLMp2ndStage = false;
0N/A continue;
0N/A }
2240N/A } else {
2240N/A inNegotiateProxy = false;
2240N/A doingNTLMp2ndStage = false;
2240N/A if (!isUserProxyAuth)
2240N/A requests.remove("Proxy-Authorization");
0N/A }
0N/A
0N/A // cache proxy authentication info
0N/A if (proxyAuthentication != null) {
0N/A // cache auth info on success, domain header not relevant.
0N/A proxyAuthentication.addToCache();
0N/A }
0N/A
0N/A if (respCode == HTTP_UNAUTHORIZED) {
0N/A if (streaming()) {
0N/A disconnectInternal();
0N/A throw new HttpRetryException (
0N/A RETRY_MSG2, HTTP_UNAUTHORIZED);
0N/A }
0N/A
0N/A // Read comments labeled "Failed Negotiate" for details.
0N/A boolean dontUseNegotiate = false;
0N/A Iterator iter = responses.multiValueIterator("WWW-Authenticate");
0N/A while (iter.hasNext()) {
0N/A String value = ((String)iter.next()).trim();
0N/A if (value.equalsIgnoreCase("Negotiate") ||
0N/A value.equalsIgnoreCase("Kerberos")) {
0N/A if (!inNegotiate) {
0N/A inNegotiate = true;
0N/A } else {
0N/A dontUseNegotiate = true;
0N/A doingNTLM2ndStage = false;
0N/A serverAuthentication = null;
0N/A }
0N/A break;
0N/A }
0N/A }
0N/A
0N/A srvHdr = new AuthenticationHeader (
0N/A "WWW-Authenticate", responses,
1266N/A new HttpCallerInfo(url),
1266N/A dontUseNegotiate
0N/A );
0N/A
0N/A String raw = srvHdr.raw();
0N/A if (!doingNTLM2ndStage) {
0N/A if ((serverAuthentication != null)&&
1670N/A serverAuthentication.getAuthScheme() != NTLM) {
0N/A if (serverAuthentication.isAuthorizationStale (raw)) {
0N/A /* we can retry with the current credentials */
2240N/A disconnectWeb();
0N/A redirects++;
0N/A requests.set(serverAuthentication.getHeaderName(),
0N/A serverAuthentication.getHeaderValue(url, method));
0N/A currentServerCredentials = serverAuthentication;
0N/A setCookieHeader();
0N/A continue;
0N/A } else {
0N/A serverAuthentication.removeFromCache();
0N/A }
0N/A }
0N/A serverAuthentication = getServerAuthentication(srvHdr);
0N/A currentServerCredentials = serverAuthentication;
0N/A
0N/A if (serverAuthentication != null) {
2240N/A disconnectWeb();
0N/A redirects++; // don't let things loop ad nauseum
0N/A setCookieHeader();
0N/A continue;
0N/A }
0N/A } else {
0N/A reset ();
0N/A /* header not used for ntlm */
0N/A if (!serverAuthentication.setHeaders(this, null, raw)) {
2240N/A disconnectWeb();
0N/A throw new IOException ("Authentication failure");
0N/A }
0N/A doingNTLM2ndStage = false;
1315N/A authObj = null;
0N/A setCookieHeader();
0N/A continue;
0N/A }
0N/A }
0N/A // cache server authentication info
0N/A if (serverAuthentication != null) {
0N/A // cache auth info on success
0N/A if (!(serverAuthentication instanceof DigestAuthentication) ||
0N/A (domain == null)) {
0N/A if (serverAuthentication instanceof BasicAuthentication) {
0N/A // check if the path is shorter than the existing entry
0N/A String npath = AuthenticationInfo.reducePath (url.getPath());
0N/A String opath = serverAuthentication.path;
0N/A if (!opath.startsWith (npath) || npath.length() >= opath.length()) {
0N/A /* npath is longer, there must be a common root */
0N/A npath = BasicAuthentication.getRootPath (opath, npath);
0N/A }
0N/A // remove the entry and create a new one
0N/A BasicAuthentication a =
0N/A (BasicAuthentication) serverAuthentication.clone();
0N/A serverAuthentication.removeFromCache();
0N/A a.path = npath;
0N/A serverAuthentication = a;
0N/A }
0N/A serverAuthentication.addToCache();
0N/A } else {
0N/A // what we cache is based on the domain list in the request
0N/A DigestAuthentication srv = (DigestAuthentication)
0N/A serverAuthentication;
0N/A StringTokenizer tok = new StringTokenizer (domain," ");
0N/A String realm = srv.realm;
0N/A PasswordAuthentication pw = srv.pw;
0N/A digestparams = srv.params;
0N/A while (tok.hasMoreTokens()) {
0N/A String path = tok.nextToken();
0N/A try {
0N/A /* path could be an abs_path or a complete URI */
0N/A URL u = new URL (url, path);
0N/A DigestAuthentication d = new DigestAuthentication (
0N/A false, u, realm, "Digest", pw, digestparams);
0N/A d.addToCache ();
0N/A } catch (Exception e) {}
0N/A }
0N/A }
0N/A }
0N/A
0N/A // some flags should be reset to its initialized form so that
0N/A // even after a redirect the necessary checks can still be
0N/A // preformed.
0N/A inNegotiate = false;
0N/A inNegotiateProxy = false;
0N/A
0N/A //serverAuthentication = null;
0N/A doingNTLMp2ndStage = false;
0N/A doingNTLM2ndStage = false;
0N/A if (!isUserServerAuth)
0N/A requests.remove("Authorization");
0N/A if (!isUserProxyAuth)
0N/A requests.remove("Proxy-Authorization");
0N/A
0N/A if (respCode == HTTP_OK) {
0N/A checkResponseCredentials (false);
0N/A } else {
0N/A needToCheck = false;
0N/A }
0N/A
0N/A // a flag need to clean
0N/A needToCheck = true;
0N/A
0N/A if (followRedirect()) {
0N/A /* if we should follow a redirect, then the followRedirects()
0N/A * method will disconnect() and re-connect us to the new
0N/A * location
0N/A */
0N/A redirects++;
0N/A
0N/A // redirecting HTTP response may have set cookie, so
0N/A // need to re-generate request header
0N/A setCookieHeader();
0N/A
0N/A continue;
0N/A }
0N/A
0N/A try {
0N/A cl = Long.parseLong(responses.findValue("content-length"));
0N/A } catch (Exception exc) { };
0N/A
0N/A if (method.equals("HEAD") || cl == 0 ||
0N/A respCode == HTTP_NOT_MODIFIED ||
0N/A respCode == HTTP_NO_CONTENT) {
0N/A
0N/A if (pi != null) {
0N/A pi.finishTracking();
0N/A pi = null;
0N/A }
0N/A http.finished();
0N/A http = null;
0N/A inputStream = new EmptyInputStream();
0N/A connected = false;
0N/A }
0N/A
0N/A if (respCode == 200 || respCode == 203 || respCode == 206 ||
0N/A respCode == 300 || respCode == 301 || respCode == 410) {
0N/A if (cacheHandler != null) {
0N/A // give cache a chance to save response in cache
0N/A URI uri = ParseUtil.toURI(url);
0N/A if (uri != null) {
0N/A URLConnection uconn = this;
0N/A if ("https".equalsIgnoreCase(uri.getScheme())) {
0N/A try {
0N/A // use reflection to get to the public
0N/A // HttpsURLConnection instance saved in
0N/A // DelegateHttpsURLConnection
0N/A uconn = (URLConnection)this.getClass().getField("httpsURLConnection").get(this);
0N/A } catch (IllegalAccessException iae) {
0N/A // ignored; use 'this'
0N/A } catch (NoSuchFieldException nsfe) {
0N/A // ignored; use 'this'
0N/A }
0N/A }
0N/A CacheRequest cacheRequest =
0N/A cacheHandler.put(uri, uconn);
0N/A if (cacheRequest != null && http != null) {
0N/A http.setCacheRequest(cacheRequest);
0N/A inputStream = new HttpInputStream(inputStream, cacheRequest);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (!(inputStream instanceof HttpInputStream)) {
0N/A inputStream = new HttpInputStream(inputStream);
0N/A }
0N/A
0N/A if (respCode >= 400) {
0N/A if (respCode == 404 || respCode == 410) {
0N/A throw new FileNotFoundException(url.toString());
0N/A } else {
0N/A throw new java.io.IOException("Server returned HTTP" +
0N/A " response code: " + respCode + " for URL: " +
0N/A url.toString());
0N/A }
0N/A }
0N/A poster = null;
0N/A strOutputStream = null;
0N/A return inputStream;
0N/A } while (redirects < maxRedirects);
0N/A
0N/A throw new ProtocolException("Server redirected too many " +
0N/A " times ("+ redirects + ")");
0N/A } catch (RuntimeException e) {
0N/A disconnectInternal();
0N/A rememberedException = e;
0N/A throw e;
0N/A } catch (IOException e) {
0N/A rememberedException = e;
0N/A
0N/A // buffer the error stream if bytes < 4k
0N/A // and it can be buffered within 1 second
0N/A String te = responses.findValue("Transfer-Encoding");
0N/A if (http != null && http.isKeepingAlive() && enableESBuffer &&
0N/A (cl > 0 || (te != null && te.equalsIgnoreCase("chunked")))) {
0N/A errorStream = ErrorStream.getErrorStream(inputStream, cl, http);
0N/A }
0N/A throw e;
0N/A } finally {
2278N/A if (proxyAuthKey != null) {
2278N/A AuthenticationInfo.endAuthRequest(proxyAuthKey);
0N/A }
2278N/A if (serverAuthKey != null) {
2278N/A AuthenticationInfo.endAuthRequest(serverAuthKey);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Creates a chained exception that has the same type as
0N/A * original exception and with the same message. Right now,
0N/A * there is no convenient APIs for doing so.
0N/A */
28N/A private IOException getChainedException(final IOException rememberedException) {
0N/A try {
28N/A final Object[] args = { rememberedException.getMessage() };
28N/A IOException chainedException =
28N/A java.security.AccessController.doPrivileged(
28N/A new java.security.PrivilegedExceptionAction<IOException>() {
28N/A public IOException run() throws Exception {
28N/A return (IOException)
28N/A rememberedException.getClass()
28N/A .getConstructor(new Class[] { String.class })
28N/A .newInstance(args);
0N/A }
0N/A });
28N/A chainedException.initCause(rememberedException);
0N/A return chainedException;
0N/A } catch (Exception ignored) {
0N/A return rememberedException;
0N/A }
0N/A }
0N/A
51N/A @Override
0N/A public InputStream getErrorStream() {
0N/A if (connected && responseCode >= 400) {
0N/A // Client Error 4xx and Server Error 5xx
0N/A if (errorStream != null) {
0N/A return errorStream;
0N/A } else if (inputStream != null) {
0N/A return inputStream;
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * set or reset proxy authentication info in request headers
0N/A * after receiving a 407 error. In the case of NTLM however,
0N/A * receiving a 407 is normal and we just skip the stale check
0N/A * because ntlm does not support this feature.
0N/A */
0N/A private AuthenticationInfo
1784N/A resetProxyAuthentication(AuthenticationInfo proxyAuthentication, AuthenticationHeader auth) throws IOException {
1670N/A if ((proxyAuthentication != null )&&
1670N/A proxyAuthentication.getAuthScheme() != NTLM) {
0N/A String raw = auth.raw();
0N/A if (proxyAuthentication.isAuthorizationStale (raw)) {
0N/A /* we can retry with the current credentials */
256N/A String value;
1710N/A if (proxyAuthentication instanceof DigestAuthentication) {
1710N/A DigestAuthentication digestProxy = (DigestAuthentication)
1710N/A proxyAuthentication;
1710N/A if (tunnelState() == TunnelState.SETUP) {
1710N/A value = digestProxy.getHeaderValue(connectRequestURI(url), HTTP_CONNECT);
1710N/A } else {
1710N/A value = digestProxy.getHeaderValue(getRequestURI(), method);
1710N/A }
256N/A } else {
256N/A value = proxyAuthentication.getHeaderValue(url, method);
256N/A }
256N/A requests.set(proxyAuthentication.getHeaderName(), value);
0N/A currentProxyCredentials = proxyAuthentication;
256N/A return proxyAuthentication;
0N/A } else {
0N/A proxyAuthentication.removeFromCache();
0N/A }
0N/A }
0N/A proxyAuthentication = getHttpProxyAuthentication(auth);
0N/A currentProxyCredentials = proxyAuthentication;
0N/A return proxyAuthentication;
0N/A }
0N/A
0N/A /**
256N/A * Returns the tunnel state.
256N/A *
256N/A * @return the state
256N/A */
256N/A TunnelState tunnelState() {
256N/A return tunnelState;
256N/A }
256N/A
256N/A /**
256N/A * Set the tunneling status.
256N/A *
256N/A * @param the state
256N/A */
5365N/A public void setTunnelState(TunnelState tunnelState) {
256N/A this.tunnelState = tunnelState;
256N/A }
256N/A
256N/A /**
0N/A * establish a tunnel through proxy server
0N/A */
0N/A public synchronized void doTunneling() throws IOException {
0N/A int retryTunnel = 0;
0N/A String statusLine = "";
0N/A int respCode = 0;
0N/A AuthenticationInfo proxyAuthentication = null;
0N/A String proxyHost = null;
0N/A int proxyPort = -1;
0N/A
0N/A // save current requests so that they can be restored after tunnel is setup.
0N/A MessageHeader savedRequests = requests;
0N/A requests = new MessageHeader();
0N/A
0N/A // Read comments labeled "Failed Negotiate" for details.
0N/A boolean inNegotiateProxy = false;
0N/A
0N/A try {
256N/A /* Actively setting up a tunnel */
256N/A setTunnelState(TunnelState.SETUP);
256N/A
0N/A do {
0N/A if (!checkReuseConnection()) {
0N/A proxiedConnect(url, proxyHost, proxyPort, false);
0N/A }
0N/A // send the "CONNECT" request to establish a tunnel
0N/A // through proxy server
0N/A sendCONNECTRequest();
0N/A responses.reset();
0N/A
0N/A // There is no need to track progress in HTTP Tunneling,
0N/A // so ProgressSource is null.
0N/A http.parseHTTP(responses, null, this);
0N/A
255N/A /* Log the response to the CONNECT */
1677N/A if (logger.isLoggable(PlatformLogger.FINE)) {
1677N/A logger.fine(responses.toString());
1313N/A }
255N/A
5608N/A if (responses.filterNTLMResponses("Proxy-Authenticate")) {
5608N/A if (logger.isLoggable(PlatformLogger.FINE)) {
5608N/A logger.fine(">>>> Headers are filtered");
5608N/A logger.fine(responses.toString());
5608N/A }
5608N/A }
5608N/A
0N/A statusLine = responses.getValue(0);
0N/A StringTokenizer st = new StringTokenizer(statusLine);
0N/A st.nextToken();
0N/A respCode = Integer.parseInt(st.nextToken().trim());
0N/A if (respCode == HTTP_PROXY_AUTH) {
0N/A // Read comments labeled "Failed Negotiate" for details.
0N/A boolean dontUseNegotiate = false;
0N/A Iterator iter = responses.multiValueIterator("Proxy-Authenticate");
0N/A while (iter.hasNext()) {
0N/A String value = ((String)iter.next()).trim();
0N/A if (value.equalsIgnoreCase("Negotiate") ||
0N/A value.equalsIgnoreCase("Kerberos")) {
0N/A if (!inNegotiateProxy) {
0N/A inNegotiateProxy = true;
0N/A } else {
0N/A dontUseNegotiate = true;
0N/A doingNTLMp2ndStage = false;
0N/A proxyAuthentication = null;
0N/A }
0N/A break;
0N/A }
0N/A }
0N/A
0N/A AuthenticationHeader authhdr = new AuthenticationHeader (
0N/A "Proxy-Authenticate", responses,
1266N/A new HttpCallerInfo(url, http.getProxyHostUsed(),
1266N/A http.getProxyPortUsed()),
1266N/A dontUseNegotiate
0N/A );
0N/A if (!doingNTLMp2ndStage) {
0N/A proxyAuthentication =
0N/A resetProxyAuthentication(proxyAuthentication, authhdr);
0N/A if (proxyAuthentication != null) {
0N/A proxyHost = http.getProxyHostUsed();
0N/A proxyPort = http.getProxyPortUsed();
0N/A disconnectInternal();
0N/A retryTunnel++;
0N/A continue;
0N/A }
0N/A } else {
0N/A String raw = responses.findValue ("Proxy-Authenticate");
0N/A reset ();
0N/A if (!proxyAuthentication.setHeaders(this,
0N/A authhdr.headerParser(), raw)) {
0N/A disconnectInternal();
0N/A throw new IOException ("Authentication failure");
0N/A }
1315N/A authObj = null;
0N/A doingNTLMp2ndStage = false;
0N/A continue;
0N/A }
0N/A }
0N/A // cache proxy authentication info
0N/A if (proxyAuthentication != null) {
0N/A // cache auth info on success, domain header not relevant.
0N/A proxyAuthentication.addToCache();
0N/A }
0N/A
0N/A if (respCode == HTTP_OK) {
256N/A setTunnelState(TunnelState.TUNNELING);
0N/A break;
0N/A }
0N/A // we don't know how to deal with other response code
0N/A // so disconnect and report error
0N/A disconnectInternal();
256N/A setTunnelState(TunnelState.NONE);
0N/A break;
0N/A } while (retryTunnel < maxRedirects);
0N/A
0N/A if (retryTunnel >= maxRedirects || (respCode != HTTP_OK)) {
0N/A throw new IOException("Unable to tunnel through proxy."+
0N/A " Proxy returns \"" +
0N/A statusLine + "\"");
0N/A }
0N/A } finally {
2278N/A if (proxyAuthKey != null) {
2278N/A AuthenticationInfo.endAuthRequest(proxyAuthKey);
0N/A }
0N/A }
0N/A
0N/A // restore original request headers
0N/A requests = savedRequests;
0N/A
0N/A // reset responses
0N/A responses.reset();
0N/A }
0N/A
256N/A static String connectRequestURI(URL url) {
256N/A String host = url.getHost();
256N/A int port = url.getPort();
256N/A port = port != -1 ? port : url.getDefaultPort();
256N/A
256N/A return host + ":" + port;
256N/A }
256N/A
0N/A /**
0N/A * send a CONNECT request for establishing a tunnel to proxy server
0N/A */
0N/A private void sendCONNECTRequest() throws IOException {
0N/A int port = url.getPort();
0N/A
4370N/A requests.set(0, HTTP_CONNECT + " " + connectRequestURI(url)
0N/A + " " + httpVersion, null);
0N/A requests.setIfNotSet("User-Agent", userAgent);
0N/A
0N/A String host = url.getHost();
0N/A if (port != -1 && port != url.getDefaultPort()) {
0N/A host += ":" + String.valueOf(port);
0N/A }
0N/A requests.setIfNotSet("Host", host);
0N/A
0N/A // Not really necessary for a tunnel, but can't hurt
0N/A requests.setIfNotSet("Accept", acceptString);
0N/A
2646N/A if (http.getHttpKeepAliveSet()) {
2646N/A requests.setIfNotSet("Proxy-Connection", "keep-alive");
2646N/A }
2646N/A
0N/A setPreemptiveProxyAuthentication(requests);
255N/A
255N/A /* Log the CONNECT request */
1677N/A if (logger.isLoggable(PlatformLogger.FINE)) {
1677N/A logger.fine(requests.toString());
1313N/A }
255N/A
0N/A http.writeRequests(requests, null);
0N/A }
0N/A
0N/A /**
0N/A * Sets pre-emptive proxy authentication in header
0N/A */
1784N/A private void setPreemptiveProxyAuthentication(MessageHeader requests) throws IOException {
0N/A AuthenticationInfo pauth
0N/A = AuthenticationInfo.getProxyAuth(http.getProxyHostUsed(),
0N/A http.getProxyPortUsed());
0N/A if (pauth != null && pauth.supportsPreemptiveAuthorization()) {
256N/A String value;
1710N/A if (pauth instanceof DigestAuthentication) {
1710N/A DigestAuthentication digestProxy = (DigestAuthentication) pauth;
1710N/A if (tunnelState() == TunnelState.SETUP) {
1710N/A value = digestProxy
256N/A .getHeaderValue(connectRequestURI(url), HTTP_CONNECT);
1710N/A } else {
1710N/A value = digestProxy.getHeaderValue(getRequestURI(), method);
1710N/A }
256N/A } else {
256N/A value = pauth.getHeaderValue(url, method);
256N/A }
256N/A
0N/A // Sets "Proxy-authorization"
256N/A requests.set(pauth.getHeaderName(), value);
0N/A currentProxyCredentials = pauth;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Gets the authentication for an HTTP proxy, and applies it to
0N/A * the connection.
0N/A */
0N/A private AuthenticationInfo getHttpProxyAuthentication (AuthenticationHeader authhdr) {
0N/A /* get authorization from authenticator */
0N/A AuthenticationInfo ret = null;
0N/A String raw = authhdr.raw();
0N/A String host = http.getProxyHostUsed();
0N/A int port = http.getProxyPortUsed();
0N/A if (host != null && authhdr.isPresent()) {
0N/A HeaderParser p = authhdr.headerParser();
0N/A String realm = p.findValue("realm");
0N/A String scheme = authhdr.scheme();
1670N/A AuthScheme authScheme = UNKNOWN;
0N/A if ("basic".equalsIgnoreCase(scheme)) {
1670N/A authScheme = BASIC;
0N/A } else if ("digest".equalsIgnoreCase(scheme)) {
1670N/A authScheme = DIGEST;
0N/A } else if ("ntlm".equalsIgnoreCase(scheme)) {
1670N/A authScheme = NTLM;
0N/A doingNTLMp2ndStage = true;
0N/A } else if ("Kerberos".equalsIgnoreCase(scheme)) {
1670N/A authScheme = KERBEROS;
0N/A doingNTLMp2ndStage = true;
0N/A } else if ("Negotiate".equalsIgnoreCase(scheme)) {
1670N/A authScheme = NEGOTIATE;
0N/A doingNTLMp2ndStage = true;
0N/A }
1670N/A
0N/A if (realm == null)
0N/A realm = "";
2278N/A proxyAuthKey = AuthenticationInfo.getProxyAuthKey(host, port, realm, authScheme);
2278N/A ret = AuthenticationInfo.getProxyAuth(proxyAuthKey);
0N/A if (ret == null) {
1670N/A switch (authScheme) {
1670N/A case BASIC:
0N/A InetAddress addr = null;
0N/A try {
0N/A final String finalHost = host;
28N/A addr = java.security.AccessController.doPrivileged(
28N/A new java.security.PrivilegedExceptionAction<InetAddress>() {
28N/A public InetAddress run()
0N/A throws java.net.UnknownHostException {
0N/A return InetAddress.getByName(finalHost);
0N/A }
0N/A });
0N/A } catch (java.security.PrivilegedActionException ignored) {
0N/A // User will have an unknown host.
0N/A }
0N/A PasswordAuthentication a =
0N/A privilegedRequestPasswordAuthentication(
0N/A host, addr, port, "http",
0N/A realm, scheme, url, RequestorType.PROXY);
0N/A if (a != null) {
0N/A ret = new BasicAuthentication(true, host, port, realm, a);
0N/A }
1670N/A break;
1670N/A case DIGEST:
1670N/A a = privilegedRequestPasswordAuthentication(
0N/A host, null, port, url.getProtocol(),
0N/A realm, scheme, url, RequestorType.PROXY);
0N/A if (a != null) {
0N/A DigestAuthentication.Parameters params =
0N/A new DigestAuthentication.Parameters();
0N/A ret = new DigestAuthentication(true, host, port, realm,
0N/A scheme, a, params);
0N/A }
1670N/A break;
1670N/A case NTLM:
1670N/A if (NTLMAuthenticationProxy.proxy.supported) {
1670N/A /* tryTransparentNTLMProxy will always be true the first
1670N/A * time around, but verify that the platform supports it
1670N/A * otherwise don't try. */
1670N/A if (tryTransparentNTLMProxy) {
1670N/A tryTransparentNTLMProxy =
1670N/A NTLMAuthenticationProxy.proxy.supportsTransparentAuth;
1670N/A }
1670N/A a = null;
1670N/A if (tryTransparentNTLMProxy) {
1677N/A logger.finest("Trying Transparent NTLM authentication");
1670N/A } else {
1670N/A a = privilegedRequestPasswordAuthentication(
1670N/A host, null, port, url.getProtocol(),
1670N/A "", scheme, url, RequestorType.PROXY);
1670N/A }
1670N/A /* If we are not trying transparent authentication then
1670N/A * we need to have a PasswordAuthentication instance. For
1670N/A * transparent authentication (Windows only) the username
1670N/A * and password will be picked up from the current logged
1670N/A * on users credentials.
1670N/A */
1670N/A if (tryTransparentNTLMProxy ||
1670N/A (!tryTransparentNTLMProxy && a != null)) {
1670N/A ret = NTLMAuthenticationProxy.proxy.create(true, host, port, a);
1670N/A }
1670N/A
1670N/A /* set to false so that we do not try again */
1670N/A tryTransparentNTLMProxy = false;
0N/A }
1670N/A break;
1670N/A case NEGOTIATE:
1266N/A ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
1670N/A break;
1670N/A case KERBEROS:
1266N/A ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
1670N/A break;
1670N/A case UNKNOWN:
1677N/A logger.finest("Unknown/Unsupported authentication scheme: " + scheme);
1670N/A default:
1670N/A throw new AssertionError("should not reach here");
0N/A }
0N/A }
0N/A // For backwards compatibility, we also try defaultAuth
0N/A // REMIND: Get rid of this for JDK2.0.
0N/A
0N/A if (ret == null && defaultAuth != null
0N/A && defaultAuth.schemeSupported(scheme)) {
0N/A try {
0N/A URL u = new URL("http", host, port, "/");
0N/A String a = defaultAuth.authString(u, scheme, realm);
0N/A if (a != null) {
0N/A ret = new BasicAuthentication (true, host, port, realm, a);
0N/A // not in cache by default - cache on success
0N/A }
0N/A } catch (java.net.MalformedURLException ignored) {
0N/A }
0N/A }
0N/A if (ret != null) {
0N/A if (!ret.setHeaders(this, p, raw)) {
0N/A ret = null;
0N/A }
0N/A }
0N/A }
1677N/A if (logger.isLoggable(PlatformLogger.FINER)) {
1677N/A logger.finer("Proxy Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
1313N/A }
0N/A return ret;
0N/A }
0N/A
0N/A /**
0N/A * Gets the authentication for an HTTP server, and applies it to
0N/A * the connection.
0N/A * @param authHdr the AuthenticationHeader which tells what auth scheme is
0N/A * prefered.
0N/A */
0N/A private AuthenticationInfo getServerAuthentication (AuthenticationHeader authhdr) {
0N/A /* get authorization from authenticator */
0N/A AuthenticationInfo ret = null;
0N/A String raw = authhdr.raw();
0N/A /* When we get an NTLM auth from cache, don't set any special headers */
0N/A if (authhdr.isPresent()) {
0N/A HeaderParser p = authhdr.headerParser();
0N/A String realm = p.findValue("realm");
0N/A String scheme = authhdr.scheme();
1670N/A AuthScheme authScheme = UNKNOWN;
0N/A if ("basic".equalsIgnoreCase(scheme)) {
1670N/A authScheme = BASIC;
0N/A } else if ("digest".equalsIgnoreCase(scheme)) {
1670N/A authScheme = DIGEST;
0N/A } else if ("ntlm".equalsIgnoreCase(scheme)) {
1670N/A authScheme = NTLM;
0N/A doingNTLM2ndStage = true;
0N/A } else if ("Kerberos".equalsIgnoreCase(scheme)) {
1670N/A authScheme = KERBEROS;
0N/A doingNTLM2ndStage = true;
0N/A } else if ("Negotiate".equalsIgnoreCase(scheme)) {
1670N/A authScheme = NEGOTIATE;
0N/A doingNTLM2ndStage = true;
0N/A }
1670N/A
0N/A domain = p.findValue ("domain");
0N/A if (realm == null)
0N/A realm = "";
2278N/A serverAuthKey = AuthenticationInfo.getServerAuthKey(url, realm, authScheme);
2278N/A ret = AuthenticationInfo.getServerAuth(serverAuthKey);
0N/A InetAddress addr = null;
0N/A if (ret == null) {
0N/A try {
0N/A addr = InetAddress.getByName(url.getHost());
0N/A } catch (java.net.UnknownHostException ignored) {
0N/A // User will have addr = null
0N/A }
0N/A }
0N/A // replacing -1 with default port for a protocol
0N/A int port = url.getPort();
0N/A if (port == -1) {
0N/A port = url.getDefaultPort();
0N/A }
0N/A if (ret == null) {
1670N/A switch(authScheme) {
1670N/A case KERBEROS:
1266N/A ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Kerberos"));
1670N/A break;
1670N/A case NEGOTIATE:
1266N/A ret = new NegotiateAuthentication(new HttpCallerInfo(authhdr.getHttpCallerInfo(), "Negotiate"));
1670N/A break;
1670N/A case BASIC:
0N/A PasswordAuthentication a =
0N/A privilegedRequestPasswordAuthentication(
0N/A url.getHost(), addr, port, url.getProtocol(),
0N/A realm, scheme, url, RequestorType.SERVER);
0N/A if (a != null) {
0N/A ret = new BasicAuthentication(false, url, realm, a);
0N/A }
1670N/A break;
1670N/A case DIGEST:
1670N/A a = privilegedRequestPasswordAuthentication(
0N/A url.getHost(), addr, port, url.getProtocol(),
0N/A realm, scheme, url, RequestorType.SERVER);
0N/A if (a != null) {
0N/A digestparams = new DigestAuthentication.Parameters();
0N/A ret = new DigestAuthentication(false, url, realm, scheme, a, digestparams);
0N/A }
1670N/A break;
1670N/A case NTLM:
1670N/A if (NTLMAuthenticationProxy.proxy.supported) {
1670N/A URL url1;
1670N/A try {
1670N/A url1 = new URL (url, "/"); /* truncate the path */
1670N/A } catch (Exception e) {
1670N/A url1 = url;
1670N/A }
0N/A
1670N/A /* tryTransparentNTLMServer will always be true the first
1670N/A * time around, but verify that the platform supports it
1670N/A * otherwise don't try. */
1670N/A if (tryTransparentNTLMServer) {
1670N/A tryTransparentNTLMServer =
1670N/A NTLMAuthenticationProxy.proxy.supportsTransparentAuth;
3786N/A /* If the platform supports transparent authentication
3786N/A * then check if we are in a secure environment
3786N/A * whether, or not, we should try transparent authentication.*/
3786N/A if (tryTransparentNTLMServer) {
3786N/A tryTransparentNTLMServer =
3786N/A NTLMAuthenticationProxy.proxy.isTrustedSite(url);
3786N/A }
1670N/A }
1670N/A a = null;
1670N/A if (tryTransparentNTLMServer) {
1677N/A logger.finest("Trying Transparent NTLM authentication");
1670N/A } else {
1670N/A a = privilegedRequestPasswordAuthentication(
1670N/A url.getHost(), addr, port, url.getProtocol(),
1670N/A "", scheme, url, RequestorType.SERVER);
1670N/A }
1670N/A
1670N/A /* If we are not trying transparent authentication then
1670N/A * we need to have a PasswordAuthentication instance. For
1670N/A * transparent authentication (Windows only) the username
1670N/A * and password will be picked up from the current logged
1670N/A * on users credentials.
1670N/A */
1670N/A if (tryTransparentNTLMServer ||
1670N/A (!tryTransparentNTLMServer && a != null)) {
1670N/A ret = NTLMAuthenticationProxy.proxy.create(false, url1, a);
1670N/A }
1670N/A
1670N/A /* set to false so that we do not try again */
1670N/A tryTransparentNTLMServer = false;
0N/A }
1670N/A break;
1670N/A case UNKNOWN:
1677N/A logger.finest("Unknown/Unsupported authentication scheme: " + scheme);
1670N/A default:
1670N/A throw new AssertionError("should not reach here");
0N/A }
0N/A }
0N/A
0N/A // For backwards compatibility, we also try defaultAuth
0N/A // REMIND: Get rid of this for JDK2.0.
0N/A
0N/A if (ret == null && defaultAuth != null
0N/A && defaultAuth.schemeSupported(scheme)) {
0N/A String a = defaultAuth.authString(url, scheme, realm);
0N/A if (a != null) {
0N/A ret = new BasicAuthentication (false, url, realm, a);
0N/A // not in cache by default - cache on success
0N/A }
0N/A }
0N/A
0N/A if (ret != null ) {
0N/A if (!ret.setHeaders(this, p, raw)) {
0N/A ret = null;
0N/A }
0N/A }
0N/A }
1677N/A if (logger.isLoggable(PlatformLogger.FINER)) {
1677N/A logger.finer("Server Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
1313N/A }
0N/A return ret;
0N/A }
0N/A
0N/A /* inclose will be true if called from close(), in which case we
0N/A * force the call to check because this is the last chance to do so.
0N/A * If not in close(), then the authentication info could arrive in a trailer
0N/A * field, which we have not read yet.
0N/A */
0N/A private void checkResponseCredentials (boolean inClose) throws IOException {
0N/A try {
0N/A if (!needToCheck)
0N/A return;
1710N/A if ((validateProxy && currentProxyCredentials != null) &&
1710N/A (currentProxyCredentials instanceof DigestAuthentication)) {
0N/A String raw = responses.findValue ("Proxy-Authentication-Info");
0N/A if (inClose || (raw != null)) {
1710N/A DigestAuthentication da = (DigestAuthentication)
1710N/A currentProxyCredentials;
1710N/A da.checkResponse (raw, method, getRequestURI());
0N/A currentProxyCredentials = null;
0N/A }
0N/A }
1710N/A if ((validateServer && currentServerCredentials != null) &&
1710N/A (currentServerCredentials instanceof DigestAuthentication)) {
0N/A String raw = responses.findValue ("Authentication-Info");
0N/A if (inClose || (raw != null)) {
1710N/A DigestAuthentication da = (DigestAuthentication)
1710N/A currentServerCredentials;
1710N/A da.checkResponse (raw, method, url);
0N/A currentServerCredentials = null;
0N/A }
0N/A }
0N/A if ((currentServerCredentials==null) && (currentProxyCredentials == null)) {
0N/A needToCheck = false;
0N/A }
0N/A } catch (IOException e) {
0N/A disconnectInternal();
0N/A connected = false;
0N/A throw e;
0N/A }
0N/A }
0N/A
1710N/A /* The request URI used in the request line for this request.
1710N/A * Also, needed for digest authentication
1710N/A */
1710N/A
1710N/A String requestURI = null;
1710N/A
1784N/A String getRequestURI() throws IOException {
1710N/A if (requestURI == null) {
1784N/A requestURI = http.getURLFile();
1710N/A }
1710N/A return requestURI;
1710N/A }
1710N/A
0N/A /* Tells us whether to follow a redirect. If so, it
0N/A * closes the connection (break any keep-alive) and
0N/A * resets the url, re-connects, and resets the request
0N/A * property.
0N/A */
0N/A private boolean followRedirect() throws IOException {
0N/A if (!getInstanceFollowRedirects()) {
0N/A return false;
0N/A }
0N/A
0N/A int stat = getResponseCode();
0N/A if (stat < 300 || stat > 307 || stat == 306
0N/A || stat == HTTP_NOT_MODIFIED) {
0N/A return false;
0N/A }
0N/A String loc = getHeaderField("Location");
0N/A if (loc == null) {
0N/A /* this should be present - if not, we have no choice
0N/A * but to go forward w/ the response we got
0N/A */
0N/A return false;
0N/A }
0N/A URL locUrl;
0N/A try {
0N/A locUrl = new URL(loc);
0N/A if (!url.getProtocol().equalsIgnoreCase(locUrl.getProtocol())) {
0N/A return false;
0N/A }
0N/A
0N/A } catch (MalformedURLException mue) {
0N/A // treat loc as a relative URI to conform to popular browsers
0N/A locUrl = new URL(url, loc);
0N/A }
0N/A disconnectInternal();
0N/A if (streaming()) {
0N/A throw new HttpRetryException (RETRY_MSG3, stat, loc);
0N/A }
1677N/A if (logger.isLoggable(PlatformLogger.FINE)) {
1677N/A logger.fine("Redirected from " + url + " to " + locUrl);
1313N/A }
0N/A
0N/A // clear out old response headers!!!!
0N/A responses = new MessageHeader();
0N/A if (stat == HTTP_USE_PROXY) {
0N/A /* This means we must re-request the resource through the
0N/A * proxy denoted in the "Location:" field of the response.
0N/A * Judging by the spec, the string in the Location header
0N/A * _should_ denote a URL - let's hope for "http://my.proxy.org"
0N/A * Make a new HttpClient to the proxy, using HttpClient's
0N/A * Instance-specific proxy fields, but note we're still fetching
0N/A * the same URL.
0N/A */
0N/A String proxyHost = locUrl.getHost();
0N/A int proxyPort = locUrl.getPort();
0N/A
0N/A SecurityManager security = System.getSecurityManager();
0N/A if (security != null) {
0N/A security.checkConnect(proxyHost, proxyPort);
0N/A }
0N/A
0N/A setProxiedClient (url, proxyHost, proxyPort);
1710N/A requests.set(0, method + " " + getRequestURI()+" " +
0N/A httpVersion, null);
0N/A connected = true;
0N/A } else {
0N/A // maintain previous headers, just change the name
0N/A // of the file we're getting
0N/A url = locUrl;
1710N/A requestURI = null; // force it to be recalculated
0N/A if (method.equals("POST") && !Boolean.getBoolean("http.strictPostRedirect") && (stat!=307)) {
0N/A /* The HTTP/1.1 spec says that a redirect from a POST
0N/A * *should not* be immediately turned into a GET, and
0N/A * that some HTTP/1.0 clients incorrectly did this.
0N/A * Correct behavior redirects a POST to another POST.
0N/A * Unfortunately, since most browsers have this incorrect
0N/A * behavior, the web works this way now. Typical usage
0N/A * seems to be:
0N/A * POST a login code or passwd to a web page.
0N/A * after validation, the server redirects to another
0N/A * (welcome) page
0N/A * The second request is (erroneously) expected to be GET
0N/A *
0N/A * We will do the incorrect thing (POST-->GET) by default.
0N/A * We will provide the capability to do the "right" thing
0N/A * (POST-->POST) by a system property, "http.strictPostRedirect=true"
0N/A */
0N/A
0N/A requests = new MessageHeader();
0N/A setRequests = false;
0N/A setRequestMethod("GET");
0N/A poster = null;
0N/A if (!checkReuseConnection())
0N/A connect();
0N/A } else {
0N/A if (!checkReuseConnection())
0N/A connect();
0N/A /* Even after a connect() call, http variable still can be
0N/A * null, if a ResponseCache has been installed and it returns
0N/A * a non-null CacheResponse instance. So check nullity before using it.
0N/A *
0N/A * And further, if http is null, there's no need to do anything
0N/A * about request headers because successive http session will use
0N/A * cachedInputStream/cachedHeaders anyway, which is returned by
0N/A * CacheResponse.
0N/A */
0N/A if (http != null) {
1710N/A requests.set(0, method + " " + getRequestURI()+" " +
0N/A httpVersion, null);
0N/A int port = url.getPort();
0N/A String host = url.getHost();
0N/A if (port != -1 && port != url.getDefaultPort()) {
0N/A host += ":" + String.valueOf(port);
0N/A }
0N/A requests.set("Host", host);
0N/A }
0N/A }
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A /* dummy byte buffer for reading off socket prior to closing */
0N/A byte[] cdata = new byte [128];
0N/A
0N/A /**
0N/A * Reset (without disconnecting the TCP conn) in order to do another transaction with this instance
0N/A */
0N/A private void reset() throws IOException {
0N/A http.reuse = true;
0N/A /* must save before calling close */
0N/A reuseClient = http;
0N/A InputStream is = http.getInputStream();
0N/A if (!method.equals("HEAD")) {
0N/A try {
0N/A /* we want to read the rest of the response without using the
0N/A * hurry mechanism, because that would close the connection
0N/A * if everything is not available immediately
0N/A */
0N/A if ((is instanceof ChunkedInputStream) ||
0N/A (is instanceof MeteredStream)) {
0N/A /* reading until eof will not block */
0N/A while (is.read (cdata) > 0) {}
0N/A } else {
0N/A /* raw stream, which will block on read, so only read
0N/A * the expected number of bytes, probably 0
0N/A */
1313N/A long cl = 0;
1313N/A int n = 0;
1313N/A String cls = responses.findValue ("Content-Length");
1313N/A if (cls != null) {
1313N/A try {
1313N/A cl = Long.parseLong (cls);
1313N/A } catch (NumberFormatException e) {
1313N/A cl = 0;
1313N/A }
1313N/A }
1313N/A for (long i=0; i<cl; ) {
0N/A if ((n = is.read (cdata)) == -1) {
0N/A break;
0N/A } else {
0N/A i+= n;
0N/A }
0N/A }
0N/A }
0N/A } catch (IOException e) {
0N/A http.reuse = false;
0N/A reuseClient = null;
0N/A disconnectInternal();
0N/A return;
0N/A }
0N/A try {
0N/A if (is instanceof MeteredStream) {
0N/A is.close();
0N/A }
0N/A } catch (IOException e) { }
0N/A }
0N/A responseCode = -1;
0N/A responses = new MessageHeader();
0N/A connected = false;
0N/A }
0N/A
0N/A /**
2240N/A * Disconnect from the web server at the first 401 error. Do not
2240N/A * disconnect when using a proxy, a good proxy should have already
2240N/A * closed the connection to the web server.
2240N/A */
2240N/A private void disconnectWeb() throws IOException {
2555N/A if (usingProxy() && http.isKeepingAlive()) {
2240N/A responseCode = -1;
2240N/A // clean up, particularly, skip the content part
2240N/A // of a 401 error response
2240N/A reset();
2240N/A } else {
2240N/A disconnectInternal();
2240N/A }
2240N/A }
2240N/A
2240N/A /**
0N/A * Disconnect from the server (for internal use)
0N/A */
0N/A private void disconnectInternal() {
0N/A responseCode = -1;
55N/A inputStream = null;
0N/A if (pi != null) {
0N/A pi.finishTracking();
0N/A pi = null;
0N/A }
0N/A if (http != null) {
0N/A http.closeServer();
0N/A http = null;
0N/A connected = false;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Disconnect from the server (public API)
0N/A */
0N/A public void disconnect() {
0N/A
0N/A responseCode = -1;
0N/A if (pi != null) {
0N/A pi.finishTracking();
0N/A pi = null;
0N/A }
0N/A
0N/A if (http != null) {
0N/A /*
0N/A * If we have an input stream this means we received a response
0N/A * from the server. That stream may have been read to EOF and
0N/A * dependening on the stream type may already be closed or the
0N/A * the http client may be returned to the keep-alive cache.
0N/A * If the http client has been returned to the keep-alive cache
0N/A * it may be closed (idle timeout) or may be allocated to
0N/A * another request.
0N/A *
0N/A * In other to avoid timing issues we close the input stream
0N/A * which will either close the underlying connection or return
0N/A * the client to the cache. If there's a possibility that the
0N/A * client has been returned to the cache (ie: stream is a keep
0N/A * alive stream or a chunked input stream) then we remove an
0N/A * idle connection to the server. Note that this approach
0N/A * can be considered an approximation in that we may close a
0N/A * different idle connection to that used by the request.
0N/A * Additionally it's possible that we close two connections
0N/A * - the first becuase it wasn't an EOF (and couldn't be
0N/A * hurried) - the second, another idle connection to the
0N/A * same server. The is okay because "disconnect" is an
0N/A * indication that the application doesn't intend to access
0N/A * this http server for a while.
0N/A */
0N/A
0N/A if (inputStream != null) {
0N/A HttpClient hc = http;
0N/A
0N/A // un-synchronized
0N/A boolean ka = hc.isKeepingAlive();
0N/A
0N/A try {
0N/A inputStream.close();
0N/A } catch (IOException ioe) { }
0N/A
0N/A // if the connection is persistent it may have been closed
0N/A // or returned to the keep-alive cache. If it's been returned
0N/A // to the keep-alive cache then we would like to close it
0N/A // but it may have been allocated
0N/A
0N/A if (ka) {
0N/A hc.closeIdleConnection();
0N/A }
0N/A
0N/A
0N/A } else {
0N/A // We are deliberatly being disconnected so HttpClient
0N/A // should not try to resend the request no matter what stage
0N/A // of the connection we are in.
0N/A http.setDoNotRetry(true);
0N/A
0N/A http.closeServer();
0N/A }
0N/A
0N/A // poster = null;
0N/A http = null;
0N/A connected = false;
0N/A }
0N/A cachedInputStream = null;
0N/A if (cachedHeaders != null) {
0N/A cachedHeaders.reset();
0N/A }
0N/A }
0N/A
0N/A public boolean usingProxy() {
0N/A if (http != null) {
0N/A return (http.getProxyHostUsed() != null);
0N/A }
0N/A return false;
0N/A }
0N/A
4566N/A // constant strings represent set-cookie header names
4566N/A private final static String SET_COOKIE = "set-cookie";
4566N/A private final static String SET_COOKIE2 = "set-cookie2";
4566N/A
4566N/A /**
4566N/A * Returns a filtered version of the given headers value.
4566N/A *
4566N/A * Note: The implementation currently only filters out HttpOnly cookies
4566N/A * from Set-Cookie and Set-Cookie2 headers.
4566N/A */
4566N/A private String filterHeaderField(String name, String value) {
4566N/A if (value == null)
4566N/A return null;
4566N/A
4566N/A if (SET_COOKIE.equalsIgnoreCase(name) ||
4566N/A SET_COOKIE2.equalsIgnoreCase(name)) {
4566N/A // Filtering only if there is a cookie handler. [Assumption: the
4566N/A // cookie handler will store/retrieve the HttpOnly cookies]
4566N/A if (cookieHandler == null)
4566N/A return value;
4566N/A
4566N/A sun.misc.JavaNetHttpCookieAccess access =
4566N/A sun.misc.SharedSecrets.getJavaNetHttpCookieAccess();
4566N/A StringBuilder retValue = new StringBuilder();
4566N/A List<HttpCookie> cookies = access.parse(value);
4566N/A boolean multipleCookies = false;
4566N/A for (HttpCookie cookie : cookies) {
4566N/A // skip HttpOnly cookies
4566N/A if (cookie.isHttpOnly())
4566N/A continue;
4566N/A if (multipleCookies)
4566N/A retValue.append(','); // RFC 2965, comma separated
4566N/A retValue.append(access.header(cookie));
4566N/A multipleCookies = true;
4566N/A }
4566N/A
5839N/A return retValue.length() == 0 ? "" : retValue.toString();
4566N/A }
4566N/A
4566N/A return value;
4566N/A }
4566N/A
4566N/A // Cache the filtered response headers so that they don't need
4566N/A // to be generated for every getHeaderFields() call.
4566N/A private Map<String, List<String>> filteredHeaders; // null
4566N/A
4566N/A private Map<String, List<String>> getFilteredHeaderFields() {
4566N/A if (filteredHeaders != null)
4566N/A return filteredHeaders;
4566N/A
4567N/A Map<String, List<String>> headers, tmpMap = new HashMap<>();
4566N/A
4566N/A if (cachedHeaders != null)
4566N/A headers = cachedHeaders.getHeaders();
4566N/A else
4566N/A headers = responses.getHeaders();
4566N/A
4566N/A for (Map.Entry<String, List<String>> e: headers.entrySet()) {
4566N/A String key = e.getKey();
4566N/A List<String> values = e.getValue(), filteredVals = new ArrayList<>();
4566N/A for (String value : values) {
4566N/A String fVal = filterHeaderField(key, value);
4566N/A if (fVal != null)
4566N/A filteredVals.add(fVal);
4566N/A }
4566N/A if (!filteredVals.isEmpty())
4567N/A tmpMap.put(key, Collections.unmodifiableList(filteredVals));
4566N/A }
4566N/A
4567N/A return filteredHeaders = Collections.unmodifiableMap(tmpMap);
4566N/A }
4566N/A
0N/A /**
0N/A * Gets a header field by name. Returns null if not known.
0N/A * @param name the name of the header field
0N/A */
51N/A @Override
0N/A public String getHeaderField(String name) {
0N/A try {
0N/A getInputStream();
0N/A } catch (IOException e) {}
0N/A
0N/A if (cachedHeaders != null) {
4566N/A return filterHeaderField(name, cachedHeaders.findValue(name));
0N/A }
0N/A
4566N/A return filterHeaderField(name, responses.findValue(name));
0N/A }
0N/A
0N/A /**
0N/A * Returns an unmodifiable Map of the header fields.
0N/A * The Map keys are Strings that represent the
0N/A * response-header field names. Each Map value is an
0N/A * unmodifiable List of Strings that represents
0N/A * the corresponding field values.
0N/A *
0N/A * @return a Map of header fields
0N/A * @since 1.4
0N/A */
51N/A @Override
28N/A public Map<String, List<String>> getHeaderFields() {
0N/A try {
0N/A getInputStream();
0N/A } catch (IOException e) {}
0N/A
4566N/A return getFilteredHeaderFields();
0N/A }
0N/A
0N/A /**
0N/A * Gets a header field by index. Returns null if not known.
0N/A * @param n the index of the header field
0N/A */
51N/A @Override
0N/A public String getHeaderField(int n) {
0N/A try {
0N/A getInputStream();
0N/A } catch (IOException e) {}
0N/A
0N/A if (cachedHeaders != null) {
4566N/A return filterHeaderField(cachedHeaders.getKey(n),
4566N/A cachedHeaders.getValue(n));
0N/A }
4566N/A return filterHeaderField(responses.getKey(n), responses.getValue(n));
0N/A }
0N/A
0N/A /**
0N/A * Gets a header field by index. Returns null if not known.
0N/A * @param n the index of the header field
0N/A */
51N/A @Override
0N/A public String getHeaderFieldKey(int n) {
0N/A try {
0N/A getInputStream();
0N/A } catch (IOException e) {}
0N/A
0N/A if (cachedHeaders != null) {
0N/A return cachedHeaders.getKey(n);
0N/A }
0N/A
0N/A return responses.getKey(n);
0N/A }
0N/A
0N/A /**
0N/A * Sets request property. If a property with the key already
0N/A * exists, overwrite its value with the new value.
0N/A * @param value the value to be set
0N/A */
51N/A @Override
0N/A public void setRequestProperty(String key, String value) {
0N/A if (connected)
0N/A throw new IllegalStateException("Already connected");
0N/A if (key == null)
0N/A throw new NullPointerException ("key is null");
0N/A
2910N/A if (isExternalMessageHeaderAllowed(key, value)) {
2910N/A requests.set(key, value);
2910N/A }
0N/A }
0N/A
0N/A /**
0N/A * Adds a general request property specified by a
0N/A * key-value pair. This method will not overwrite
0N/A * existing values associated with the same key.
0N/A *
0N/A * @param key the keyword by which the request is known
0N/A * (e.g., "<code>accept</code>").
0N/A * @param value the value associated with it.
0N/A * @see #getRequestProperties(java.lang.String)
0N/A * @since 1.4
0N/A */
51N/A @Override
0N/A public void addRequestProperty(String key, String value) {
0N/A if (connected)
0N/A throw new IllegalStateException("Already connected");
0N/A if (key == null)
0N/A throw new NullPointerException ("key is null");
0N/A
2910N/A if (isExternalMessageHeaderAllowed(key, value)) {
2910N/A requests.add(key, value);
2910N/A }
0N/A }
0N/A
0N/A //
0N/A // Set a property for authentication. This can safely disregard
0N/A // the connected test.
0N/A //
1791N/A public void setAuthenticationProperty(String key, String value) {
0N/A checkMessageHeader(key, value);
0N/A requests.set(key, value);
0N/A }
0N/A
51N/A @Override
2910N/A public synchronized String getRequestProperty (String key) {
2910N/A if (key == null) {
2910N/A return null;
2910N/A }
2910N/A
0N/A // don't return headers containing security sensitive information
2910N/A for (int i=0; i < EXCLUDE_HEADERS.length; i++) {
2910N/A if (key.equalsIgnoreCase(EXCLUDE_HEADERS[i])) {
2910N/A return null;
2910N/A }
2910N/A }
2910N/A if (!setUserCookies) {
2910N/A if (key.equalsIgnoreCase("Cookie")) {
2910N/A return userCookies;
2910N/A }
2910N/A if (key.equalsIgnoreCase("Cookie2")) {
2910N/A return userCookies2;
0N/A }
0N/A }
0N/A return requests.findValue(key);
0N/A }
0N/A
0N/A /**
0N/A * Returns an unmodifiable Map of general request
0N/A * properties for this connection. The Map keys
0N/A * are Strings that represent the request-header
0N/A * field names. Each Map value is a unmodifiable List
0N/A * of Strings that represents the corresponding
0N/A * field values.
0N/A *
0N/A * @return a Map of the general request properties for this connection.
0N/A * @throws IllegalStateException if already connected
0N/A * @since 1.4
0N/A */
51N/A @Override
2910N/A public synchronized Map<String, List<String>> getRequestProperties() {
0N/A if (connected)
0N/A throw new IllegalStateException("Already connected");
0N/A
0N/A // exclude headers containing security-sensitive info
2910N/A if (setUserCookies) {
2910N/A return requests.getHeaders(EXCLUDE_HEADERS);
2910N/A }
2910N/A /*
2910N/A * The cookies in the requests message headers may have
2910N/A * been modified. Use the saved user cookies instead.
2910N/A */
2910N/A Map userCookiesMap = null;
2910N/A if (userCookies != null || userCookies2 != null) {
2910N/A userCookiesMap = new HashMap();
2910N/A if (userCookies != null) {
2910N/A userCookiesMap.put("Cookie", userCookies);
2910N/A }
2910N/A if (userCookies2 != null) {
2910N/A userCookiesMap.put("Cookie2", userCookies2);
2910N/A }
2910N/A }
2910N/A return requests.filterAndAddHeaders(EXCLUDE_HEADERS2, userCookiesMap);
0N/A }
0N/A
51N/A @Override
0N/A public void setConnectTimeout(int timeout) {
0N/A if (timeout < 0)
0N/A throw new IllegalArgumentException("timeouts can't be negative");
0N/A connectTimeout = timeout;
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Returns setting for connect timeout.
0N/A * <p>
0N/A * 0 return implies that the option is disabled
0N/A * (i.e., timeout of infinity).
0N/A *
0N/A * @return an <code>int</code> that indicates the connect timeout
0N/A * value in milliseconds
0N/A * @see java.net.URLConnection#setConnectTimeout(int)
0N/A * @see java.net.URLConnection#connect()
0N/A * @since 1.5
0N/A */
51N/A @Override
0N/A public int getConnectTimeout() {
0N/A return (connectTimeout < 0 ? 0 : connectTimeout);
0N/A }
0N/A
0N/A /**
0N/A * Sets the read timeout to a specified timeout, in
0N/A * milliseconds. A non-zero value specifies the timeout when
0N/A * reading from Input stream when a connection is established to a
0N/A * resource. If the timeout expires before there is data available
0N/A * for read, a java.net.SocketTimeoutException is raised. A
0N/A * timeout of zero is interpreted as an infinite timeout.
0N/A *
0N/A * <p> Some non-standard implementation of this method ignores the
0N/A * specified timeout. To see the read timeout set, please call
0N/A * getReadTimeout().
0N/A *
0N/A * @param timeout an <code>int</code> that specifies the timeout
0N/A * value to be used in milliseconds
0N/A * @throws IllegalArgumentException if the timeout parameter is negative
0N/A *
0N/A * @see java.net.URLConnectiongetReadTimeout()
0N/A * @see java.io.InputStream#read()
0N/A * @since 1.5
0N/A */
51N/A @Override
0N/A public void setReadTimeout(int timeout) {
0N/A if (timeout < 0)
0N/A throw new IllegalArgumentException("timeouts can't be negative");
0N/A readTimeout = timeout;
0N/A }
0N/A
0N/A /**
0N/A * Returns setting for read timeout. 0 return implies that the
0N/A * option is disabled (i.e., timeout of infinity).
0N/A *
0N/A * @return an <code>int</code> that indicates the read timeout
0N/A * value in milliseconds
0N/A *
0N/A * @see java.net.URLConnection#setReadTimeout(int)
0N/A * @see java.io.InputStream#read()
0N/A * @since 1.5
0N/A */
51N/A @Override
0N/A public int getReadTimeout() {
0N/A return readTimeout < 0 ? 0 : readTimeout;
0N/A }
0N/A
4593N/A public CookieHandler getCookieHandler() {
4593N/A return cookieHandler;
4593N/A }
4593N/A
0N/A String getMethod() {
0N/A return method;
0N/A }
0N/A
28N/A private MessageHeader mapToMessageHeader(Map<String, List<String>> map) {
0N/A MessageHeader headers = new MessageHeader();
0N/A if (map == null || map.isEmpty()) {
0N/A return headers;
0N/A }
28N/A for (Map.Entry<String, List<String>> entry : map.entrySet()) {
28N/A String key = entry.getKey();
28N/A List<String> values = entry.getValue();
28N/A for (String value : values) {
0N/A if (key == null) {
0N/A headers.prepend(key, value);
0N/A } else {
0N/A headers.add(key, value);
0N/A }
0N/A }
0N/A }
0N/A return headers;
0N/A }
0N/A
0N/A /* The purpose of this wrapper is just to capture the close() call
0N/A * so we can check authentication information that may have
0N/A * arrived in a Trailer field
0N/A */
0N/A class HttpInputStream extends FilterInputStream {
0N/A private CacheRequest cacheRequest;
0N/A private OutputStream outputStream;
0N/A private boolean marked = false;
0N/A private int inCache = 0;
0N/A private int markCount = 0;
0N/A
0N/A public HttpInputStream (InputStream is) {
0N/A super (is);
0N/A this.cacheRequest = null;
0N/A this.outputStream = null;
0N/A }
0N/A
0N/A public HttpInputStream (InputStream is, CacheRequest cacheRequest) {
0N/A super (is);
0N/A this.cacheRequest = cacheRequest;
0N/A try {
0N/A this.outputStream = cacheRequest.getBody();
0N/A } catch (IOException ioex) {
0N/A this.cacheRequest.abort();
0N/A this.cacheRequest = null;
0N/A this.outputStream = null;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Marks the current position in this input stream. A subsequent
0N/A * call to the <code>reset</code> method repositions this stream at
0N/A * the last marked position so that subsequent reads re-read the same
0N/A * bytes.
0N/A * <p>
0N/A * The <code>readlimit</code> argument tells this input stream to
0N/A * allow that many bytes to be read before the mark position gets
0N/A * invalidated.
0N/A * <p>
0N/A * This method simply performs <code>in.mark(readlimit)</code>.
0N/A *
0N/A * @param readlimit the maximum limit of bytes that can be read before
0N/A * the mark position becomes invalid.
0N/A * @see java.io.FilterInputStream#in
0N/A * @see java.io.FilterInputStream#reset()
0N/A */
51N/A @Override
0N/A public synchronized void mark(int readlimit) {
0N/A super.mark(readlimit);
0N/A if (cacheRequest != null) {
0N/A marked = true;
0N/A markCount = 0;
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Repositions this stream to the position at the time the
0N/A * <code>mark</code> method was last called on this input stream.
0N/A * <p>
0N/A * This method
0N/A * simply performs <code>in.reset()</code>.
0N/A * <p>
0N/A * Stream marks are intended to be used in
0N/A * situations where you need to read ahead a little to see what's in
0N/A * the stream. Often this is most easily done by invoking some
0N/A * general parser. If the stream is of the type handled by the
0N/A * parse, it just chugs along happily. If the stream is not of
0N/A * that type, the parser should toss an exception when it fails.
0N/A * If this happens within readlimit bytes, it allows the outer
0N/A * code to reset the stream and try another parser.
0N/A *
0N/A * @exception IOException if the stream has not been marked or if the
0N/A * mark has been invalidated.
0N/A * @see java.io.FilterInputStream#in
0N/A * @see java.io.FilterInputStream#mark(int)
0N/A */
51N/A @Override
0N/A public synchronized void reset() throws IOException {
0N/A super.reset();
0N/A if (cacheRequest != null) {
0N/A marked = false;
0N/A inCache += markCount;
0N/A }
0N/A }
0N/A
51N/A @Override
0N/A public int read() throws IOException {
0N/A try {
0N/A byte[] b = new byte[1];
0N/A int ret = read(b);
0N/A return (ret == -1? ret : (b[0] & 0x00FF));
0N/A } catch (IOException ioex) {
0N/A if (cacheRequest != null) {
0N/A cacheRequest.abort();
0N/A }
0N/A throw ioex;
0N/A }
0N/A }
0N/A
51N/A @Override
0N/A public int read(byte[] b) throws IOException {
0N/A return read(b, 0, b.length);
0N/A }
0N/A
51N/A @Override
0N/A public int read(byte[] b, int off, int len) throws IOException {
0N/A try {
0N/A int newLen = super.read(b, off, len);
0N/A int nWrite;
0N/A // write to cache
0N/A if (inCache > 0) {
0N/A if (inCache >= newLen) {
0N/A inCache -= newLen;
0N/A nWrite = 0;
0N/A } else {
0N/A nWrite = newLen - inCache;
0N/A inCache = 0;
0N/A }
0N/A } else {
0N/A nWrite = newLen;
0N/A }
0N/A if (nWrite > 0 && outputStream != null)
0N/A outputStream.write(b, off + (newLen-nWrite), nWrite);
0N/A if (marked) {
0N/A markCount += newLen;
0N/A }
0N/A return newLen;
0N/A } catch (IOException ioex) {
0N/A if (cacheRequest != null) {
0N/A cacheRequest.abort();
0N/A }
0N/A throw ioex;
0N/A }
0N/A }
0N/A
2816N/A /* skip() calls read() in order to ensure that entire response gets
2816N/A * cached. same implementation as InputStream.skip */
2816N/A
2816N/A private byte[] skipBuffer;
2816N/A private static final int SKIP_BUFFER_SIZE = 8096;
2816N/A
2816N/A @Override
2816N/A public long skip (long n) throws IOException {
2816N/A
2816N/A long remaining = n;
2816N/A int nr;
2816N/A if (skipBuffer == null)
2816N/A skipBuffer = new byte[SKIP_BUFFER_SIZE];
2816N/A
2816N/A byte[] localSkipBuffer = skipBuffer;
2816N/A
2816N/A if (n <= 0) {
2816N/A return 0;
2816N/A }
2816N/A
2816N/A while (remaining > 0) {
2816N/A nr = read(localSkipBuffer, 0,
2816N/A (int) Math.min(SKIP_BUFFER_SIZE, remaining));
2816N/A if (nr < 0) {
2816N/A break;
2816N/A }
2816N/A remaining -= nr;
2816N/A }
2816N/A
2816N/A return n - remaining;
2816N/A }
2816N/A
51N/A @Override
0N/A public void close () throws IOException {
0N/A try {
0N/A if (outputStream != null) {
0N/A if (read() != -1) {
0N/A cacheRequest.abort();
0N/A } else {
0N/A outputStream.close();
0N/A }
0N/A }
0N/A super.close ();
0N/A } catch (IOException ioex) {
0N/A if (cacheRequest != null) {
0N/A cacheRequest.abort();
0N/A }
0N/A throw ioex;
0N/A } finally {
0N/A HttpURLConnection.this.http = null;
0N/A checkResponseCredentials (true);
0N/A }
0N/A }
0N/A }
0N/A
0N/A class StreamingOutputStream extends FilterOutputStream {
0N/A
705N/A long expected;
705N/A long written;
0N/A boolean closed;
0N/A boolean error;
0N/A IOException errorExcp;
0N/A
0N/A /**
0N/A * expectedLength == -1 if the stream is chunked
0N/A * expectedLength > 0 if the stream is fixed content-length
0N/A * In the 2nd case, we make sure the expected number of
0N/A * of bytes are actually written
0N/A */
705N/A StreamingOutputStream (OutputStream os, long expectedLength) {
0N/A super (os);
0N/A expected = expectedLength;
705N/A written = 0L;
0N/A closed = false;
0N/A error = false;
0N/A }
0N/A
51N/A @Override
0N/A public void write (int b) throws IOException {
0N/A checkError();
0N/A written ++;
705N/A if (expected != -1L && written > expected) {
0N/A throw new IOException ("too many bytes written");
0N/A }
0N/A out.write (b);
0N/A }
0N/A
51N/A @Override
0N/A public void write (byte[] b) throws IOException {
0N/A write (b, 0, b.length);
0N/A }
0N/A
51N/A @Override
0N/A public void write (byte[] b, int off, int len) throws IOException {
0N/A checkError();
0N/A written += len;
705N/A if (expected != -1L && written > expected) {
0N/A out.close ();
0N/A throw new IOException ("too many bytes written");
0N/A }
0N/A out.write (b, off, len);
0N/A }
0N/A
0N/A void checkError () throws IOException {
0N/A if (closed) {
0N/A throw new IOException ("Stream is closed");
0N/A }
0N/A if (error) {
0N/A throw errorExcp;
0N/A }
0N/A if (((PrintStream)out).checkError()) {
0N/A throw new IOException("Error writing request body to server");
0N/A }
0N/A }
0N/A
0N/A /* this is called to check that all the bytes
0N/A * that were supposed to be written were written
0N/A * and that the stream is now closed().
0N/A */
0N/A boolean writtenOK () {
0N/A return closed && ! error;
0N/A }
0N/A
51N/A @Override
0N/A public void close () throws IOException {
0N/A if (closed) {
0N/A return;
0N/A }
0N/A closed = true;
705N/A if (expected != -1L) {
0N/A /* not chunked */
0N/A if (written != expected) {
0N/A error = true;
0N/A errorExcp = new IOException ("insufficient data written");
0N/A out.close ();
0N/A throw errorExcp;
0N/A }
0N/A super.flush(); /* can't close the socket */
0N/A } else {
0N/A /* chunked */
0N/A super.close (); /* force final chunk to be written */
0N/A /* trailing \r\n */
0N/A OutputStream o = http.getOutputStream();
0N/A o.write ('\r');
0N/A o.write ('\n');
0N/A o.flush();
0N/A }
0N/A }
0N/A }
0N/A
0N/A
0N/A static class ErrorStream extends InputStream {
0N/A ByteBuffer buffer;
0N/A InputStream is;
0N/A
0N/A private ErrorStream(ByteBuffer buf) {
0N/A buffer = buf;
0N/A is = null;
0N/A }
0N/A
0N/A private ErrorStream(ByteBuffer buf, InputStream is) {
0N/A buffer = buf;
0N/A this.is = is;
0N/A }
0N/A
0N/A // when this method is called, it's either the case that cl > 0, or
0N/A // if chunk-encoded, cl = -1; in other words, cl can't be 0
0N/A public static InputStream getErrorStream(InputStream is, long cl, HttpClient http) {
0N/A
0N/A // cl can't be 0; this following is here for extra precaution
0N/A if (cl == 0) {
0N/A return null;
0N/A }
0N/A
0N/A try {
0N/A // set SO_TIMEOUT to 1/5th of the total timeout
0N/A // remember the old timeout value so that we can restore it
1252N/A int oldTimeout = http.getReadTimeout();
1252N/A http.setReadTimeout(timeout4ESBuffer/5);
0N/A
0N/A long expected = 0;
0N/A boolean isChunked = false;
0N/A // the chunked case
0N/A if (cl < 0) {
0N/A expected = bufSize4ES;
0N/A isChunked = true;
0N/A } else {
0N/A expected = cl;
0N/A }
0N/A if (expected <= bufSize4ES) {
0N/A int exp = (int) expected;
0N/A byte[] buffer = new byte[exp];
0N/A int count = 0, time = 0, len = 0;
0N/A do {
0N/A try {
0N/A len = is.read(buffer, count,
0N/A buffer.length - count);
0N/A if (len < 0) {
0N/A if (isChunked) {
0N/A // chunked ended
0N/A // if chunked ended prematurely,
0N/A // an IOException would be thrown
0N/A break;
0N/A }
0N/A // the server sends less than cl bytes of data
0N/A throw new IOException("the server closes"+
0N/A " before sending "+cl+
0N/A " bytes of data");
0N/A }
0N/A count += len;
0N/A } catch (SocketTimeoutException ex) {
0N/A time += timeout4ESBuffer/5;
0N/A }
0N/A } while (count < exp && time < timeout4ESBuffer);
0N/A
0N/A // reset SO_TIMEOUT to old value
1252N/A http.setReadTimeout(oldTimeout);
0N/A
0N/A // if count < cl at this point, we will not try to reuse
0N/A // the connection
0N/A if (count == 0) {
0N/A // since we haven't read anything,
0N/A // we will return the underlying
0N/A // inputstream back to the application
0N/A return null;
0N/A } else if ((count == expected && !(isChunked)) || (isChunked && len <0)) {
0N/A // put the connection into keep-alive cache
0N/A // the inputstream will try to do the right thing
0N/A is.close();
0N/A return new ErrorStream(ByteBuffer.wrap(buffer, 0, count));
0N/A } else {
0N/A // we read part of the response body
0N/A return new ErrorStream(
0N/A ByteBuffer.wrap(buffer, 0, count), is);
0N/A }
0N/A }
0N/A return null;
0N/A } catch (IOException ioex) {
0N/A // ioex.printStackTrace();
0N/A return null;
0N/A }
0N/A }
0N/A
51N/A @Override
0N/A public int available() throws IOException {
0N/A if (is == null) {
0N/A return buffer.remaining();
0N/A } else {
0N/A return buffer.remaining()+is.available();
0N/A }
0N/A }
0N/A
0N/A public int read() throws IOException {
0N/A byte[] b = new byte[1];
0N/A int ret = read(b);
0N/A return (ret == -1? ret : (b[0] & 0x00FF));
0N/A }
0N/A
51N/A @Override
0N/A public int read(byte[] b) throws IOException {
0N/A return read(b, 0, b.length);
0N/A }
0N/A
51N/A @Override
0N/A public int read(byte[] b, int off, int len) throws IOException {
0N/A int rem = buffer.remaining();
0N/A if (rem > 0) {
0N/A int ret = rem < len? rem : len;
0N/A buffer.get(b, off, ret);
0N/A return ret;
0N/A } else {
0N/A if (is == null) {
0N/A return -1;
0N/A } else {
0N/A return is.read(b, off, len);
0N/A }
0N/A }
0N/A }
0N/A
51N/A @Override
0N/A public void close() throws IOException {
0N/A buffer = null;
0N/A if (is != null) {
0N/A is.close();
0N/A }
0N/A }
0N/A }
0N/A}
0N/A
0N/A/** An input stream that just returns EOF. This is for
0N/A * HTTP URLConnections that are KeepAlive && use the
0N/A * HEAD method - i.e., stream not dead, but nothing to be read.
0N/A */
0N/A
0N/Aclass EmptyInputStream extends InputStream {
0N/A
51N/A @Override
0N/A public int available() {
0N/A return 0;
0N/A }
0N/A
0N/A public int read() {
0N/A return -1;
0N/A }
0N/A}