/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* A class to represent an HTTP connection to a remote object.
*/
/* max # of allowed re-directs */
static final int maxRedirects;
/* Not all servers support the (Proxy)-Authentication-Info headers.
* By default, we don't require them to be sent
*/
static final boolean validateProxy;
static final boolean validateServer;
"cannot retry due to proxy authentication, in streaming mode";
"cannot retry due to server authentication, in streaming mode";
"cannot retry due to redirection, in streaming mode";
/*
* System properties related to error stream handling:
*
* sun.net.http.errorstream.enableBuffering = <boolean>
*
* With the above system property set to true (default is false),
* when the response code is >=400, the HTTP handler will try to
* buffer the response body (up to a certain amount and within a
* time limit). Thus freeing up the underlying socket connection
* for reuse. The rationale behind this is that usually when the
* server responds with a >=400 error (client error or server
* error, such as 404 file not found), the server will send a
* small response body to explain who to contact and what to do to
* recover. With this property set to true, even if the
* application doesn't call getErrorStream(), read the response
* body, and then call close(), the underlying socket connection
* can still be kept-alive and reused. The following two system
* properties provide further control to the error stream
* buffering behaviour.
*
* sun.net.http.errorstream.timeout = <int>
* the timeout (in millisec) waiting the error stream
* to be buffered; default is 300 ms
*
* sun.net.http.errorstream.bufferSize = <int>
* the size (in bytes) to use for the buffering the error stream;
* default is 4k
*/
/* Should we enable buffering of error streams? */
private static boolean enableESBuffer = false;
/* timeout waiting for read for buffered error stream;
*/
/* buffer size for buffered error stream;
*/
/*
* Restrict setting of request headers through the public api
* consistent with JavaScript XMLHttpRequest2 with a few
* exceptions. Disallowed headers are silently ignored for
* backwards compatibility reasons rather than throwing a
* SecurityException. For example, some applets set the
* Host header since old JREs did not implement HTTP 1.1.
* Additionally, any header starting with Sec- is
* disallowed.
*
* The following headers are allowed for historical reasons:
*
* Accept-Charset, Accept-Encoding, Cookie, Cookie2, Date,
* Referer, TE, User-Agent, headers beginning with Proxy-.
*
* The following headers are allowed in a limited form:
*
* Connection: close
*
*/
private static final boolean allowRestrictedHeaders;
/* Restricted by XMLHttpRequest2 */
//"Accept-Charset",
//"Accept-Encoding",
"Access-Control-Request-Headers",
"Access-Control-Request-Method",
"Connection", /* close is allowed */
"Content-Length",
//"Cookie",
//"Cookie2",
"Content-Transfer-Encoding",
//"Date",
//"Expect",
"Host",
"Keep-Alive",
"Origin",
// "Referer",
// "TE",
"Trailer",
"Transfer-Encoding",
"Upgrade",
//"User-Agent",
"Via"
};
static {
} else {
}
"http.auth.digest.validateProxy")).booleanValue();
"http.auth.digest.validateServer")).booleanValue();
"sun.net.http.errorstream.enableBuffering")).booleanValue();
if (timeout4ESBuffer <= 0) {
}
if (bufSize4ES <= 0) {
}
"sun.net.http.allowRestrictedHeaders"))).booleanValue();
if (!allowRestrictedHeaders) {
}
} else {
}
}
// the following http request headers should NOT have their values
// returned for security reasons.
"Proxy-Authorization",
"Authorization"
};
// also exclude system cookies when any might be set
"Proxy-Authorization",
"Authorization",
"Cookie",
"Cookie2"
};
// the cached response, and cached response headers and body
/* output stream to server */
/* buffered error stream */
/* User set Cookies */
private boolean setUserCookies = true;
/* We only have a single static authenticator for now.
* REMIND: backwards compatibility with JDK 1.1. Should be
* eliminated for JDK 2.0.
*/
/* all the headers we send
* NOTE: do *NOT* dump out the content of 'requests' in the
* output or stacktrace since it may contain security-sensitive
* headers such as those defined in EXCLUDE_HEADERS.
*/
/* The following two fields are only used with Digest Authentication */
/* Current credentials in use */
boolean needToCheck = true;
private boolean doingNTLM2ndStage = false; /* doing the 2nd stage of an NTLM server authentication */
private boolean doingNTLMp2ndStage = false; /* doing the 2nd stage of an NTLM proxy authentication */
/* try auth without calling Authenticator. Used for transparent NTLM authentication */
private boolean tryTransparentNTLMServer = true;
private boolean tryTransparentNTLMProxy = true;
/* Used by Windows specific code */
/* Set if the user is manually setting the Authorization or Proxy-Authorization headers */
boolean isUserServerAuth;
boolean isUserProxyAuth;
/* Progress source */
/* all the response headers we get back */
/* the stream _from_ the server */
/* post stream _to_ the server, if any */
/* Indicates if the std. request headers have been set in requests. */
private boolean setRequests=false;
/* Indicates whether a request has already failed or not */
private boolean failedOnce=false;
/* Remembered Exception, we will throw it again if somebody
calls getInputStream after disconnect */
/* If we decide we want to reuse a client, we put it here */
/* Tunnel states */
public enum TunnelState {
/* No tunnel */
NONE,
/* Setting up a tunnel */
/* Tunnel has been successfully setup */
}
/* Redefine timeouts from java.net.URLConnection as we need -1 to mean
* not set. This is to ensure backward compatibility.
*/
/* Logging support */
/*
* privileged request password authentication
*
*/
private static PasswordAuthentication
final InetAddress addr,
final int port,
final RequestorType authType) {
public PasswordAuthentication run() {
}
}
return pass;
}
});
}
if (allowRestrictedHeaders) {
return false;
}
/*
* Exceptions to restricted headers:
*
* Allow "Connection: close".
*/
return false;
}
return true;
return true;
}
return false;
}
/*
* Checks the validity of http message header and whether the header
* is restricted and throws IllegalArgumentException if invalid or
* restricted.
*/
return true;
}
return false;
}
/* Logging support */
return logger;
}
/* Used for Windows NTLM implementation */
return authObj;
}
}
/*
* checks the validity of http message header and throws
* IllegalArgumentException if invalid.
*/
char LF = '\n';
if (index != -1) {
throw new IllegalArgumentException(
"Illegal character(s) in message header field: " + key);
}
else {
return;
}
while (index != -1) {
index++;
if ((c==' ') || (c=='\t')) {
// ok, check the next occurrence
continue;
}
}
throw new IllegalArgumentException(
"Illegal character(s) in message header value: " + value);
}
}
}
* given PrintStream
*/
/* print all message headers in the MessageHeader
* onto the wire - all the ones we've set and any
* others that have been set
*/
// send any pre-emptive authentication
}
if (!setRequests) {
/* We're very particular about the order in which we
* set the request headers here. The order should not
* matter, but some careless CGI programs have been
* written to expect a very particular order of the
* standard headers. To name names, the order in which
* Navigator3.0 sends them. In particular, we make *sure*
* to send Content-type: <> and Content-length:<> second
* to last and last, respectively, in the case of a POST
* request.
*/
if (!failedOnce)
httpVersion, null);
if (!getUseCaches()) {
}
}
/*
* For HTTP/1.1 the default behavior is to keep connections alive.
* However, we may be talking to a 1.0 server so we should set
* keep-alive just in case, except if we have encountered an error
* or if keep alive is disabled via a system property
*/
// Try keep-alive only on first attempt
} else {
}
} else {
/*
* RFC 2616 HTTP/1.1 section 14.10 says:
* HTTP/1.1 applications that do not support persistent
* connections MUST include the "close" connection option
* in every message
*/
}
// Set modified since if necessary
long modTime = getIfModifiedSince();
if (modTime != 0 ) {
//use the preferred date format according to RFC 2068(HTTP1.1),
// RFC 822 and RFC 1123
}
// check for preemptive authorization
// Sets "Authorization"
}
}
boolean chunked = false;
if (streaming()) {
if (chunkLength != -1) {
chunked = true;
} else { /* fixed content length */
if (fixedContentLengthLong != -1) {
} else if (fixedContentLength != -1) {
}
}
synchronized (poster) {
/* close it, so no more data can be added */
}
}
if (!chunked) {
"use streaming mode for chunked encoding");
}
}
}
// get applicable cookies based on the uri and request headers
// add them to the existing request headers
setRequests=true;
}
}
if (ps.checkError()) {
if (failedOnce) {
throw new IOException("Error writing to server");
} else { // try once more
failedOnce=true;
} else {
setNewClient (url);
}
connected=true;
responses = new MessageHeader();
setRequests=false;
}
}
}
/**
* Create a new HttpClient object, bypassing the cache of
* HTTP client objects/connections.
*
* @param url the URL being accessed
*/
throws IOException {
setNewClient(url, false);
}
/**
* Obtain a HttpsClient object. Use the cached copy if specified.
*
* @param url the URL being accessed
* @param useCache whether the cached connection should be used
* if present
*/
throws IOException {
}
/**
* Create a new HttpClient object, set up so that it uses
* per-instance proxying to the given HTTP proxy. This
* bypasses the cache of HTTP client objects/connections.
*
* @param url the URL being accessed
* @param proxyHost the proxy host to use
* @param proxyPort the proxy port to use
*/
throws IOException {
}
/**
* Obtain a HttpClient object, set up so that it uses per-instance
* proxying to the given HTTP proxy. Use the cached copy of HTTP
* client objects/connections if specified.
*
* @param url the URL being accessed
* @param proxyHost the proxy host to use
* @param proxyPort the proxy port to use
* @param useCache whether the cached connection should be used
* if present
*/
boolean useCache)
throws IOException {
}
boolean useCache)
throws IOException {
connectTimeout, this);
}
throws IOException {
// we set proxy == null to distinguish this case with the case
// when per connection proxy is set
}
}
/** this constructor is used by other protocol handlers such as ftp
that want to use http to fetch urls on their behalf.*/
this(u, p, new Handler());
}
super(u);
requests = new MessageHeader();
responses = new MessageHeader();
instProxy = p;
/* Application set Proxies should not have access to cookies
* in a secure environment unless explicitly allowed. */
try {
} else {
public CookieHandler run() {
return CookieHandler.getDefault();
}
});
}
public ResponseCache run() {
return ResponseCache.getDefault();
}
});
}
/**
* @deprecated. Use java.net.Authenticator.setDefault() instead.
*/
defaultAuth = a;
}
/**
* opens a stream allowing redirects only to the same host.
*/
throws IOException
{
boolean redir;
int redirects = 0;
do {
if (c instanceof HttpURLConnection) {
((HttpURLConnection) c).setInstanceFollowRedirects(false);
}
// We want to open the input stream before
// getting headers, because getHeaderField()
// et al swallow IOExceptions.
in = c.getInputStream();
redir = false;
if (c instanceof HttpURLConnection) {
}
http.disconnect();
|| redirects >= 5)
{
throw new SecurityException("illegal URL redirect");
}
redir = true;
c = target.openConnection();
redirects++;
}
}
} while (redir);
return in;
}
//
// Same as java.net.URL.hostsEqual
//
return false;
return true;
}
// Have to resolve addresses before comparing, otherwise
// names like tachyon and tachyon.eng would compare different
final boolean result[] = {false};
try {
} catch(UnknownHostException e) {
} catch(SecurityException e) {
}
return null;
}
});
return result[0];
}
// overridden in HTTPS subclass
plainConnect();
}
private boolean checkReuseConnection () {
if (connected) {
return true;
}
if (reuseClient != null) {
http = reuseClient;
reuseClient = null;
connected = true;
return true;
}
return false;
}
if (connected) {
return;
}
// try to see if request can be served from local cache
try {
&& !(cachedResponse instanceof SecureCacheResponse)) {
}
}
if (cachedResponse != null) {
}
}
} catch (IOException ioex) {
// ignore and commence normal connection
}
connected = true;
return;
} else {
}
}
try {
/* Try to open connections using the following scheme,
* return on the first one that's successful:
* 1) if (instProxy != null)
* connect to instProxy; raise exception if failed
* 2) else use system default ProxySelector
* 3) is 2) fails, make direct connection
*/
/**
* Do we have to use a proxy?
*/
public ProxySelector run() {
return ProxySelector.getDefault();
}
});
}
Proxy p;
try {
if (!failedOnce) {
} else {
// make sure to construct new connection if first
// attempt failed
}
if (p != null) {
}
}
break;
} catch (IOException ioex) {
// fallback to direct connection
break;
}
} else {
throw ioex;
}
continue;
}
}
} else {
// No proxy selector, create http client with no proxy
if (!failedOnce) {
} else {
// make sure to construct new connection if first
// attempt failed
}
}
} else {
if (!failedOnce) {
} else {
// make sure to construct new connection if first
// attempt failed
}
}
} catch (IOException e) {
throw e;
}
// constructor to HTTP client calls openserver
connected = true;
}
// subclass HttpsClient will overwrite & return an instance of HttpsClient
throws IOException {
}
// subclass HttpsClient will overwrite & return an instance of HttpsClient
int connectTimeout, boolean useCache)
throws IOException {
}
// Expect: 100-Continue was set, so check the return code for
// Acceptance
boolean enforceTimeOut = false;
boolean timedOut = false;
if (oldTimeout <= 0) {
// 5s read timeout in case the server doesn't understand
// Expect: 100-Continue
enforceTimeOut = true;
}
try {
} catch (SocketTimeoutException se) {
if (!enforceTimeOut) {
throw se;
}
timedOut = true;
http.setIgnoreContinue(true);
}
if (!timedOut) {
// Can't use getResponseCode() yet
// Parse the response which is of the form:
// HTTP/1.1 417 Expectation Failed
// HTTP/1.1 100 Continue
responseCode = -1;
try {
// Response code is 2nd token on the line
} catch (NumberFormatException numberFormatException) {
}
}
if (responseCode != 100) {
throw new ProtocolException("Server rejected operation");
}
}
responseCode = -1;
// Proceed
}
/*
* - get output, [write output,] get input, [read input]
* - get output, [write output]
* [interpreted as GET]
* - get input, [read input]
* Disallowed:
* - get input, [read input,] get output, [write output]
*/
try {
if (!doOutput) {
throw new ProtocolException("cannot write to a URLConnection"
+ " if doOutput=false - call setDoOutput(true)");
}
}
" doesn't support output");
}
// if there's already an input stream open, throw an exception
if (inputStream != null) {
throw new ProtocolException("Cannot write output after reading input.");
}
if (!checkReuseConnection())
connect();
boolean expectContinue = false;
http.setIgnoreContinue(false);
expectContinue = true;
}
}
if (expectContinue) {
}
if (streaming()) {
if (strOutputStream == null) {
} else { /* must be fixed content length */
long length = 0L;
if (fixedContentLengthLong != -1) {
} else if (fixedContentLength != -1) {
}
}
}
return strOutputStream;
} else {
poster = new PosterOutputStream();
}
return poster;
}
} catch (RuntimeException e) {
throw e;
} catch (ProtocolException e) {
// Save the response code which may have been set while enforcing
// the 100-continue. disconnectInternal() forces it to -1
int i = responseCode;
responseCode = i;
throw e;
} catch (IOException e) {
throw e;
}
}
public boolean streaming () {
(chunkLength != -1);
}
/*
* get applicable cookies based on the uri and request headers
* add them to the existing request headers
*/
if (cookieHandler != null) {
// we only want to capture the user defined Cookies once, as
// they cannot be changed by user code after we are connected,
// only internally.
synchronized (this) {
if (setUserCookies) {
if (k != -1)
if (k != -1)
setUserCookies = false;
}
}
// remove old Cookie header before setting new one.
}
= cookieHandler.get(
}
// ignore all entries that don't have "Cookie"
// or "Cookie2" as keys
continue;
}
}
// strip off the trailing '; '
try {
} catch (StringIndexOutOfBoundsException ignored) {
// no-op
}
}
}
}
}
if (userCookies != null) {
int k;
else
}
if (userCookies2 != null) {
int k;
else
}
} // end of getting cookies
}
@SuppressWarnings("empty-statement")
if (!doInput) {
throw new ProtocolException("Cannot read from URLConnection"
+ " if doInput=false (call setDoInput(true))");
}
if (rememberedException != null) {
if (rememberedException instanceof RuntimeException)
throw new RuntimeException(rememberedException);
else {
}
}
if (inputStream != null) {
return inputStream;
}
if (streaming() ) {
if (strOutputStream == null) {
}
/* make sure stream is closed */
strOutputStream.close ();
if (!strOutputStream.writtenOK()) {
throw new IOException ("Incomplete output stream");
}
}
int redirects = 0;
int respCode = 0;
long cl = -1;
/**
* Failed Negotiate
*
* In some cases, the Negotiate auth is supported for the
* remote host but the negotiate process still fails (For
* example, if the web page is located on a backend server
* and delegation is needed but fails). The authentication
* process will start again, and we need to detect this
* kind of failure and do proper fallback (say, to NTLM).
*
* In order to achieve this, the inNegotiate flag is set
* when the first negotiate challenge is met (and reset
* if authentication is finished). If a fresh new negotiate
* challenge (no parameter) is found while inNegotiate is
* set, we know there's a failed auth attempt recently.
* Here we'll ignore the header line so that fallback
* can be practiced.
*
* inNegotiateProxy is for proxy authentication.
*/
boolean inNegotiate = false;
boolean inNegotiateProxy = false;
// If the user has set either of these headers then do not remove them
try {
do {
if (!checkReuseConnection())
connect();
if (cachedInputStream != null) {
return cachedInputStream;
}
// Check if URL should be metered
if (meteredInput) {
pi.beginTracking();
}
/* REMIND: This exists to fix the HttpsURLConnection subclass.
* Hotjava needs to run on JDK1.1FCS. Do proper fix once a
* proper solution for SSL can be found.
*/
if (!streaming()) {
}
}
}
}
respCode = getResponseCode();
if (respCode == -1) {
throw new IOException ("Invalid Http response");
}
if (respCode == HTTP_PROXY_AUTH) {
if (streaming()) {
throw new HttpRetryException (
}
// Read comments labeled "Failed Negotiate" for details.
boolean dontUseNegotiate = false;
if (!inNegotiateProxy) {
inNegotiateProxy = true;
} else {
dontUseNegotiate = true;
doingNTLMp2ndStage = false;
}
break;
}
}
// changes: add a 3rd parameter to the constructor of
// AuthenticationHeader, so that NegotiateAuthentication.
// isSupported can be tested.
// The other 2 appearances of "new AuthenticationHeader" is
// altered in similar ways.
"Proxy-Authenticate", responses,
http.getProxyPortUsed()),
);
if (!doingNTLMp2ndStage) {
if (proxyAuthentication != null) {
redirects++;
continue;
}
} else {
/* in this case, only one header field will be present */
reset ();
if (!proxyAuthentication.setHeaders(this,
throw new IOException ("Authentication failure");
}
!serverAuthentication.setHeaders(this,
throw new IOException ("Authentication failure");
}
doingNTLMp2ndStage = false;
continue;
}
} else {
inNegotiateProxy = false;
doingNTLMp2ndStage = false;
if (!isUserProxyAuth)
}
// cache proxy authentication info
if (proxyAuthentication != null) {
// cache auth info on success, domain header not relevant.
}
if (respCode == HTTP_UNAUTHORIZED) {
if (streaming()) {
throw new HttpRetryException (
}
// Read comments labeled "Failed Negotiate" for details.
boolean dontUseNegotiate = false;
if (!inNegotiate) {
inNegotiate = true;
} else {
dontUseNegotiate = true;
doingNTLM2ndStage = false;
}
break;
}
}
srvHdr = new AuthenticationHeader (
"WWW-Authenticate", responses,
new HttpCallerInfo(url),
);
if (!doingNTLM2ndStage) {
if ((serverAuthentication != null)&&
/* we can retry with the current credentials */
redirects++;
continue;
} else {
}
}
if (serverAuthentication != null) {
redirects++; // don't let things loop ad nauseum
continue;
}
} else {
reset ();
/* header not used for ntlm */
throw new IOException ("Authentication failure");
}
doingNTLM2ndStage = false;
continue;
}
}
// cache server authentication info
if (serverAuthentication != null) {
// cache auth info on success
if (!(serverAuthentication instanceof DigestAuthentication) ||
if (serverAuthentication instanceof BasicAuthentication) {
// check if the path is shorter than the existing entry
/* npath is longer, there must be a common root */
}
// remove the entry and create a new one
serverAuthentication = a;
}
} else {
// what we cache is based on the domain list in the request
while (tok.hasMoreTokens()) {
try {
/* path could be an abs_path or a complete URI */
DigestAuthentication d = new DigestAuthentication (
d.addToCache ();
} catch (Exception e) {}
}
}
}
// some flags should be reset to its initialized form so that
// even after a redirect the necessary checks can still be
// preformed.
inNegotiate = false;
inNegotiateProxy = false;
//serverAuthentication = null;
doingNTLMp2ndStage = false;
doingNTLM2ndStage = false;
if (!isUserServerAuth)
if (!isUserProxyAuth)
checkResponseCredentials (false);
} else {
needToCheck = false;
}
// a flag need to clean
needToCheck = true;
if (followRedirect()) {
/* if we should follow a redirect, then the followRedirects()
* method will disconnect() and re-connect us to the new
* location
*/
redirects++;
// redirecting HTTP response may have set cookie, so
// need to re-generate request header
continue;
}
try {
respCode == HTTP_NOT_MODIFIED ||
respCode == HTTP_NO_CONTENT) {
pi.finishTracking();
}
inputStream = new EmptyInputStream();
connected = false;
}
if (cacheHandler != null) {
// give cache a chance to save response in cache
URLConnection uconn = this;
try {
// use reflection to get to the public
// HttpsURLConnection instance saved in
// DelegateHttpsURLConnection
} catch (IllegalAccessException iae) {
// ignored; use 'this'
} catch (NoSuchFieldException nsfe) {
// ignored; use 'this'
}
}
}
}
}
}
if (!(inputStream instanceof HttpInputStream)) {
}
if (respCode >= 400) {
} else {
}
}
return inputStream;
} while (redirects < maxRedirects);
throw new ProtocolException("Server redirected too many " +
} catch (RuntimeException e) {
rememberedException = e;
throw e;
} catch (IOException e) {
rememberedException = e;
// buffer the error stream if bytes < 4k
// and it can be buffered within 1 second
}
throw e;
} finally {
if (proxyAuthKey != null) {
}
if (serverAuthKey != null) {
}
}
}
/*
* Creates a chained exception that has the same type as
* original exception and with the same message. Right now,
* there is no convenient APIs for doing so.
*/
try {
return (IOException)
.newInstance(args);
}
});
return chainedException;
return rememberedException;
}
}
// Client Error 4xx and Server Error 5xx
if (errorStream != null) {
return errorStream;
} else if (inputStream != null) {
return inputStream;
}
}
return null;
}
/**
* set or reset proxy authentication info in request headers
* after receiving a 407 error. In the case of NTLM however,
* receiving a 407 is normal and we just skip the stale check
* because ntlm does not support this feature.
*/
private AuthenticationInfo
resetProxyAuthentication(AuthenticationInfo proxyAuthentication, AuthenticationHeader auth) throws IOException {
if ((proxyAuthentication != null )&&
/* we can retry with the current credentials */
if (proxyAuthentication instanceof DigestAuthentication) {
} else {
}
} else {
}
return proxyAuthentication;
} else {
}
}
return proxyAuthentication;
}
/**
* Returns the tunnel state.
*
* @return the state
*/
return tunnelState;
}
/**
* Set the tunneling status.
*
* @param the state
*/
this.tunnelState = tunnelState;
}
/**
* establish a tunnel through proxy server
*/
int retryTunnel = 0;
int respCode = 0;
int proxyPort = -1;
// save current requests so that they can be restored after tunnel is setup.
requests = new MessageHeader();
// Read comments labeled "Failed Negotiate" for details.
boolean inNegotiateProxy = false;
try {
/* Actively setting up a tunnel */
do {
if (!checkReuseConnection()) {
}
// send the "CONNECT" request to establish a tunnel
// through proxy server
// There is no need to track progress in HTTP Tunneling,
// so ProgressSource is null.
/* Log the response to the CONNECT */
}
}
}
if (respCode == HTTP_PROXY_AUTH) {
// Read comments labeled "Failed Negotiate" for details.
boolean dontUseNegotiate = false;
if (!inNegotiateProxy) {
inNegotiateProxy = true;
} else {
dontUseNegotiate = true;
doingNTLMp2ndStage = false;
}
break;
}
}
"Proxy-Authenticate", responses,
http.getProxyPortUsed()),
);
if (!doingNTLMp2ndStage) {
if (proxyAuthentication != null) {
retryTunnel++;
continue;
}
} else {
reset ();
if (!proxyAuthentication.setHeaders(this,
throw new IOException ("Authentication failure");
}
doingNTLMp2ndStage = false;
continue;
}
}
// cache proxy authentication info
if (proxyAuthentication != null) {
// cache auth info on success, domain header not relevant.
}
break;
}
// we don't know how to deal with other response code
// so disconnect and report error
break;
} while (retryTunnel < maxRedirects);
throw new IOException("Unable to tunnel through proxy."+
" Proxy returns \"" +
statusLine + "\"");
}
} finally {
if (proxyAuthKey != null) {
}
}
// restore original request headers
// reset responses
}
}
/**
* send a CONNECT request for establishing a tunnel to proxy server
*/
}
// Not really necessary for a tunnel, but can't hurt
if (http.getHttpKeepAliveSet()) {
}
/* Log the CONNECT request */
}
}
/**
* Sets pre-emptive proxy authentication in header
*/
http.getProxyPortUsed());
if (pauth instanceof DigestAuthentication) {
} else {
}
} else {
}
// Sets "Proxy-authorization"
}
}
/**
* Gets the authentication for an HTTP proxy, and applies it to
* the connection.
*/
/* get authorization from authenticator */
authScheme = BASIC;
authScheme = DIGEST;
authScheme = NTLM;
doingNTLMp2ndStage = true;
doingNTLMp2ndStage = true;
doingNTLMp2ndStage = true;
}
realm = "";
switch (authScheme) {
case BASIC:
try {
public InetAddress run()
}
});
// User will have an unknown host.
}
if (a != null) {
}
break;
case DIGEST:
if (a != null) {
new DigestAuthentication.Parameters();
}
break;
case NTLM:
/* tryTransparentNTLMProxy will always be true the first
* time around, but verify that the platform supports it
* otherwise don't try. */
if (tryTransparentNTLMProxy) {
}
a = null;
if (tryTransparentNTLMProxy) {
} else {
}
/* If we are not trying transparent authentication then
* we need to have a PasswordAuthentication instance. For
* transparent authentication (Windows only) the username
* and password will be picked up from the current logged
* on users credentials.
*/
if (tryTransparentNTLMProxy ||
(!tryTransparentNTLMProxy && a != null)) {
}
/* set to false so that we do not try again */
tryTransparentNTLMProxy = false;
}
break;
case NEGOTIATE:
break;
case KERBEROS:
break;
case UNKNOWN:
default:
throw new AssertionError("should not reach here");
}
}
// For backwards compatibility, we also try defaultAuth
// REMIND: Get rid of this for JDK2.0.
try {
if (a != null) {
// not in cache by default - cache on success
}
}
}
}
}
}
logger.finer("Proxy Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
}
return ret;
}
/**
* Gets the authentication for an HTTP server, and applies it to
* the connection.
* @param authHdr the AuthenticationHeader which tells what auth scheme is
* prefered.
*/
/* get authorization from authenticator */
/* When we get an NTLM auth from cache, don't set any special headers */
authScheme = BASIC;
authScheme = DIGEST;
authScheme = NTLM;
doingNTLM2ndStage = true;
doingNTLM2ndStage = true;
doingNTLM2ndStage = true;
}
realm = "";
try {
// User will have addr = null
}
}
// replacing -1 with default port for a protocol
if (port == -1) {
}
switch(authScheme) {
case KERBEROS:
break;
case NEGOTIATE:
break;
case BASIC:
if (a != null) {
}
break;
case DIGEST:
if (a != null) {
}
break;
case NTLM:
try {
} catch (Exception e) {
}
/* tryTransparentNTLMServer will always be true the first
* time around, but verify that the platform supports it
* otherwise don't try. */
if (tryTransparentNTLMServer) {
/* If the platform supports transparent authentication
* then check if we are in a secure environment
* whether, or not, we should try transparent authentication.*/
if (tryTransparentNTLMServer) {
}
}
a = null;
if (tryTransparentNTLMServer) {
} else {
}
/* If we are not trying transparent authentication then
* we need to have a PasswordAuthentication instance. For
* transparent authentication (Windows only) the username
* and password will be picked up from the current logged
* on users credentials.
*/
if (tryTransparentNTLMServer ||
(!tryTransparentNTLMServer && a != null)) {
}
/* set to false so that we do not try again */
tryTransparentNTLMServer = false;
}
break;
case UNKNOWN:
default:
throw new AssertionError("should not reach here");
}
}
// For backwards compatibility, we also try defaultAuth
// REMIND: Get rid of this for JDK2.0.
if (a != null) {
// not in cache by default - cache on success
}
}
}
}
}
logger.finer("Server Authentication for " + authhdr.toString() +" returned " + (ret != null ? ret.toString() : "null"));
}
return ret;
}
/* inclose will be true if called from close(), in which case we
* force the call to check because this is the last chance to do so.
* If not in close(), then the authentication info could arrive in a trailer
* field, which we have not read yet.
*/
try {
if (!needToCheck)
return;
(currentProxyCredentials instanceof DigestAuthentication)) {
}
}
(currentServerCredentials instanceof DigestAuthentication)) {
}
}
needToCheck = false;
}
} catch (IOException e) {
connected = false;
throw e;
}
}
/* The request URI used in the request line for this request.
* Also, needed for digest authentication
*/
if (requestURI == null) {
}
return requestURI;
}
/* Tells us whether to follow a redirect. If so, it
* closes the connection (break any keep-alive) and
* resets the url, re-connects, and resets the request
* property.
*/
if (!getInstanceFollowRedirects()) {
return false;
}
int stat = getResponseCode();
|| stat == HTTP_NOT_MODIFIED) {
return false;
}
/* this should be present - if not, we have no choice
* but to go forward w/ the response we got
*/
return false;
}
try {
return false;
}
} catch (MalformedURLException mue) {
// treat loc as a relative URI to conform to popular browsers
}
if (streaming()) {
}
}
// clear out old response headers!!!!
responses = new MessageHeader();
if (stat == HTTP_USE_PROXY) {
/* This means we must re-request the resource through the
* proxy denoted in the "Location:" field of the response.
* Judging by the spec, the string in the Location header
* _should_ denote a URL - let's hope for "http://my.proxy.org"
* Make a new HttpClient to the proxy, using HttpClient's
* Instance-specific proxy fields, but note we're still fetching
* the same URL.
*/
}
httpVersion, null);
connected = true;
} else {
// maintain previous headers, just change the name
// of the file we're getting
/* The HTTP/1.1 spec says that a redirect from a POST
* *should not* be immediately turned into a GET, and
* that some HTTP/1.0 clients incorrectly did this.
* Correct behavior redirects a POST to another POST.
* Unfortunately, since most browsers have this incorrect
* behavior, the web works this way now. Typical usage
* seems to be:
* POST a login code or passwd to a web page.
* after validation, the server redirects to another
* (welcome) page
* The second request is (erroneously) expected to be GET
*
* We will do the incorrect thing (POST-->GET) by default.
* We will provide the capability to do the "right" thing
* (POST-->POST) by a system property, "http.strictPostRedirect=true"
*/
requests = new MessageHeader();
setRequests = false;
setRequestMethod("GET");
if (!checkReuseConnection())
connect();
} else {
if (!checkReuseConnection())
connect();
/* Even after a connect() call, http variable still can be
* null, if a ResponseCache has been installed and it returns
* a non-null CacheResponse instance. So check nullity before using it.
*
* And further, if http is null, there's no need to do anything
* about request headers because successive http session will use
* cachedInputStream/cachedHeaders anyway, which is returned by
* CacheResponse.
*/
httpVersion, null);
}
}
}
}
return true;
}
/* dummy byte buffer for reading off socket prior to closing */
/**
* Reset (without disconnecting the TCP conn) in order to do another transaction with this instance
*/
/* must save before calling close */
reuseClient = http;
try {
/* we want to read the rest of the response without using the
* hurry mechanism, because that would close the connection
* if everything is not available immediately
*/
if ((is instanceof ChunkedInputStream) ||
(is instanceof MeteredStream)) {
/* reading until eof will not block */
} else {
/* raw stream, which will block on read, so only read
* the expected number of bytes, probably 0
*/
long cl = 0;
int n = 0;
try {
} catch (NumberFormatException e) {
cl = 0;
}
}
for (long i=0; i<cl; ) {
break;
} else {
i+= n;
}
}
}
} catch (IOException e) {
reuseClient = null;
return;
}
try {
if (is instanceof MeteredStream) {
}
} catch (IOException e) { }
}
responseCode = -1;
responses = new MessageHeader();
connected = false;
}
/**
* Disconnect from the web server at the first 401 error. Do not
* disconnect when using a proxy, a good proxy should have already
* closed the connection to the web server.
*/
responseCode = -1;
// clean up, particularly, skip the content part
// of a 401 error response
reset();
} else {
}
}
/**
* Disconnect from the server (for internal use)
*/
private void disconnectInternal() {
responseCode = -1;
inputStream = null;
pi.finishTracking();
}
http.closeServer();
connected = false;
}
}
/**
* Disconnect from the server (public API)
*/
public void disconnect() {
responseCode = -1;
pi.finishTracking();
}
/*
* If we have an input stream this means we received a response
* from the server. That stream may have been read to EOF and
* dependening on the stream type may already be closed or the
* the http client may be returned to the keep-alive cache.
* If the http client has been returned to the keep-alive cache
* it may be closed (idle timeout) or may be allocated to
* another request.
*
* In other to avoid timing issues we close the input stream
* which will either close the underlying connection or return
* the client to the cache. If there's a possibility that the
* client has been returned to the cache (ie: stream is a keep
* alive stream or a chunked input stream) then we remove an
* idle connection to the server. Note that this approach
* can be considered an approximation in that we may close a
* different idle connection to that used by the request.
* Additionally it's possible that we close two connections
* - the first becuase it wasn't an EOF (and couldn't be
* hurried) - the second, another idle connection to the
* same server. The is okay because "disconnect" is an
* indication that the application doesn't intend to access
* this http server for a while.
*/
if (inputStream != null) {
// un-synchronized
try {
inputStream.close();
} catch (IOException ioe) { }
// if the connection is persistent it may have been closed
// or returned to the keep-alive cache. If it's been returned
// to the keep-alive cache then we would like to close it
// but it may have been allocated
if (ka) {
}
} else {
// We are deliberatly being disconnected so HttpClient
// should not try to resend the request no matter what stage
// of the connection we are in.
http.setDoNotRetry(true);
http.closeServer();
}
// poster = null;
connected = false;
}
if (cachedHeaders != null) {
}
}
public boolean usingProxy() {
}
return false;
}
// constant strings represent set-cookie header names
/**
* Returns a filtered version of the given headers value.
*
* Note: The implementation currently only filters out HttpOnly cookies
* from Set-Cookie and Set-Cookie2 headers.
*/
return null;
// Filtering only if there is a cookie handler. [Assumption: the
if (cookieHandler == null)
return value;
boolean multipleCookies = false;
// skip HttpOnly cookies
if (cookie.isHttpOnly())
continue;
if (multipleCookies)
multipleCookies = true;
}
}
return value;
}
// Cache the filtered response headers so that they don't need
// to be generated for every getHeaderFields() call.
if (filteredHeaders != null)
return filteredHeaders;
if (cachedHeaders != null)
else
}
if (!filteredVals.isEmpty())
}
}
/**
* Gets a header field by name. Returns null if not known.
* @param name the name of the header field
*/
try {
} catch (IOException e) {}
if (cachedHeaders != null) {
}
}
/**
* Returns an unmodifiable Map of the header fields.
* The Map keys are Strings that represent the
* response-header field names. Each Map value is an
* unmodifiable List of Strings that represents
* the corresponding field values.
*
* @return a Map of header fields
* @since 1.4
*/
try {
} catch (IOException e) {}
return getFilteredHeaderFields();
}
/**
* Gets a header field by index. Returns null if not known.
* @param n the index of the header field
*/
try {
} catch (IOException e) {}
if (cachedHeaders != null) {
cachedHeaders.getValue(n));
}
}
/**
* Gets a header field by index. Returns null if not known.
* @param n the index of the header field
*/
try {
} catch (IOException e) {}
if (cachedHeaders != null) {
return cachedHeaders.getKey(n);
}
}
/**
* Sets request property. If a property with the key already
* exists, overwrite its value with the new value.
* @param value the value to be set
*/
if (connected)
throw new IllegalStateException("Already connected");
throw new NullPointerException ("key is null");
}
}
/**
* Adds a general request property specified by a
* key-value pair. This method will not overwrite
* existing values associated with the same key.
*
* @param key the keyword by which the request is known
* (e.g., "<code>accept</code>").
* @param value the value associated with it.
* @see #getRequestProperties(java.lang.String)
* @since 1.4
*/
if (connected)
throw new IllegalStateException("Already connected");
throw new NullPointerException ("key is null");
}
}
//
// Set a property for authentication. This can safely disregard
// the connected test.
//
}
return null;
}
// don't return headers containing security sensitive information
return null;
}
}
if (!setUserCookies) {
return userCookies;
}
return userCookies2;
}
}
}
/**
* Returns an unmodifiable Map of general request
* properties for this connection. The Map keys
* are Strings that represent the request-header
* field names. Each Map value is a unmodifiable List
* of Strings that represents the corresponding
* field values.
*
* @return a Map of the general request properties for this connection.
* @throws IllegalStateException if already connected
* @since 1.4
*/
if (connected)
throw new IllegalStateException("Already connected");
// exclude headers containing security-sensitive info
if (setUserCookies) {
}
/*
* The cookies in the requests message headers may have
* been modified. Use the saved user cookies instead.
*/
userCookiesMap = new HashMap();
if (userCookies != null) {
}
if (userCookies2 != null) {
}
}
}
if (timeout < 0)
throw new IllegalArgumentException("timeouts can't be negative");
}
/**
* Returns setting for connect timeout.
* <p>
* 0 return implies that the option is disabled
* (i.e., timeout of infinity).
*
* @return an <code>int</code> that indicates the connect timeout
* value in milliseconds
* @see java.net.URLConnection#setConnectTimeout(int)
* @see java.net.URLConnection#connect()
* @since 1.5
*/
public int getConnectTimeout() {
}
/**
* Sets the read timeout to a specified timeout, in
* milliseconds. A non-zero value specifies the timeout when
* reading from Input stream when a connection is established to a
* resource. If the timeout expires before there is data available
* for read, a java.net.SocketTimeoutException is raised. A
* timeout of zero is interpreted as an infinite timeout.
*
* <p> Some non-standard implementation of this method ignores the
* specified timeout. To see the read timeout set, please call
* getReadTimeout().
*
* @param timeout an <code>int</code> that specifies the timeout
* value to be used in milliseconds
* @throws IllegalArgumentException if the timeout parameter is negative
*
* @see java.net.URLConnectiongetReadTimeout()
* @see java.io.InputStream#read()
* @since 1.5
*/
if (timeout < 0)
throw new IllegalArgumentException("timeouts can't be negative");
}
/**
* Returns setting for read timeout. 0 return implies that the
* option is disabled (i.e., timeout of infinity).
*
* @return an <code>int</code> that indicates the read timeout
* value in milliseconds
*
* @see java.net.URLConnection#setReadTimeout(int)
* @see java.io.InputStream#read()
* @since 1.5
*/
public int getReadTimeout() {
}
return cookieHandler;
}
return method;
}
return headers;
}
} else {
}
}
}
return headers;
}
/* The purpose of this wrapper is just to capture the close() call
* so we can check authentication information that may have
* arrived in a Trailer field
*/
private boolean marked = false;
super (is);
this.cacheRequest = null;
this.outputStream = null;
}
super (is);
this.cacheRequest = cacheRequest;
try {
} catch (IOException ioex) {
this.cacheRequest.abort();
this.cacheRequest = null;
this.outputStream = null;
}
}
/**
* Marks the current position in this input stream. A subsequent
* call to the <code>reset</code> method repositions this stream at
* the last marked position so that subsequent reads re-read the same
* bytes.
* <p>
* The <code>readlimit</code> argument tells this input stream to
* allow that many bytes to be read before the mark position gets
* invalidated.
* <p>
* This method simply performs <code>in.mark(readlimit)</code>.
*
* @param readlimit the maximum limit of bytes that can be read before
* the mark position becomes invalid.
* @see java.io.FilterInputStream#in
* @see java.io.FilterInputStream#reset()
*/
if (cacheRequest != null) {
marked = true;
markCount = 0;
}
}
/**
* Repositions this stream to the position at the time the
* <code>mark</code> method was last called on this input stream.
* <p>
* This method
* simply performs <code>in.reset()</code>.
* <p>
* Stream marks are intended to be used in
* situations where you need to read ahead a little to see what's in
* the stream. Often this is most easily done by invoking some
* general parser. If the stream is of the type handled by the
* parse, it just chugs along happily. If the stream is not of
* that type, the parser should toss an exception when it fails.
* If this happens within readlimit bytes, it allows the outer
* code to reset the stream and try another parser.
*
* @exception IOException if the stream has not been marked or if the
* mark has been invalidated.
* @see java.io.FilterInputStream#in
* @see java.io.FilterInputStream#mark(int)
*/
super.reset();
if (cacheRequest != null) {
marked = false;
}
}
try {
byte[] b = new byte[1];
} catch (IOException ioex) {
if (cacheRequest != null) {
}
throw ioex;
}
}
}
try {
int nWrite;
// write to cache
if (inCache > 0) {
nWrite = 0;
} else {
inCache = 0;
}
} else {
}
if (marked) {
}
return newLen;
} catch (IOException ioex) {
if (cacheRequest != null) {
}
throw ioex;
}
}
/* skip() calls read() in order to ensure that entire response gets
* cached. same implementation as InputStream.skip */
private byte[] skipBuffer;
long remaining = n;
int nr;
if (skipBuffer == null)
skipBuffer = new byte[SKIP_BUFFER_SIZE];
byte[] localSkipBuffer = skipBuffer;
if (n <= 0) {
return 0;
}
while (remaining > 0) {
if (nr < 0) {
break;
}
}
return n - remaining;
}
try {
if (outputStream != null) {
if (read() != -1) {
} else {
}
}
super.close ();
} catch (IOException ioex) {
if (cacheRequest != null) {
}
throw ioex;
} finally {
checkResponseCredentials (true);
}
}
}
long expected;
long written;
boolean closed;
boolean error;
/**
* expectedLength == -1 if the stream is chunked
* expectedLength > 0 if the stream is fixed content-length
* In the 2nd case, we make sure the expected number of
* of bytes are actually written
*/
super (os);
written = 0L;
closed = false;
error = false;
}
checkError();
written ++;
throw new IOException ("too many bytes written");
}
}
}
checkError();
throw new IOException ("too many bytes written");
}
}
if (closed) {
throw new IOException ("Stream is closed");
}
if (error) {
throw errorExcp;
}
throw new IOException("Error writing request body to server");
}
}
/* this is called to check that all the bytes
* that were supposed to be written were written
* and that the stream is now closed().
*/
boolean writtenOK () {
}
if (closed) {
return;
}
closed = true;
if (expected != -1L) {
/* not chunked */
error = true;
throw errorExcp;
}
super.flush(); /* can't close the socket */
} else {
/* chunked */
super.close (); /* force final chunk to be written */
/* trailing \r\n */
o.write ('\r');
o.write ('\n');
o.flush();
}
}
}
}
}
// when this method is called, it's either the case that cl > 0, or
// if chunk-encoded, cl = -1; in other words, cl can't be 0
// cl can't be 0; this following is here for extra precaution
if (cl == 0) {
return null;
}
try {
// set SO_TIMEOUT to 1/5th of the total timeout
// remember the old timeout value so that we can restore it
long expected = 0;
boolean isChunked = false;
// the chunked case
if (cl < 0) {
isChunked = true;
} else {
}
if (expected <= bufSize4ES) {
do {
try {
if (len < 0) {
if (isChunked) {
// chunked ended
// if chunked ended prematurely,
// an IOException would be thrown
break;
}
// the server sends less than cl bytes of data
throw new IOException("the server closes"+
" before sending "+cl+
" bytes of data");
}
} catch (SocketTimeoutException ex) {
}
// reset SO_TIMEOUT to old value
// if count < cl at this point, we will not try to reuse
// the connection
if (count == 0) {
// since we haven't read anything,
// we will return the underlying
// inputstream back to the application
return null;
// put the connection into keep-alive cache
// the inputstream will try to do the right thing
} else {
// we read part of the response body
return new ErrorStream(
}
}
return null;
} catch (IOException ioex) {
// ioex.printStackTrace();
return null;
}
}
} else {
}
}
byte[] b = new byte[1];
}
}
if (rem > 0) {
return ret;
} else {
return -1;
} else {
}
}
}
}
}
}
}
/** An input stream that just returns EOF. This is for
* HTTP URLConnections that are KeepAlive && use the
* HEAD method - i.e., stream not dead, but nothing to be read.
*/
public int available() {
return 0;
}
public int read() {
return -1;
}
}