Implemented authentication support for tunneling requests via an authenticating proxy

git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@539780 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2007-05-19 17:19:49 +00:00
parent 2dc964aef6
commit 7f12408be7
1 changed files with 98 additions and 27 deletions

View File

@ -68,6 +68,7 @@ import org.apache.http.client.methods.AbortableHttpRequest;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.HttpClientParams; import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.protocol.RequestProxyAuthentication;
import org.apache.http.conn.BasicManagedEntity; import org.apache.http.conn.BasicManagedEntity;
import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ConnectionPoolTimeoutException; import org.apache.http.conn.ConnectionPoolTimeoutException;
@ -80,11 +81,13 @@ import org.apache.http.message.BasicHttpRequest;
import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams; import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams; import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpExecutionContext; import org.apache.http.protocol.HttpExecutionContext;
import org.apache.http.protocol.HttpProcessor; import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpRequestExecutor; import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.protocol.RequestUserAgent;
import org.apache.http.util.CharArrayBuffer; import org.apache.http.util.CharArrayBuffer;
/** /**
@ -267,6 +270,8 @@ public class DefaultClientRequestDirector
orig.addHeader((Header) it.next()); orig.addHeader((Header) it.next());
} }
} }
long timeout = HttpClientParams.getConnectionManagerTimeout(params);
int execCount = 0; int execCount = 0;
@ -279,7 +284,7 @@ public class DefaultClientRequestDirector
// Allocate connection if needed // Allocate connection if needed
if (managedConn == null) { if (managedConn == null) {
managedConn = allocateConnection(route); managedConn = allocateConnection(route, timeout);
} }
// Reopen connection if needed // Reopen connection if needed
if (!managedConn.isOpen()) { if (!managedConn.isOpen()) {
@ -438,10 +443,9 @@ public class DefaultClientRequestDirector
* *
* @throws HttpException in case of a problem * @throws HttpException in case of a problem
*/ */
protected ManagedClientConnection allocateConnection(HttpRoute route) protected ManagedClientConnection allocateConnection(HttpRoute route, long timeout)
throws HttpException, ConnectionPoolTimeoutException { throws HttpException, ConnectionPoolTimeoutException {
long timeout = HttpClientParams.getConnectionManagerTimeout(params);
return connManager.getConnection(route, timeout); return connManager.getConnection(route, timeout);
} // allocateConnection } // allocateConnection
@ -468,15 +472,11 @@ public class DefaultClientRequestDirector
//@@@ probably not, because the parent is replaced //@@@ probably not, because the parent is replaced
//@@@ just make sure we don't link parameters to themselves //@@@ just make sure we don't link parameters to themselves
//System.out.println("@@@ planned: " + route);
RouteDirector rowdy = new RouteDirector(); RouteDirector rowdy = new RouteDirector();
int step; int step;
do { do {
HttpRoute fact = managedConn.getRoute(); HttpRoute fact = managedConn.getRoute();
//System.out.println("@@@ current: " + fact);
step = rowdy.nextStep(route, fact); step = rowdy.nextStep(route, fact);
//System.out.println("@@@ action => " + step);
switch (step) { switch (step) {
@ -487,6 +487,7 @@ public class DefaultClientRequestDirector
case RouteDirector.CREATE_TUNNEL: case RouteDirector.CREATE_TUNNEL:
boolean secure = createTunnel(route, context); boolean secure = createTunnel(route, context);
LOG.debug("Tunnel created");
managedConn.tunnelCreated(secure, requestExec.getParams()); managedConn.tunnelCreated(secure, requestExec.getParams());
break; break;
@ -536,29 +537,106 @@ public class DefaultClientRequestDirector
protected boolean createTunnel(HttpRoute route, HttpContext context) protected boolean createTunnel(HttpRoute route, HttpContext context)
throws HttpException, IOException { throws HttpException, IOException {
HttpRequest connect = createConnectRequest(route, context); HttpHost proxy = route.getProxyHost();
//@@@ authenticate here, in method above, or in request interceptor? HttpResponse response = null;
boolean done = false;
while (!done) {
HttpResponse response = done = true;
requestExec.execute(connect, managedConn, context);
managedConn.markReusable(); if (!this.managedConn.isOpen()) {
int status = response.getStatusLine().getStatusCode(); this.managedConn.open(route, context, this.params);
}
HttpRequest connect = createConnectRequest(route, context);
String agent = HttpProtocolParams.getUserAgent(params);
if (agent != null) {
connect.addHeader(HTTP.USER_AGENT, agent);
}
AuthScheme authScheme = this.proxyAuthState.getAuthScheme();
AuthScope authScope = this.proxyAuthState.getAuthScope();
Credentials creds = this.proxyAuthState.getCredentials();
if (creds != null) {
if (authScope != null || !authScheme.isConnectionBased()) {
try {
connect.addHeader(authScheme.authenticate(creds, connect));
} catch (AuthenticationException ex) {
if (LOG.isErrorEnabled()) {
LOG.error("Proxy authentication error: " + ex.getMessage());
}
}
}
}
response = requestExec.execute(connect, this.managedConn, context);
int status = response.getStatusLine().getStatusCode();
if (status < 200) {
throw new HttpException("Unexpected response to CONNECT request: " +
response.getStatusLine());
}
HttpState state = (HttpState) context.getAttribute(HttpClientContext.HTTP_STATE);
if (state != null && HttpClientParams.isAuthenticating(params)) {
if (this.authHandler.isProxyAuthenticationRequested(response, context)) {
if (status < 200) { LOG.debug("Proxy requested authentication");
throw new HttpException("Unexpected response to CONNECT request: " + Map challenges = this.authHandler.getProxyChallenges(response, context);
response.getStatusLine()); try {
processChallenges(challenges, this.proxyAuthState, response, context);
} catch (AuthenticationException ex) {
if (LOG.isWarnEnabled()) {
LOG.warn("Authentication error: " + ex.getMessage());
break;
}
}
updateAuthState(this.proxyAuthState, proxy, state);
if (this.proxyAuthState.getCredentials() != null) {
done = false;
// Retry request
if (this.reuseStrategy.keepAlive(response, context)) {
LOG.debug("Connection kept alive");
// Consume response content
HttpEntity entity = response.getEntity();
if (entity != null) {
entity.consumeContent();
}
} else {
this.managedConn.close();
}
}
} else {
// Reset proxy auth scope
this.proxyAuthState.setAuthScope(null);
}
}
} }
// Buffer response int status = response.getStatusLine().getStatusCode();
if (response.getEntity() != null) {
response.setEntity(new BufferedHttpEntity(response.getEntity()));
}
if (status > 299) { if (status > 299) {
// Buffer response content
HttpEntity entity = response.getEntity();
if (entity != null) {
response.setEntity(new BufferedHttpEntity(entity));
}
this.managedConn.close();
throw new TunnelRefusedException("CONNECT refused by proxy: " + throw new TunnelRefusedException("CONNECT refused by proxy: " +
response.getStatusLine(), response); response.getStatusLine(), response);
} }
this.managedConn.markReusable();
// How to decide on security of the tunnelled connection? // How to decide on security of the tunnelled connection?
// The socket factory knows only about the segment to the proxy. // The socket factory knows only about the segment to the proxy.
// Even if that is secure, the hop to the target may be insecure. // Even if that is secure, the hop to the target may be insecure.
@ -602,13 +680,6 @@ public class DefaultClientRequestDirector
HttpRequest req = new BasicHttpRequest HttpRequest req = new BasicHttpRequest
("CONNECT", authority, ver); ("CONNECT", authority, ver);
String agent = HttpProtocolParams.getUserAgent(params);
if (agent != null) {
req.addHeader(HTTP.USER_AGENT, agent);
}
//@@@ authenticate here, in caller, or in request interceptor?
return req; return req;
} }