HTTPCLIENT-649: ManagedClientConnection now supports proxy chains

git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@573844 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Roland Weber 2007-09-08 14:20:53 +00:00
parent 7738386c10
commit efe4b9c9c0
6 changed files with 156 additions and 23 deletions

View File

@ -1,5 +1,8 @@
Changes since release 4.0 Alpha 1
* [HTTPCLIENT-649] support for proxy chains in HttpConn
Contributed by Roland Weber <rolandw at apache.org>
* [HTTPCLIENT-636] refactor ThreadSafeClientConnManager in separate package
Contributed by Roland Weber <rolandw at apache.org>

View File

@ -35,6 +35,7 @@ import java.io.IOException;
import org.apache.http.HttpClientConnection;
import org.apache.http.HttpInetConnection;
import org.apache.http.HttpHost;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
@ -80,8 +81,8 @@ public interface ManagedClientConnection extends
/**
* Opens this connection according to the given route.
*
* @param route the route along which to open. It will be opened
* to the proxy if present, or directly to the target.
* @param route the route along which to open. It will be opened to
* the first proxy if present, or directly to the target.
* @param context the context for opening this connection
* @param params the parameters for opening this connection
*
@ -93,7 +94,7 @@ public interface ManagedClientConnection extends
/**
* Indicates that a tunnel has been established.
* Indicates that a tunnel to the target has been established.
* The route is the one previously passed to {@link #open open}.
* Subsequently, {@link #layerProtocol layerProtocol} can be called
* to layer the TLS/SSL protocol on top of the tunnelled connection.
@ -109,7 +110,32 @@ public interface ManagedClientConnection extends
*
* @throws IOException in case of a problem
*/
void tunnelCreated(boolean secure, HttpParams params)
void tunnelTarget(boolean secure, HttpParams params)
throws IOException
;
/**
* Indicates that a tunnel to an intermediate proxy has been established.
* This is used exclusively for so-called <i>proxy chains</i>, where
* a request has to pass through multiple proxies before reaching the
* target. In that case, all proxies but the last need to be tunnelled
* when establishing the connection. Tunnelling of the last proxy to the
* target is optional and would be indicated via {@link #tunnelTarget}.
*
* @param next the proxy to which the tunnel was established.
* This is <i>not</i> the proxy <i>through</i> which
* the tunnel was established, but the new end point
* of the tunnel. The tunnel does <i>not</i> yet
* reach to the target, use {@link #tunnelTarget}
* to indicate an end-to-end tunnel.
* @param secure <code>true</code> if the connection should be
* considered secure, <code>false</code> otherwise
* @param params the parameters for tunnelling this connection
*
* @throws IOException in case of a problem
*/
void tunnelProxy(HttpHost next, boolean secure, HttpParams params)
throws IOException
;

View File

@ -439,7 +439,8 @@ public class DefaultClientRequestDirector
*
* @throws HttpException in case of a problem
*/
protected ManagedClientConnection allocateConnection(HttpRoute route, long timeout)
protected ManagedClientConnection allocateConnection(HttpRoute route,
long timeout)
throws HttpException, ConnectionPoolTimeoutException {
return connManager.getConnection(route, timeout);
@ -481,15 +482,24 @@ public class DefaultClientRequestDirector
managedConn.open(route, context, this.params);
break;
case HttpRouteDirector.TUNNEL_TARGET:
boolean secure = createTunnel(route, context);
LOG.debug("Tunnel created");
managedConn.tunnelCreated(secure, this.params);
break;
case HttpRouteDirector.TUNNEL_TARGET: {
boolean secure = createTunnelToTarget(route, context);
LOG.debug("Tunnel to target created.");
managedConn.tunnelTarget(secure, this.params);
} break;
case HttpRouteDirector.TUNNEL_PROXY: {
// The most simple example for this case is a proxy chain
// of two proxies, where P1 must be tunnelled to P2.
// route: Source -> P1 -> P2 -> Target (3 hops)
// fact: Source -> P1 -> Target (2 hops)
final int hop = fact.getHopCount()-1; // the hop to establish
boolean secure = createTunnelToProxy(route, hop, context);
LOG.debug("Tunnel to proxy created.");
managedConn.tunnelProxy(route.getHopTarget(hop),
secure, this.params);
} break;
case HttpRouteDirector.TUNNEL_PROXY:
throw new UnsupportedOperationException
("Proxy chains are not supported.");
case HttpRouteDirector.LAYER_PROTOCOL:
managedConn.layerProtocol(context, this.params);
@ -516,8 +526,8 @@ public class DefaultClientRequestDirector
/**
* Creates a tunnel.
* The connection must be established to the proxy.
* Creates a tunnel to the target server.
* The connection must be established to the (last) proxy.
* A CONNECT request for tunnelling through the proxy will
* be created and sent, the response received and checked.
* This method does <i>not</i> update the connection with
@ -534,7 +544,8 @@ public class DefaultClientRequestDirector
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
*/
protected boolean createTunnel(HttpRoute route, HttpContext context)
protected boolean createTunnelToTarget(HttpRoute route,
HttpContext context)
throws HttpException, IOException {
HttpHost proxy = route.getProxyHost();
@ -645,9 +656,47 @@ public class DefaultClientRequestDirector
// Even if that is secure, the hop to the target may be insecure.
// Leave it to derived classes, consider insecure by default here.
return false;
} // createTunnelToTarget
/**
* Creates a tunnel to an intermediate proxy.
* This method is <i>not</i> implemented in this class.
* It just throws an exception here.
*
* @param route the route to establish
* @param hop the hop in the route to establish now.
* <code>route.getHopTarget(hop)</code>
* will return the proxy to tunnel to.
* @param context the context for request execution
*
* @return <code>true</code> if the partially tunnelled connection
* is secure, <code>false</code> otherwise.
*
* @throws HttpException in case of a problem
* @throws IOException in case of an IO problem
*/
protected boolean createTunnelToProxy(HttpRoute route, int hop,
HttpContext context)
throws HttpException, IOException {
// Have a look at createTunnelToTarget and replicate the parts
// you need in a custom derived class. If your proxies don't require
// authentication, it is not too hard. But for the stock version of
// HttpClient, we cannot make such simplifying assumptions and would
// have to include proxy authentication code. The HttpComponents team
// is currently not in a position to support rarely used code of this
// complexity. Feel free to submit patches that refactor the code in
// createTunnelToTarget to facilitate re-use for proxy tunnelling.
throw new UnsupportedOperationException
("Proxy chains are not supported.");
}
/**
* Creates the CONNECT request for tunnelling.
* Called by {@link #createTunnel createTunnel}.

View File

@ -161,9 +161,9 @@ public abstract class AbstractPoolEntry {
/**
* Tracks tunnelling of the connection.
* Tracks tunnelling of the connection to the target.
* The tunnel has to be established outside by sending a CONNECT
* request to the proxy.
* request to the (last) proxy.
*
* @param secure <code>true</code> if the tunnel should be
* considered secure, <code>false</code> otherwise
@ -171,7 +171,7 @@ public abstract class AbstractPoolEntry {
*
* @throws IOException in case of a problem
*/
public void tunnelCreated(boolean secure, HttpParams params)
public void tunnelTarget(boolean secure, HttpParams params)
throws IOException {
if (params == null) {
@ -194,7 +194,47 @@ public abstract class AbstractPoolEntry {
secure, params);
this.tracker.tunnelTarget(secure);
} // tunnelCreated
} // tunnelTarget
/**
* Tracks tunnelling of the connection to a chained proxy.
* The tunnel has to be established outside by sending a CONNECT
* request to the previous proxy.
*
* @param next the proxy to which the tunnel was established.
* See {@link org.apache.http.conn.ManagedClientConnection#tunnelProxy
* ManagedClientConnection.tunnelProxy}
* for details.
* @param secure <code>true</code> if the tunnel should be
* considered secure, <code>false</code> otherwise
* @param params the parameters for tunnelling the connection
*
* @throws IOException in case of a problem
*/
public void tunnelProxy(HttpHost next, boolean secure, HttpParams params)
throws IOException {
if (next == null) {
throw new IllegalArgumentException
("Next proxy must not be null.");
}
if (params == null) {
throw new IllegalArgumentException
("Parameters must not be null.");
}
//@@@ check for proxy in planned route?
if ((this.tracker == null) || !this.tracker.isConnected()) {
throw new IllegalStateException("Connection not open.");
}
// LOG.debug?
this.connection.update(null, next, secure, params);
this.tracker.tunnelProxy(next, secure);
} // tunnelProxy
/**

View File

@ -33,6 +33,7 @@ package org.apache.http.impl.conn;
import java.io.IOException;
import org.apache.http.HttpHost;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.conn.HttpRoute;
@ -122,11 +123,20 @@ public abstract class AbstractPooledConnAdapter
// non-javadoc, see interface ManagedHttpConnection
public void tunnelCreated(boolean secure, HttpParams params)
public void tunnelTarget(boolean secure, HttpParams params)
throws IOException {
assertAttached();
poolEntry.tunnelCreated(secure, params);
poolEntry.tunnelTarget(secure, params);
}
// non-javadoc, see interface ManagedHttpConnection
public void tunnelProxy(HttpHost next, boolean secure, HttpParams params)
throws IOException {
assertAttached();
poolEntry.tunnelProxy(next, secure, params);
}

View File

@ -30,6 +30,7 @@
package org.apache.http.impl.conn;
import org.apache.http.HttpHost;
import org.apache.http.conn.HttpRoute;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
@ -64,7 +65,11 @@ public class ClientConnAdapterMockup extends AbstractClientConnAdapter {
throw new UnsupportedOperationException("just a mockup");
}
public void tunnelCreated(boolean secure, HttpParams params) {
public void tunnelTarget(boolean secure, HttpParams params) {
throw new UnsupportedOperationException("just a mockup");
}
public void tunnelProxy(HttpHost next, boolean secure, HttpParams params) {
throw new UnsupportedOperationException("just a mockup");
}