request director uses route director; still missing tunnelling
git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@505491 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
a72fc9c4c5
commit
7d2d2f47a8
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* $HeadURL$
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
* ====================================================================
|
||||
*
|
||||
* This software consists of voluntary contributions made by many
|
||||
* individuals on behalf of the Apache Software Foundation. For more
|
||||
* information on the Apache Software Foundation, please see
|
||||
* <http://www.apache.org/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.http.conn;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Provides directions on establishing a route.
|
||||
* Instances of this class compare a planned route with a tracked route
|
||||
* and indicate the next step required.
|
||||
*
|
||||
* @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
|
||||
*
|
||||
*
|
||||
* <!-- empty lines to avoid svn diff problems -->
|
||||
* @version $Revision$
|
||||
*
|
||||
* @since 4.0
|
||||
*/
|
||||
public class RouteDirector {
|
||||
|
||||
/** Indicates that the route can not be established at all. */
|
||||
public final static int UNREACHABLE = -1;
|
||||
|
||||
/** Indicates that the route is complete. */
|
||||
public final static int COMPLETE = 0;
|
||||
|
||||
/** Step: open connection to target. */
|
||||
public final static int CONNECT_TARGET = 1;
|
||||
|
||||
/** Step: open connection to proxy. */
|
||||
public final static int CONNECT_PROXY = 2;
|
||||
|
||||
/** Step: tunnel through proxy. */
|
||||
public final static int CREATE_TUNNEL = 3;
|
||||
|
||||
/** Step: layer protocol (over tunnel). */
|
||||
public final static int LAYER_PROTOCOL = 4;
|
||||
|
||||
|
||||
// public default constructor
|
||||
|
||||
|
||||
/**
|
||||
* Provides the next step.
|
||||
*
|
||||
* @param plan the planned route
|
||||
* @param fact the currently established route, or
|
||||
* <code>null</code> if nothing is established
|
||||
*
|
||||
* @return one of the constants defined in this class, indicating
|
||||
* either the next step to perform, or success, or failure.
|
||||
* 0 is for success, a negative value for failure.
|
||||
*/
|
||||
public int nextStep(HttpRoute plan, HttpRoute fact) {
|
||||
if (plan == null) {
|
||||
throw new IllegalArgumentException
|
||||
("Planned route may not be null.");
|
||||
}
|
||||
|
||||
int step = UNREACHABLE;
|
||||
|
||||
if (fact == null)
|
||||
step = firstStep(plan);
|
||||
else if (plan.getProxyHost() == null)
|
||||
step = directStep(plan, fact);
|
||||
else
|
||||
step = proxiedStep(plan, fact);
|
||||
|
||||
return step;
|
||||
|
||||
} // nextStep
|
||||
|
||||
|
||||
/**
|
||||
* Determines the first step to establish a route.
|
||||
*
|
||||
* @param plan the planned route
|
||||
*
|
||||
* @return the first step
|
||||
*/
|
||||
protected int firstStep(HttpRoute plan) {
|
||||
|
||||
return (plan.getProxyHost() == null) ?
|
||||
CONNECT_TARGET : CONNECT_PROXY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines the next step to establish a direct connection.
|
||||
*
|
||||
* @param plan the planned route
|
||||
* @param fact the currently established route
|
||||
*
|
||||
* @return one of the constants defined in this class, indicating
|
||||
* either the next step to perform, or success, or failure
|
||||
*/
|
||||
protected int directStep(HttpRoute plan, HttpRoute fact) {
|
||||
|
||||
if (fact.getProxyHost() != null)
|
||||
return UNREACHABLE;
|
||||
if (!plan.getTargetHost().equals(fact.getTargetHost()))
|
||||
return UNREACHABLE;
|
||||
// If the security values differ, we could now suggest to layer
|
||||
// a secure protocol on the direct connection. Layering on direct
|
||||
// connections has not been supported in HttpClient 3.x, we don't
|
||||
// consider it here until there is a real-life use case for it.
|
||||
|
||||
// yes, this would cover the two checks above as well...
|
||||
if (!plan.equals(fact))
|
||||
return UNREACHABLE;
|
||||
|
||||
return COMPLETE;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines the next step to establish a connection via proxy.
|
||||
*
|
||||
* @param plan the planned route
|
||||
* @param fact the currently established route
|
||||
*
|
||||
* @return one of the constants defined in this class, indicating
|
||||
* either the next step to perform, or success, or failure
|
||||
*/
|
||||
protected int proxiedStep(HttpRoute plan, HttpRoute fact) {
|
||||
|
||||
if (fact.getProxyHost() == null)
|
||||
return UNREACHABLE;
|
||||
if (!plan.getProxyHost().equals(fact.getProxyHost()) ||
|
||||
!plan.getTargetHost().equals(fact.getTargetHost()))
|
||||
return UNREACHABLE;
|
||||
|
||||
// proxy 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;
|
||||
if (plan.isLayered() && !fact.isLayered())
|
||||
return LAYER_PROTOCOL;
|
||||
|
||||
// tunnel and layering are the same, remains to check the security
|
||||
if (plan.isSecure() != fact.isSecure())
|
||||
return UNREACHABLE;
|
||||
|
||||
return COMPLETE;
|
||||
}
|
||||
|
||||
|
||||
} // class RouteDirector
|
|
@ -142,7 +142,7 @@ public final class RouteTracker implements Cloneable {
|
|||
* @param secure <code>true</code> if the route is secure,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public final void establishTunnel(boolean secure) {
|
||||
public final void createTunnel(boolean secure) {
|
||||
if (this.proxyHost == null) {
|
||||
throw new IllegalStateException("No tunnel without proxy.");
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ import org.apache.http.HttpException;
|
|||
import org.apache.http.params.HttpParams;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.apache.http.protocol.HttpRequestExecutor;
|
||||
import org.apache.http.conn.HttpRoute;
|
||||
import org.apache.http.conn.RouteDirector;
|
||||
import org.apache.http.conn.HostConfiguration;
|
||||
import org.apache.http.conn.ClientConnectionManager;
|
||||
import org.apache.http.conn.ManagedClientConnection;
|
||||
|
@ -166,41 +168,118 @@ public class DefaultClientRequestDirector
|
|||
/**
|
||||
* Establishes the target route.
|
||||
*
|
||||
* @param route the route to establish
|
||||
* @param hostconf the route to establish
|
||||
* @param context the context for the request execution
|
||||
*
|
||||
* @throws HttpException in case of a problem
|
||||
* @throws IOException in case of an IO problem
|
||||
*/
|
||||
protected void establishRoute(HostConfiguration route,
|
||||
protected void establishRoute(HostConfiguration hostconf,
|
||||
HttpContext context)
|
||||
throws HttpException, IOException {
|
||||
|
||||
//@@@ where do we get the currently established route?
|
||||
//@@@ how to handle CONNECT requests for tunnelling?
|
||||
//@@@ refuse to send external CONNECT via director? special handling?
|
||||
|
||||
//@@@ for now, let's just deal with connected and not connected
|
||||
if ((route.getProxyHost() != null) &&
|
||||
!"http".equals(route.getHost().getSchemeName())) {
|
||||
//@@@ the actual check should be whether the socket factory
|
||||
//@@@ for the target host scheme is a SecureSocketFactory
|
||||
throw new UnsupportedOperationException
|
||||
("Currently only plain http via proxy is supported.");
|
||||
}
|
||||
if (managedConn.isOpen())
|
||||
return; // already established
|
||||
//@@@ this check for secure connections is an ugly hack until the
|
||||
//@@@ director is changed to expect HttpRoute instead of HostConfig
|
||||
//@@@ the actual check should be whether the socket factory
|
||||
//@@@ for the target host scheme is a SecureSocketFactory
|
||||
HttpRoute route = null;
|
||||
{
|
||||
final boolean secure =
|
||||
!"http".equals(hostconf.getHost().getSchemeName());
|
||||
if (hostconf.getProxyHost() == null)
|
||||
route = new HttpRoute(hostconf.getHost(),
|
||||
hostconf.getLocalAddress(),
|
||||
secure);
|
||||
else
|
||||
route = new HttpRoute(hostconf.getHost(),
|
||||
hostconf.getLocalAddress(),
|
||||
hostconf.getProxyHost(),
|
||||
secure);
|
||||
System.out.println("@@@ planned: " + route);
|
||||
} //@@@ end of ugly HostConfiguration -> HttpRoute conversion
|
||||
|
||||
//@@@ should the request parameters already be used here?
|
||||
|
||||
//@@@ should the request parameters already be used below?
|
||||
//@@@ probably yes, but they're not linked yet
|
||||
//@@@ will linking above cause problems with linking in reqExec?
|
||||
//@@@ will linking here/above cause problems with linking in reqExec?
|
||||
//@@@ probably not, because the parent is replaced
|
||||
//@@@ just make sure we don't link parameters to themselves
|
||||
|
||||
managedConn.open(route, context, defaultParams);
|
||||
|
||||
RouteDirector rowdy = new RouteDirector();
|
||||
int step;
|
||||
do {
|
||||
HttpRoute fact = managedConn.getRoute();
|
||||
System.out.println("@@@ current: " + fact);
|
||||
step = rowdy.nextStep(route, fact);
|
||||
System.out.println("@@@ action => " + step);
|
||||
|
||||
switch (step) {
|
||||
|
||||
case RouteDirector.CONNECT_TARGET:
|
||||
case RouteDirector.CONNECT_PROXY:
|
||||
managedConn.open(hostconf, context, defaultParams);
|
||||
break;
|
||||
|
||||
case RouteDirector.CREATE_TUNNEL:
|
||||
boolean secure = createTunnel(route, context);
|
||||
managedConn.tunnelCreated(secure, defaultParams);
|
||||
break;
|
||||
|
||||
case RouteDirector.LAYER_PROTOCOL:
|
||||
managedConn.layerProtocol(context, defaultParams);
|
||||
break;
|
||||
|
||||
case RouteDirector.UNREACHABLE:
|
||||
throw new IllegalStateException
|
||||
("Unable to establish route." +
|
||||
"\nplanned = " + route +
|
||||
"\ncurrent = " + fact);
|
||||
|
||||
case RouteDirector.COMPLETE:
|
||||
// do nothing
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException
|
||||
("Unknown step indicator "+step+" from RouteDirector.");
|
||||
} // switch
|
||||
|
||||
} while (step > RouteDirector.COMPLETE);
|
||||
|
||||
} // establishConnection
|
||||
|
||||
|
||||
/**
|
||||
* Creates a tunnel.
|
||||
* The connection must be established to the proxy.
|
||||
* A CONNECT request for tunnelling through the proxy
|
||||
* will be created and sent.
|
||||
*
|
||||
* @param route the route to establish
|
||||
* @param context the context for request execution
|
||||
*
|
||||
* @return <code>true</code> if the tunnelled route is secure,
|
||||
* <code>false</code> otherwise.
|
||||
* The implementation here always returns <code>false</code>,
|
||||
* but derived classes may override.
|
||||
*
|
||||
* @throws HttpException in case of a problem
|
||||
* @throws IOException in case of an IO problem
|
||||
*/
|
||||
protected boolean createTunnel(HttpRoute route, HttpContext context) {
|
||||
if (true) throw new UnsupportedOperationException("@@@ don't know how to establish a tunnel yet");
|
||||
|
||||
// How to decide on security of the tunnelled connection?
|
||||
// The socket factory knows only about the segment to the proxy.
|
||||
// Even if that is secure, the hop to the target may be insecure.
|
||||
// Leave it to derived classes, consider everything insecure here.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a request for execution.
|
||||
*
|
||||
|
|
|
@ -1211,7 +1211,7 @@ public class ThreadSafeClientConnManager
|
|||
|
||||
this.connection.update(null, tracker.getTargetHost(),
|
||||
secure, params);
|
||||
this.tracker.establishTunnel(secure);
|
||||
this.tracker.createTunnel(secure);
|
||||
|
||||
} // tunnelCreated
|
||||
|
||||
|
|
Loading…
Reference in New Issue