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:
parent
2dc964aef6
commit
7f12408be7
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue