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:
Roland Weber 2007-02-09 20:52:27 +00:00
parent a72fc9c4c5
commit 7d2d2f47a8
4 changed files with 278 additions and 18 deletions

View File

@ -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

View File

@ -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.");
}

View File

@ -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())) {
//@@@ 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
throw new UnsupportedOperationException
("Currently only plain http via proxy is supported.");
}
if (managedConn.isOpen())
return; // already established
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.
*

View File

@ -1211,7 +1211,7 @@ public class ThreadSafeClientConnManager
this.connection.update(null, tracker.getTargetHost(),
secure, params);
this.tracker.establishTunnel(secure);
this.tracker.createTunnel(secure);
} // tunnelCreated