proxy chains in HttpRoute/RouteTracker/RouteDirector
git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@542193 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
942bff97c5
commit
d0a973d3bf
|
@ -50,7 +50,7 @@ import org.apache.http.util.CharArrayBuffer;
|
|||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public final class HttpRoute {
|
||||
public final class HttpRoute implements Cloneable {
|
||||
|
||||
/** The target host to connect to. */
|
||||
private final HttpHost targetHost;
|
||||
|
@ -61,8 +61,8 @@ public final class HttpRoute {
|
|||
*/
|
||||
private final InetAddress localAddress;
|
||||
|
||||
/** The proxy server, if any. */
|
||||
private final HttpHost proxyHost;
|
||||
/** The proxy servers, if any. */
|
||||
private final HttpHost[] proxyChain;
|
||||
|
||||
/** Whether the the route is tunnelled through the proxy. */
|
||||
private final boolean tunnelled;
|
||||
|
@ -74,12 +74,77 @@ public final class HttpRoute {
|
|||
private final boolean secure;
|
||||
|
||||
|
||||
/**
|
||||
* Internal, fully-specified constructor.
|
||||
* This constructor does <i>not</i> clone the proxy chain array,
|
||||
* nor test it for <code>null</code> elements. This conversion and
|
||||
* check is the responsibility of the public constructors.
|
||||
* The order of arguments here is different from the similar public
|
||||
* constructor, as required by Java.
|
||||
*
|
||||
* @param local the local address to route from, or
|
||||
* <code>null</code> for the default
|
||||
* @param target the host to which to route
|
||||
* @param proxies the proxy chain to use, or
|
||||
* <code>null</code> for a direct route
|
||||
* @param secure <code>true</code> if the route is (to be) secure,
|
||||
* <code>false</code> otherwise
|
||||
* @param tunnelled <code>true</code> if the route is (to be) tunnelled
|
||||
* end-to-end via the proxy chain,
|
||||
* <code>false</code> otherwise
|
||||
* @param layered <code>true</code> if the route includes a
|
||||
* layered protocol,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
private HttpRoute(InetAddress local, HttpHost target, HttpHost[] proxies,
|
||||
boolean secure, boolean tunnelled, boolean layered) {
|
||||
if (target == null) {
|
||||
throw new IllegalArgumentException
|
||||
("Target host may not be null.");
|
||||
}
|
||||
if (tunnelled && (proxies == null)) {
|
||||
throw new IllegalArgumentException
|
||||
("Proxy required if tunnelled.");
|
||||
}
|
||||
|
||||
this.targetHost = target;
|
||||
this.localAddress = local;
|
||||
this.proxyChain = proxies;
|
||||
this.secure = secure;
|
||||
this.tunnelled = tunnelled;
|
||||
this.layered = layered;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new route with all attributes specified explicitly.
|
||||
*
|
||||
* @param target the host to which to route
|
||||
* @param local the local address to route from, or
|
||||
* <code>null</code> for the default
|
||||
* @param proxies the proxy chain to use, or
|
||||
* <code>null</code> for a direct route
|
||||
* @param secure <code>true</code> if the route is (to be) secure,
|
||||
* <code>false</code> otherwise
|
||||
* @param tunnelled <code>true</code> if the route is (to be) tunnelled
|
||||
* end-to-end via the proxy chain,
|
||||
* <code>false</code> otherwise
|
||||
* @param layered <code>true</code> if the route includes a
|
||||
* layered protocol,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public HttpRoute(HttpHost target, InetAddress local, HttpHost[] proxies,
|
||||
boolean secure, boolean tunnelled, boolean layered) {
|
||||
this(local, target, toChain(proxies), secure, tunnelled, layered);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new route with at most one proxy.
|
||||
*
|
||||
* @param target the host to which to route
|
||||
* @param local the local address to route from, or
|
||||
* <code>null</code> for the default
|
||||
* @param proxy the proxy to use, or
|
||||
* <code>null</code> for a direct route
|
||||
* @param secure <code>true</code> if the route is (to be) secure,
|
||||
|
@ -93,21 +158,7 @@ public final class HttpRoute {
|
|||
*/
|
||||
public HttpRoute(HttpHost target, InetAddress local, HttpHost proxy,
|
||||
boolean secure, boolean tunnelled, boolean layered) {
|
||||
if (target == null) {
|
||||
throw new IllegalArgumentException
|
||||
("Target host may not be null.");
|
||||
}
|
||||
if (tunnelled && (proxy == null)) {
|
||||
throw new IllegalArgumentException
|
||||
("Proxy host may not be null if tunnelled.");
|
||||
}
|
||||
|
||||
this.targetHost = target;
|
||||
this.localAddress = local;
|
||||
this.proxyHost = proxy;
|
||||
this.secure = secure;
|
||||
this.tunnelled = tunnelled;
|
||||
this.layered = layered;
|
||||
this(local, target, toChain(proxy), secure, tunnelled, layered);
|
||||
}
|
||||
|
||||
|
||||
|
@ -122,18 +173,17 @@ public final class HttpRoute {
|
|||
* <code>false</code> otherwise
|
||||
*/
|
||||
public HttpRoute(HttpHost target, InetAddress local, boolean secure) {
|
||||
this(target, local, null, secure, false, false);
|
||||
this(local, target, null, secure, false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new direct insecure route.
|
||||
* That is a route without a proxy.
|
||||
*
|
||||
* @param target the host to which to route
|
||||
*/
|
||||
public HttpRoute(HttpHost target) {
|
||||
this(target, null, null, false, false, false);
|
||||
this(null, target, null, false, false, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -152,7 +202,7 @@ public final class HttpRoute {
|
|||
*/
|
||||
public HttpRoute(HttpHost target, InetAddress local, HttpHost proxy,
|
||||
boolean secure) {
|
||||
this(target, local, proxy, secure, secure, secure);
|
||||
this(local, target, toChain(proxy), secure, secure, secure);
|
||||
if (proxy == null) {
|
||||
throw new IllegalArgumentException
|
||||
("Proxy host may not be null.");
|
||||
|
@ -160,6 +210,47 @@ public final class HttpRoute {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to convert a proxy to a proxy chain.
|
||||
*
|
||||
* @param proxy the only proxy in the chain, or <code>null</code>
|
||||
*
|
||||
* @return a proxy chain array, or <code>null</code>
|
||||
*/
|
||||
private static HttpHost[] toChain(HttpHost proxy) {
|
||||
if (proxy == null)
|
||||
return null;
|
||||
|
||||
return new HttpHost[]{ proxy };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to duplicate and check a proxy chain.
|
||||
* An empty proxy chain is converted to <code>null</code>.
|
||||
*
|
||||
* @param proxies the proxy chain to duplicate, or <code>null</code>
|
||||
*
|
||||
* @return a new proxy chain array, or <code>null</code>
|
||||
*/
|
||||
private static HttpHost[] toChain(HttpHost[] proxies) {
|
||||
if ((proxies == null) || (proxies.length < 1))
|
||||
return null;
|
||||
|
||||
for (int i=0; i<proxies.length; i++) {
|
||||
if (proxies[i] == null)
|
||||
throw new IllegalArgumentException
|
||||
("Proxy chain may not contain null elements.");
|
||||
}
|
||||
|
||||
// copy the proxy chain, the traditional way
|
||||
HttpHost[] result = new HttpHost[proxies.length];
|
||||
System.arraycopy(proxies, 0, result, 0, proxies.length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the target host.
|
||||
*
|
||||
|
@ -182,20 +273,71 @@ public final class HttpRoute {
|
|||
|
||||
|
||||
/**
|
||||
* Obtains the proxy host.
|
||||
* Obtains the number of hops in this route.
|
||||
* A direct route has one hop. A route through a proxy has two hops.
|
||||
* A route through a chain of <i>n</i> proxies has <i>n+1</i> hops.
|
||||
*
|
||||
* @return the number of hops in this route
|
||||
*/
|
||||
public final int getHopCount() {
|
||||
return (proxyChain == null) ? 1 : (proxyChain.length+1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the target of a hop in this route.
|
||||
* The target of the last hop is the {@link #getTargetHost target host},
|
||||
* the target of previous hops is the respective proxy in the chain.
|
||||
* For a route through exactly one proxy, target of hop 0 is the proxy
|
||||
* and target of hop 1 is the target host.
|
||||
*
|
||||
* @param hop index of the hop for which to get the target,
|
||||
* 0 for first
|
||||
*
|
||||
* @return the target of the given hop
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* if the argument is negative or not less than
|
||||
* {@link #getHopCount getHopCount()}
|
||||
*/
|
||||
public final HttpHost getHopTarget(int hop) {
|
||||
if (hop < 0)
|
||||
throw new IllegalArgumentException
|
||||
("Hop index must not be negative: " + hop);
|
||||
if (((this.proxyChain == null) && (hop > 0)) ||
|
||||
( this.proxyChain.length < hop)) {
|
||||
throw new IllegalArgumentException
|
||||
("Hop index " + hop +
|
||||
" exceeds route length " + getHopCount() +".");
|
||||
}
|
||||
|
||||
HttpHost result = null;
|
||||
if ((this.proxyChain != null) && (hop < this.proxyChain.length))
|
||||
result = this.proxyChain[hop];
|
||||
else
|
||||
result = this.targetHost;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the first proxy host.
|
||||
*
|
||||
* @return the proxy host, or
|
||||
* @return the first proxy in the proxy chain, or
|
||||
* <code>null</code> if this route is direct
|
||||
*/
|
||||
public final HttpHost getProxyHost() {
|
||||
return this.proxyHost;
|
||||
return (this.proxyChain == null) ? null : this.proxyChain[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether this route is tunnelled through a proxy.
|
||||
* If there is a proxy chain, only end-to-end tunnels are considered.
|
||||
*
|
||||
* @return <code>true</code> if tunnelled,
|
||||
* @return <code>true</code> if tunnelled end-to-end through at least
|
||||
* one proxy,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public final boolean isTunnelled() {
|
||||
|
@ -205,6 +347,8 @@ public final class HttpRoute {
|
|||
|
||||
/**
|
||||
* Checks whether this route includes a layered protocol.
|
||||
* In the presence of proxies, only layering over an end-to-end tunnel
|
||||
* is considered.
|
||||
*
|
||||
* @return <code>true</code> if layered,
|
||||
* <code>false</code> otherwise
|
||||
|
@ -235,8 +379,11 @@ public final class HttpRoute {
|
|||
* where routes need to be represented. No conversion necessary.
|
||||
*/
|
||||
public final HostConfiguration toHostConfig() {
|
||||
if ((this.proxyChain != null) && (this.proxyChain.length > 1)) {
|
||||
throw new IllegalStateException("Cannot convert proxy chain.");
|
||||
}
|
||||
return new HostConfiguration
|
||||
(this.targetHost, this.proxyHost, this.localAddress);
|
||||
(this.targetHost, getProxyHost(), this.localAddress);
|
||||
}
|
||||
|
||||
|
||||
|
@ -257,18 +404,25 @@ public final class HttpRoute {
|
|||
HttpRoute that = (HttpRoute) o;
|
||||
boolean equal = this.targetHost.equals(that.targetHost);
|
||||
equal &=
|
||||
( this.localAddress == that.localAddress) |
|
||||
( this.localAddress == that.localAddress) ||
|
||||
((this.localAddress != null) &&
|
||||
this.localAddress.equals(that.localAddress));
|
||||
equal &=
|
||||
( this.proxyHost == that.proxyHost) |
|
||||
((this.proxyHost != null) &&
|
||||
this.proxyHost.equals(that.proxyHost));
|
||||
( this.proxyChain == that.proxyChain) ||
|
||||
((this.proxyChain != null) &&
|
||||
(this.proxyChain.length == that.proxyChain.length));
|
||||
// comparison of actual proxies follows below
|
||||
equal &=
|
||||
(this.secure == that.secure) &&
|
||||
(this.tunnelled == that.tunnelled) &&
|
||||
(this.layered == that.layered);
|
||||
|
||||
// chain length has been compared above, now check the proxies
|
||||
if (equal && (this.proxyChain != null)) {
|
||||
for (int i=0; equal && (i<this.proxyChain.length); i++)
|
||||
equal = this.proxyChain[i].equals(that.proxyChain[i]);
|
||||
}
|
||||
|
||||
return equal;
|
||||
}
|
||||
|
||||
|
@ -284,8 +438,11 @@ public final class HttpRoute {
|
|||
|
||||
if (this.localAddress != null)
|
||||
hc ^= localAddress.hashCode();
|
||||
if (this.proxyHost != null)
|
||||
hc ^= proxyHost.hashCode();
|
||||
if (this.proxyChain != null) {
|
||||
hc ^= proxyChain.length;
|
||||
for (int i=0; i<proxyChain.length; i++)
|
||||
hc ^= proxyChain[i].hashCode();
|
||||
}
|
||||
|
||||
if (this.secure)
|
||||
hc ^= 0x11111111;
|
||||
|
@ -304,7 +461,7 @@ public final class HttpRoute {
|
|||
* @return a human-readable representation of this route
|
||||
*/
|
||||
public final String toString() {
|
||||
CharArrayBuffer cab = new CharArrayBuffer(80);
|
||||
CharArrayBuffer cab = new CharArrayBuffer(50 + getHopCount()*30);
|
||||
|
||||
cab.append("HttpRoute[");
|
||||
if (this.localAddress != null) {
|
||||
|
@ -319,9 +476,11 @@ public final class HttpRoute {
|
|||
if (this.secure)
|
||||
cab.append('s');
|
||||
cab.append("}->");
|
||||
if (this.proxyHost != null) {
|
||||
cab.append(this.proxyHost);
|
||||
cab.append("->");
|
||||
if (this.proxyChain != null) {
|
||||
for (int i=0; i<this.proxyChain.length; i++) {
|
||||
cab.append(this.proxyChain[i]);
|
||||
cab.append("->");
|
||||
}
|
||||
}
|
||||
cab.append(this.targetHost);
|
||||
cab.append(']');
|
||||
|
@ -329,4 +488,11 @@ public final class HttpRoute {
|
|||
return cab.toString();
|
||||
}
|
||||
|
||||
|
||||
// default implementation of clone() is sufficient
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
|
||||
} // class HttpRoute
|
||||
|
|
|
@ -60,11 +60,14 @@ public class RouteDirector {
|
|||
/** Step: open connection to proxy. */
|
||||
public final static int CONNECT_PROXY = 2;
|
||||
|
||||
/** Step: tunnel through proxy. */
|
||||
public final static int CREATE_TUNNEL = 3;
|
||||
/** Step: tunnel through proxy to target. */
|
||||
public final static int TUNNEL_TARGET = 3;
|
||||
|
||||
/** Step: tunnel through proxy to other proxy. */
|
||||
public final static int TUNNEL_PROXY = 4;
|
||||
|
||||
/** Step: layer protocol (over tunnel). */
|
||||
public final static int LAYER_PROTOCOL = 4;
|
||||
public final static int LAYER_PROTOCOL = 5;
|
||||
|
||||
|
||||
// public default constructor
|
||||
|
@ -91,10 +94,10 @@ public class RouteDirector {
|
|||
|
||||
if (fact == null)
|
||||
step = firstStep(plan);
|
||||
else if (plan.getProxyHost() == null)
|
||||
step = directStep(plan, fact);
|
||||
else
|
||||
else if (plan.getHopCount() > 1)
|
||||
step = proxiedStep(plan, fact);
|
||||
else
|
||||
step = directStep(plan, fact);
|
||||
|
||||
return step;
|
||||
|
||||
|
@ -110,8 +113,8 @@ public class RouteDirector {
|
|||
*/
|
||||
protected int firstStep(HttpRoute plan) {
|
||||
|
||||
return (plan.getProxyHost() == null) ?
|
||||
CONNECT_TARGET : CONNECT_PROXY;
|
||||
return (plan.getHopCount() > 1) ?
|
||||
CONNECT_PROXY : CONNECT_TARGET;
|
||||
}
|
||||
|
||||
|
||||
|
@ -126,7 +129,7 @@ public class RouteDirector {
|
|||
*/
|
||||
protected int directStep(HttpRoute plan, HttpRoute fact) {
|
||||
|
||||
if (fact.getProxyHost() != null)
|
||||
if (fact.getHopCount() > 1)
|
||||
return UNREACHABLE;
|
||||
if (!plan.getTargetHost().equals(fact.getTargetHost()))
|
||||
return UNREACHABLE;
|
||||
|
@ -156,19 +159,30 @@ public class RouteDirector {
|
|||
*/
|
||||
protected int proxiedStep(HttpRoute plan, HttpRoute fact) {
|
||||
|
||||
if (fact.getProxyHost() == null)
|
||||
if (fact.getHopCount() <= 1)
|
||||
return UNREACHABLE;
|
||||
if (!plan.getProxyHost().equals(fact.getProxyHost()) ||
|
||||
!plan.getTargetHost().equals(fact.getTargetHost()))
|
||||
if (!plan.getTargetHost().equals(fact.getTargetHost()))
|
||||
return UNREACHABLE;
|
||||
final int phc = plan.getHopCount();
|
||||
final int fhc = fact.getHopCount();
|
||||
if (phc < fhc)
|
||||
return UNREACHABLE;
|
||||
|
||||
// proxy and target are the same, check tunnelling and layering
|
||||
for (int i=0; i<phc-1; i++) {
|
||||
if (!plan.getHopTarget(i).equals(fact.getHopTarget(i)))
|
||||
return UNREACHABLE;
|
||||
}
|
||||
// now we know that the target matches and proxies so far are the same
|
||||
if (phc > fhc)
|
||||
return TUNNEL_PROXY; // need to extend the proxy chain
|
||||
|
||||
// proxy chain and target are the same, check tunnelling and layering
|
||||
if ((fact.isTunnelled() && !plan.isTunnelled()) ||
|
||||
(fact.isLayered() && !plan.isLayered()))
|
||||
return UNREACHABLE;
|
||||
|
||||
if (plan.isTunnelled() && !fact.isTunnelled())
|
||||
return CREATE_TUNNEL;
|
||||
return TUNNEL_TARGET;
|
||||
if (plan.isLayered() && !fact.isLayered())
|
||||
return LAYER_PROTOCOL;
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ import org.apache.http.util.CharArrayBuffer;
|
|||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public final class RouteTracker {
|
||||
public final class RouteTracker implements Cloneable {
|
||||
|
||||
/** The target host to connect to. */
|
||||
private final HttpHost targetHost;
|
||||
|
@ -65,10 +65,10 @@ public final class RouteTracker {
|
|||
/** Whether the first hop of the route is established. */
|
||||
private boolean connected;
|
||||
|
||||
/** The proxy server, if any. */
|
||||
private HttpHost proxyHost;
|
||||
/** The proxy chain, if any. */
|
||||
private HttpHost[] proxyChain;
|
||||
|
||||
/** Whether the the route is tunnelled through the proxy. */
|
||||
/** Whether the the route is tunnelled end-to-end through proxies. */
|
||||
private boolean tunnelled;
|
||||
|
||||
/** Whether the route is layered over a tunnel. */
|
||||
|
@ -114,13 +114,16 @@ public final class RouteTracker {
|
|||
* <code>false</code> otherwise
|
||||
*/
|
||||
public final void connectTarget(boolean secure) {
|
||||
if (this.connected) {
|
||||
throw new IllegalStateException("Already connected.");
|
||||
}
|
||||
this.connected = true;
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tracks connecting to a proxy.
|
||||
* Tracks connecting to the first proxy.
|
||||
*
|
||||
* @param proxy the proxy connected to
|
||||
* @param secure <code>true</code> if the route is secure,
|
||||
|
@ -130,20 +133,23 @@ public final class RouteTracker {
|
|||
if (proxy == null) {
|
||||
throw new IllegalArgumentException("Proxy host may not be null.");
|
||||
}
|
||||
this.connected = true;
|
||||
this.proxyHost = proxy;
|
||||
this.secure = secure;
|
||||
if (this.connected) {
|
||||
throw new IllegalStateException("Already connected.");
|
||||
}
|
||||
this.connected = true;
|
||||
this.proxyChain = new HttpHost[]{ proxy };
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tracks tunnelling through the proxy.
|
||||
* Tracks tunnelling to the target.
|
||||
*
|
||||
* @param secure <code>true</code> if the route is secure,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public final void createTunnel(boolean secure) {
|
||||
if (this.proxyHost == null) {
|
||||
public final void tunnelTarget(boolean secure) {
|
||||
if (this.proxyChain == null) {
|
||||
throw new IllegalStateException("No tunnel without proxy.");
|
||||
}
|
||||
if (!this.connected) {
|
||||
|
@ -154,6 +160,37 @@ public final class RouteTracker {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tracks tunnelling to a proxy in a proxy chain.
|
||||
* This will extend the tracked proxy chain, but it does not mark
|
||||
* the route as tunnelled. Only end-to-end tunnels are considered there.
|
||||
*
|
||||
* @param proxy the proxy tunnelled to
|
||||
* @param secure <code>true</code> if the route is secure,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public final void tunnelProxy(HttpHost proxy, boolean secure) {
|
||||
if (proxy == null) {
|
||||
throw new IllegalArgumentException("Proxy host may not be null.");
|
||||
}
|
||||
if (this.proxyChain == null) {
|
||||
throw new IllegalStateException("No proxy tunnel without proxy.");
|
||||
}
|
||||
if (!this.connected) {
|
||||
throw new IllegalStateException("No tunnel unless connected.");
|
||||
}
|
||||
|
||||
// prepare an extended proxy chain
|
||||
HttpHost[] proxies = new HttpHost[this.proxyChain.length+1];
|
||||
System.arraycopy(this.proxyChain, 0,
|
||||
proxies, 0, this.proxyChain.length);
|
||||
proxies[proxies.length+1] = proxy;
|
||||
|
||||
this.proxyChain = proxies;
|
||||
this.secure = secure;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tracks layering a protocol.
|
||||
*
|
||||
|
@ -194,12 +231,67 @@ public final class RouteTracker {
|
|||
|
||||
|
||||
/**
|
||||
* Obtains the proxy host.
|
||||
* Obtains the number of tracked hops.
|
||||
* An unconnected route has no hops.
|
||||
* Connecting directly to the target adds one hop.
|
||||
* Connecting to a proxy adds two hops, one for the proxy and
|
||||
* one for the target.
|
||||
* Tunnelling to a proxy in a proxy chain adds one hop.
|
||||
*
|
||||
* @return the number of hops in the tracked route
|
||||
*/
|
||||
public final int getHopCount() {
|
||||
int hops = 0;
|
||||
if (this.connected) {
|
||||
if (proxyChain == null)
|
||||
hops = 1;
|
||||
else
|
||||
hops = proxyChain.length + 1;
|
||||
}
|
||||
return hops;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the target of a hop in this route.
|
||||
*
|
||||
* @param hop index of the hop for which to get the target,
|
||||
* 0 for first
|
||||
*
|
||||
* @return the target of the given hop
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* if the argument is negative or not less than
|
||||
* {@link #getHopCount getHopCount()}
|
||||
*/
|
||||
public final HttpHost getHopTarget(int hop) {
|
||||
if (hop < 0)
|
||||
throw new IllegalArgumentException
|
||||
("Hop index must not be negative: " + hop);
|
||||
final int hopcount = getHopCount();
|
||||
if (hop >= hopcount) {
|
||||
throw new IllegalArgumentException
|
||||
("Hop index " + hop +
|
||||
" exceeds tracked route length " + hopcount +".");
|
||||
}
|
||||
|
||||
HttpHost result = null;
|
||||
if ((this.proxyChain != null) && (hop < this.proxyChain.length))
|
||||
result = this.proxyChain[hop];
|
||||
else
|
||||
result = this.targetHost;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtains the first proxy host.
|
||||
*
|
||||
* @return the proxy host, or <code>null</code> if not tracked
|
||||
* @return the first proxy host, or <code>null</code> if not tracked
|
||||
*/
|
||||
public final HttpHost getProxyHost() {
|
||||
return this.proxyHost;
|
||||
return (this.proxyChain == null) ? null : this.proxyChain[0];
|
||||
}
|
||||
|
||||
|
||||
|
@ -258,7 +350,7 @@ public final class RouteTracker {
|
|||
public final HttpRoute toRoute() {
|
||||
return !this.connected ?
|
||||
null : new HttpRoute(this.targetHost, this.localAddress,
|
||||
this.proxyHost, this.secure,
|
||||
this.proxyChain, this.secure,
|
||||
this.tunnelled, this.layered);
|
||||
}
|
||||
|
||||
|
@ -277,8 +369,11 @@ public final class RouteTracker {
|
|||
* @return a representation of the route tracked so far
|
||||
*/
|
||||
public final HostConfiguration toHostConfig() {
|
||||
if ((this.proxyChain != null) && (this.proxyChain.length > 1)) {
|
||||
throw new IllegalStateException("Cannot convert proxy chain.");
|
||||
}
|
||||
return new HostConfiguration
|
||||
(this.targetHost, this.proxyHost, this.localAddress);
|
||||
(this.targetHost, getProxyHost(), this.localAddress);
|
||||
}
|
||||
|
||||
|
||||
|
@ -299,19 +394,26 @@ public final class RouteTracker {
|
|||
RouteTracker that = (RouteTracker) o;
|
||||
boolean equal = this.targetHost.equals(that.targetHost);
|
||||
equal &=
|
||||
( this.localAddress == that.localAddress) |
|
||||
( this.localAddress == that.localAddress) ||
|
||||
((this.localAddress != null) &&
|
||||
this.localAddress.equals(that.localAddress));
|
||||
equal &=
|
||||
( this.proxyHost == that.proxyHost) |
|
||||
((this.proxyHost != null) &&
|
||||
this.proxyHost.equals(that.proxyHost));
|
||||
( this.proxyChain == that.proxyChain) ||
|
||||
((this.proxyChain != null) &&
|
||||
(this.proxyChain.length == that.proxyChain.length));
|
||||
// comparison of actual proxies follows below
|
||||
equal &=
|
||||
(this.connected == that.connected) &&
|
||||
(this.secure == that.secure) &&
|
||||
(this.tunnelled == that.tunnelled) &&
|
||||
(this.layered == that.layered);
|
||||
|
||||
// chain length has been compared above, now check the proxies
|
||||
if (equal && (this.proxyChain != null)) {
|
||||
for (int i=0; equal && (i<this.proxyChain.length); i++)
|
||||
equal = this.proxyChain[i].equals(that.proxyChain[i]);
|
||||
}
|
||||
|
||||
return equal;
|
||||
}
|
||||
|
||||
|
@ -330,8 +432,11 @@ public final class RouteTracker {
|
|||
|
||||
if (this.localAddress != null)
|
||||
hc ^= localAddress.hashCode();
|
||||
if (this.proxyHost != null)
|
||||
hc ^= proxyHost.hashCode();
|
||||
if (this.proxyChain != null) {
|
||||
hc ^= proxyChain.length;
|
||||
for (int i=0; i<proxyChain.length; i++)
|
||||
hc ^= proxyChain[i].hashCode();
|
||||
}
|
||||
|
||||
if (this.connected)
|
||||
hc ^= 0x11111111;
|
||||
|
@ -352,7 +457,7 @@ public final class RouteTracker {
|
|||
* @return a human-readable representation of the tracked route
|
||||
*/
|
||||
public final String toString() {
|
||||
CharArrayBuffer cab = new CharArrayBuffer(80);
|
||||
CharArrayBuffer cab = new CharArrayBuffer(50 + getHopCount()*30);
|
||||
|
||||
cab.append("RouteTracker[");
|
||||
if (this.localAddress != null) {
|
||||
|
@ -369,9 +474,11 @@ public final class RouteTracker {
|
|||
if (this.secure)
|
||||
cab.append('s');
|
||||
cab.append("}->");
|
||||
if (this.proxyHost != null) {
|
||||
cab.append(this.proxyHost);
|
||||
cab.append("->");
|
||||
if (this.proxyChain != null) {
|
||||
for (int i=0; i<this.proxyChain.length; i++) {
|
||||
cab.append(this.proxyChain[i]);
|
||||
cab.append("->");
|
||||
}
|
||||
}
|
||||
cab.append(this.targetHost);
|
||||
cab.append(']');
|
||||
|
@ -379,4 +486,11 @@ public final class RouteTracker {
|
|||
return cab.toString();
|
||||
}
|
||||
|
||||
|
||||
// default implementation of clone() is sufficient
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
return super.clone();
|
||||
}
|
||||
|
||||
|
||||
} // class RouteTracker
|
||||
|
|
|
@ -487,12 +487,16 @@ public class DefaultClientRequestDirector
|
|||
managedConn.open(route, context, requestExec.getParams());
|
||||
break;
|
||||
|
||||
case RouteDirector.CREATE_TUNNEL:
|
||||
case RouteDirector.TUNNEL_TARGET:
|
||||
boolean secure = createTunnel(route, context);
|
||||
LOG.debug("Tunnel created");
|
||||
managedConn.tunnelCreated(secure, requestExec.getParams());
|
||||
break;
|
||||
|
||||
case RouteDirector.TUNNEL_PROXY:
|
||||
throw new UnsupportedOperationException
|
||||
("Proxy chains are not supported.");
|
||||
|
||||
case RouteDirector.LAYER_PROTOCOL:
|
||||
managedConn.layerProtocol(context, requestExec.getParams());
|
||||
break;
|
||||
|
|
|
@ -186,7 +186,7 @@ public abstract class AbstractPoolEntry {
|
|||
|
||||
this.connection.update(null, tracker.getTargetHost(),
|
||||
secure, params);
|
||||
this.tracker.createTunnel(secure);
|
||||
this.tracker.tunnelTarget(secure);
|
||||
|
||||
} // tunnelCreated
|
||||
|
||||
|
|
Loading…
Reference in New Issue