in httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java [203:377]
private void doProceedToNextHop(
final State state,
final HttpRequest request,
final AsyncEntityProducer entityProducer,
final AsyncExecChain.Scope scope,
final AsyncExecChain chain,
final AsyncExecCallback asyncExecCallback) {
final RouteTracker tracker = state.tracker;
final String exchangeId = scope.exchangeId;
final HttpRoute route = scope.route;
final AsyncExecRuntime execRuntime = scope.execRuntime;
final CancellableDependency operation = scope.cancellableDependency;
final HttpClientContext clientContext = scope.clientContext;
final HttpRoute fact = tracker.toRoute();
final int step = routeDirector.nextStep(route, fact);
switch (step) {
case HttpRouteDirector.CONNECT_TARGET:
operation.setDependency(execRuntime.connectEndpoint(clientContext, new FutureCallback<AsyncExecRuntime>() {
@Override
public void completed(final AsyncExecRuntime execRuntime) {
tracker.connectTarget(route.isSecure());
if (LOG.isDebugEnabled()) {
LOG.debug("{} connected to target", exchangeId);
}
proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback);
}
@Override
public void failed(final Exception ex) {
asyncExecCallback.failed(ex);
}
@Override
public void cancelled() {
asyncExecCallback.failed(new InterruptedIOException());
}
}));
break;
case HttpRouteDirector.CONNECT_PROXY:
operation.setDependency(execRuntime.connectEndpoint(clientContext, new FutureCallback<AsyncExecRuntime>() {
@Override
public void completed(final AsyncExecRuntime execRuntime) {
final HttpHost proxy = route.getProxyHost();
tracker.connectProxy(proxy, route.isSecure() && !route.isTunnelled());
if (LOG.isDebugEnabled()) {
LOG.debug("{} connected to proxy", exchangeId);
}
proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback);
}
@Override
public void failed(final Exception ex) {
asyncExecCallback.failed(ex);
}
@Override
public void cancelled() {
asyncExecCallback.failed(new InterruptedIOException());
}
}));
break;
case HttpRouteDirector.TUNNEL_TARGET:
final HttpHost proxy = route.getProxyHost();
final HttpHost target = route.getTargetHost();
if (LOG.isDebugEnabled()) {
LOG.debug("{} create tunnel", exchangeId);
}
createTunnel(state, proxy, target, scope, new AsyncExecCallback() {
@Override
public AsyncDataConsumer handleResponse(final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
return asyncExecCallback.handleResponse(response, entityDetails);
}
@Override
public void handleInformationResponse(final HttpResponse response) throws HttpException, IOException {
asyncExecCallback.handleInformationResponse(response);
}
@Override
public void completed() {
if (!execRuntime.isEndpointConnected()) {
// Remote endpoint disconnected. Need to start over
if (LOG.isDebugEnabled()) {
LOG.debug("{} proxy disconnected", exchangeId);
}
state.tracker.reset();
}
if (state.challenged) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} proxy authentication required", exchangeId);
}
proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback);
} else {
if (state.tunnelRefused) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} tunnel refused", exchangeId);
}
asyncExecCallback.completed();
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("{} tunnel to target created", exchangeId);
}
tracker.tunnelTarget(false);
proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback);
}
}
}
@Override
public void failed(final Exception cause) {
execRuntime.markConnectionNonReusable();
asyncExecCallback.failed(cause);
}
});
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)
asyncExecCallback.failed(new HttpException("Proxy chains are not supported"));
break;
case HttpRouteDirector.LAYER_PROTOCOL:
execRuntime.upgradeTls(clientContext, new FutureCallback<AsyncExecRuntime>() {
@Override
public void completed(final AsyncExecRuntime asyncExecRuntime) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} upgraded to TLS", exchangeId);
}
tracker.layerProtocol(route.isSecure());
proceedToNextHop(state, request, entityProducer, scope, chain, asyncExecCallback);
}
@Override
public void failed(final Exception ex) {
asyncExecCallback.failed(ex);
}
@Override
public void cancelled() {
asyncExecCallback.failed(new InterruptedIOException());
}
});
break;
case HttpRouteDirector.UNREACHABLE:
asyncExecCallback.failed(new HttpException("Unable to establish route: " +
"planned = " + route + "; current = " + fact));
break;
case HttpRouteDirector.COMPLETE:
if (LOG.isDebugEnabled()) {
LOG.debug("{} route fully established", exchangeId);
}
proceedConnected(request, entityProducer, scope, chain, asyncExecCallback);
break;
default:
throw new IllegalStateException("Unknown step indicator " + step + " from RouteDirector.");
}
}