Java tutorial
/* * ==================================================================== * 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.impl.nio.client; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import org.apache.commons.logging.Log; import org.apache.http.ConnectionClosedException; import org.apache.http.ConnectionReuseStrategy; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.ProtocolException; import org.apache.http.ProtocolVersion; import org.apache.http.auth.AuthProtocolState; import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthState; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.NonRepeatableRequestException; import org.apache.http.client.RedirectException; import org.apache.http.client.RedirectStrategy; import org.apache.http.client.UserTokenHandler; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.AbortableHttpRequest; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.params.ClientPNames; import org.apache.http.client.params.HttpClientParams; import org.apache.http.client.protocol.ClientContext; import org.apache.http.client.utils.URIUtils; import org.apache.http.concurrent.FutureCallback; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.ConnectionReleaseTrigger; import org.apache.http.conn.routing.BasicRouteDirector; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.routing.HttpRouteDirector; import org.apache.http.conn.routing.HttpRoutePlanner; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.ClientParamsStack; import org.apache.http.impl.client.EntityEnclosingRequestWrapper; import org.apache.http.impl.client.HttpAuthenticator; import org.apache.http.impl.client.RequestWrapper; import org.apache.http.impl.client.RoutedRequest; import org.apache.http.message.BasicHttpRequest; import org.apache.http.nio.ContentDecoder; import org.apache.http.nio.ContentEncoder; import org.apache.http.nio.IOControl; import org.apache.http.nio.conn.ClientAsyncConnectionManager; import org.apache.http.nio.conn.ManagedClientAsyncConnection; import org.apache.http.nio.conn.scheme.AsyncScheme; import org.apache.http.nio.conn.scheme.AsyncSchemeRegistry; import org.apache.http.nio.protocol.HttpAsyncRequestExecutionHandler; import org.apache.http.nio.protocol.HttpAsyncRequestExecutor; import org.apache.http.nio.protocol.HttpAsyncRequestProducer; import org.apache.http.nio.protocol.HttpAsyncResponseConsumer; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpProcessor; @Deprecated class DefaultAsyncRequestDirector<T> implements HttpAsyncRequestExecutionHandler<T> { private static final AtomicLong COUNTER = new AtomicLong(1); private final Log log; private final HttpAsyncRequestProducer requestProducer; private final HttpAsyncResponseConsumer<T> responseConsumer; private final HttpContext localContext; private final ResultCallback<T> resultCallback; private final ClientAsyncConnectionManager connmgr; private final HttpProcessor httppocessor; private final HttpRoutePlanner routePlanner; private final HttpRouteDirector routeDirector; private final ConnectionReuseStrategy reuseStrategy; private final ConnectionKeepAliveStrategy keepaliveStrategy; private final RedirectStrategy redirectStrategy; private final AuthenticationStrategy targetAuthStrategy; private final AuthenticationStrategy proxyAuthStrategy; private final UserTokenHandler userTokenHandler; private final AuthState targetAuthState; private final AuthState proxyAuthState; private final HttpAuthenticator authenticator; private final HttpParams clientParams; private final long id; private volatile boolean closed; private volatile InternalFutureCallback connRequestCallback; private volatile ManagedClientAsyncConnection managedConn; private RoutedRequest mainRequest; private RoutedRequest followup; private HttpResponse finalResponse; private ClientParamsStack params; private RequestWrapper currentRequest; private HttpResponse currentResponse; private boolean routeEstablished; private int redirectCount; private ByteBuffer tmpbuf; private boolean requestContentProduced; private boolean requestSent; private int execCount; public DefaultAsyncRequestDirector(final Log log, final HttpAsyncRequestProducer requestProducer, final HttpAsyncResponseConsumer<T> responseConsumer, final HttpContext localContext, final ResultCallback<T> callback, final ClientAsyncConnectionManager connmgr, final HttpProcessor httppocessor, final HttpRoutePlanner routePlanner, final ConnectionReuseStrategy reuseStrategy, final ConnectionKeepAliveStrategy keepaliveStrategy, final RedirectStrategy redirectStrategy, final AuthenticationStrategy targetAuthStrategy, final AuthenticationStrategy proxyAuthStrategy, final UserTokenHandler userTokenHandler, final HttpParams clientParams) { super(); this.log = log; this.requestProducer = requestProducer; this.responseConsumer = responseConsumer; this.localContext = localContext; this.resultCallback = callback; this.connmgr = connmgr; this.httppocessor = httppocessor; this.routePlanner = routePlanner; this.reuseStrategy = reuseStrategy; this.keepaliveStrategy = keepaliveStrategy; this.redirectStrategy = redirectStrategy; this.routeDirector = new BasicRouteDirector(); this.targetAuthStrategy = targetAuthStrategy; this.proxyAuthStrategy = proxyAuthStrategy; this.userTokenHandler = userTokenHandler; this.targetAuthState = new AuthState(); this.proxyAuthState = new AuthState(); this.authenticator = new HttpAuthenticator(log); this.clientParams = clientParams; this.id = COUNTER.getAndIncrement(); } @Override public void close() { if (this.closed) { return; } this.closed = true; final ManagedClientAsyncConnection localConn = this.managedConn; if (localConn != null) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] aborting connection " + localConn); } try { localConn.abortConnection(); } catch (final IOException ioex) { this.log.debug("I/O error releasing connection", ioex); } } try { this.requestProducer.close(); } catch (final IOException ex) { this.log.debug("I/O error closing request producer", ex); } try { this.responseConsumer.close(); } catch (final IOException ex) { this.log.debug("I/O error closing response consumer", ex); } } public synchronized void start() { try { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] start execution"); } this.localContext.setAttribute(ClientContext.TARGET_AUTH_STATE, this.targetAuthState); this.localContext.setAttribute(ClientContext.PROXY_AUTH_STATE, this.proxyAuthState); final HttpHost target = this.requestProducer.getTarget(); final HttpRequest request = this.requestProducer.generateRequest(); if (request instanceof AbortableHttpRequest) { ((AbortableHttpRequest) request).setReleaseTrigger(new ConnectionReleaseTrigger() { @Override public void releaseConnection() throws IOException { } @Override public void abortConnection() throws IOException { cancel(); } }); } this.params = new ClientParamsStack(null, this.clientParams, request.getParams(), null); final RequestWrapper wrapper = wrapRequest(request); wrapper.setParams(this.params); final HttpRoute route = determineRoute(target, wrapper, this.localContext); this.mainRequest = new RoutedRequest(wrapper, route); final RequestConfig config = ParamConfig.getRequestConfig(params); this.localContext.setAttribute(ClientContext.REQUEST_CONFIG, config); this.requestContentProduced = false; requestConnection(); } catch (final Exception ex) { failed(ex); } } @Override public HttpHost getTarget() { return this.requestProducer.getTarget(); } @Override public synchronized HttpRequest generateRequest() throws IOException, HttpException { final HttpRoute route = this.mainRequest.getRoute(); if (!this.routeEstablished) { int step; do { final HttpRoute fact = this.managedConn.getRoute(); step = this.routeDirector.nextStep(route, fact); switch (step) { case HttpRouteDirector.CONNECT_TARGET: case HttpRouteDirector.CONNECT_PROXY: break; case HttpRouteDirector.TUNNEL_TARGET: if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Tunnel required"); } final HttpRequest connect = createConnectRequest(route); this.currentRequest = wrapRequest(connect); this.currentRequest.setParams(this.params); break; case HttpRouteDirector.TUNNEL_PROXY: throw new HttpException("Proxy chains are not supported"); case HttpRouteDirector.LAYER_PROTOCOL: managedConn.layerProtocol(this.localContext, this.params); break; case HttpRouteDirector.UNREACHABLE: throw new HttpException( "Unable to establish route: " + "planned = " + route + "; current = " + fact); case HttpRouteDirector.COMPLETE: this.routeEstablished = true; break; default: throw new IllegalStateException("Unknown step indicator " + step + " from RouteDirector."); } } while (step > HttpRouteDirector.COMPLETE && this.currentRequest == null); } HttpHost target = (HttpHost) this.params.getParameter(ClientPNames.VIRTUAL_HOST); if (target == null) { target = route.getTargetHost(); } final HttpHost proxy = route.getProxyHost(); this.localContext.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target); this.localContext.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy); this.localContext.setAttribute(ExecutionContext.HTTP_CONNECTION, this.managedConn); this.localContext.setAttribute(ClientContext.ROUTE, route); if (this.currentRequest == null) { this.currentRequest = this.mainRequest.getRequest(); final String userinfo = this.currentRequest.getURI().getUserInfo(); if (userinfo != null) { this.targetAuthState.update(new BasicScheme(), new UsernamePasswordCredentials(userinfo)); } // Re-write request URI if needed rewriteRequestURI(this.currentRequest, route); } // Reset headers on the request wrapper this.currentRequest.resetHeaders(); this.currentRequest.incrementExecCount(); if (this.currentRequest.getExecCount() > 1 && !this.requestProducer.isRepeatable() && this.requestContentProduced) { throw new NonRepeatableRequestException( "Cannot retry request " + "with a non-repeatable request entity."); } this.execCount++; if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Attempt " + this.execCount + " to execute request"); } return this.currentRequest; } @Override public synchronized void produceContent(final ContentEncoder encoder, final IOControl ioctrl) throws IOException { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] produce content"); } this.requestContentProduced = true; this.requestProducer.produceContent(encoder, ioctrl); if (encoder.isCompleted()) { this.requestProducer.resetRequest(); } } @Override public void requestCompleted(final HttpContext context) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Request completed"); } this.requestSent = true; this.requestProducer.requestCompleted(context); } @Override public boolean isRepeatable() { return this.requestProducer.isRepeatable(); } @Override public void resetRequest() throws IOException { this.requestSent = false; this.requestProducer.resetRequest(); } @Override public synchronized void responseReceived(final HttpResponse response) throws IOException, HttpException { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Response received " + response.getStatusLine()); } this.currentResponse = response; this.currentResponse.setParams(this.params); final int status = this.currentResponse.getStatusLine().getStatusCode(); if (!this.routeEstablished) { final String method = this.currentRequest.getMethod(); if (method.equalsIgnoreCase("CONNECT") && status == HttpStatus.SC_OK) { this.managedConn.tunnelTarget(this.params); } else { this.followup = handleConnectResponse(); if (this.followup == null) { this.finalResponse = response; } } } else { this.followup = handleResponse(); if (this.followup == null) { this.finalResponse = response; } Object userToken = this.localContext.getAttribute(ClientContext.USER_TOKEN); if (managedConn != null) { if (userToken == null) { userToken = userTokenHandler.getUserToken(this.localContext); this.localContext.setAttribute(ClientContext.USER_TOKEN, userToken); } if (userToken != null) { managedConn.setState(userToken); } } } if (this.finalResponse != null) { this.responseConsumer.responseReceived(response); } } @Override public synchronized void consumeContent(final ContentDecoder decoder, final IOControl ioctrl) throws IOException { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Consume content"); } if (this.finalResponse != null) { this.responseConsumer.consumeContent(decoder, ioctrl); } else { if (this.tmpbuf == null) { this.tmpbuf = ByteBuffer.allocate(2048); } this.tmpbuf.clear(); decoder.read(this.tmpbuf); } } private void releaseConnection() { if (this.managedConn != null) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] releasing connection " + this.managedConn); } try { this.managedConn.getContext().removeAttribute(HttpAsyncRequestExecutor.HTTP_HANDLER); this.managedConn.releaseConnection(); } catch (final IOException ioex) { this.log.debug("I/O error releasing connection", ioex); } this.managedConn = null; } } @Override public synchronized void failed(final Exception ex) { try { if (!this.requestSent) { this.requestProducer.failed(ex); } this.responseConsumer.failed(ex); } finally { try { this.resultCallback.failed(ex, this); } finally { close(); } } } @Override public synchronized void responseCompleted(final HttpContext context) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Response fully read"); } try { if (this.resultCallback.isDone()) { return; } if (this.managedConn.isOpen()) { final long duration = this.keepaliveStrategy.getKeepAliveDuration(this.currentResponse, this.localContext); if (this.log.isDebugEnabled()) { final String s; if (duration > 0) { s = "for " + duration + " " + TimeUnit.MILLISECONDS; } else { s = "indefinitely"; } this.log.debug("[exchange: " + this.id + "] Connection can be kept alive " + s); } this.managedConn.setIdleDuration(duration, TimeUnit.MILLISECONDS); } else { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Connection cannot be kept alive"); } this.managedConn.unmarkReusable(); if (this.proxyAuthState.getState() == AuthProtocolState.SUCCESS && this.proxyAuthState.getAuthScheme() != null && this.proxyAuthState.getAuthScheme().isConnectionBased()) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Resetting proxy auth state"); } this.proxyAuthState.reset(); } if (this.targetAuthState.getState() == AuthProtocolState.SUCCESS && this.targetAuthState.getAuthScheme() != null && this.targetAuthState.getAuthScheme().isConnectionBased()) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Resetting target auth state"); } this.targetAuthState.reset(); } } if (this.finalResponse != null) { this.responseConsumer.responseCompleted(this.localContext); if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Response processed"); } releaseConnection(); final T result = this.responseConsumer.getResult(); final Exception ex = this.responseConsumer.getException(); if (ex == null) { this.resultCallback.completed(result, this); } else { this.resultCallback.failed(ex, this); } } else { if (this.followup != null) { final HttpRoute actualRoute = this.mainRequest.getRoute(); final HttpRoute newRoute = this.followup.getRoute(); if (!actualRoute.equals(newRoute)) { releaseConnection(); } this.mainRequest = this.followup; } if (this.managedConn != null && !this.managedConn.isOpen()) { releaseConnection(); } if (this.managedConn != null) { this.managedConn.requestOutput(); } else { requestConnection(); } } this.followup = null; this.currentRequest = null; this.currentResponse = null; } catch (final RuntimeException runex) { failed(runex); throw runex; } } @Override public synchronized boolean cancel() { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Cancelled"); } try { final boolean cancelled = this.responseConsumer.cancel(); final T result = this.responseConsumer.getResult(); final Exception ex = this.responseConsumer.getException(); if (ex != null) { this.resultCallback.failed(ex, this); } else if (result != null) { this.resultCallback.completed(result, this); } else { this.resultCallback.cancelled(this); } return cancelled; } catch (final RuntimeException runex) { this.resultCallback.failed(runex, this); throw runex; } finally { close(); } } @Override public boolean isDone() { return this.resultCallback.isDone(); } @Override public T getResult() { return this.responseConsumer.getResult(); } @Override public Exception getException() { return this.responseConsumer.getException(); } private synchronized void connectionRequestCompleted(final ManagedClientAsyncConnection conn) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Connection allocated: " + conn); } this.connRequestCallback = null; try { this.managedConn = conn; if (this.closed) { conn.releaseConnection(); return; } final HttpRoute route = this.mainRequest.getRoute(); if (!conn.isOpen()) { conn.open(route, this.localContext, this.params); } conn.getContext().setAttribute(HttpAsyncRequestExecutor.HTTP_HANDLER, this); conn.requestOutput(); this.routeEstablished = route.equals(conn.getRoute()); if (!conn.isOpen()) { throw new ConnectionClosedException("Connection closed"); } } catch (final IOException ex) { failed(ex); } catch (final RuntimeException runex) { failed(runex); throw runex; } } private synchronized void connectionRequestFailed(final Exception ex) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] connection request failed"); } this.connRequestCallback = null; try { this.resultCallback.failed(ex, this); } finally { close(); } } private synchronized void connectionRequestCancelled() { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Connection request cancelled"); } this.connRequestCallback = null; try { this.resultCallback.cancelled(this); } finally { close(); } } class InternalFutureCallback implements FutureCallback<ManagedClientAsyncConnection> { @Override public void completed(final ManagedClientAsyncConnection session) { connectionRequestCompleted(session); } @Override public void failed(final Exception ex) { connectionRequestFailed(ex); } @Override public void cancelled() { connectionRequestCancelled(); } } private void requestConnection() { final HttpRoute route = this.mainRequest.getRoute(); if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Request connection for " + route); } final long connectTimeout = HttpConnectionParams.getConnectionTimeout(this.params); final Object userToken = this.localContext.getAttribute(ClientContext.USER_TOKEN); this.connRequestCallback = new InternalFutureCallback(); this.connmgr.leaseConnection(route, userToken, connectTimeout, TimeUnit.MILLISECONDS, this.connRequestCallback); } public synchronized void endOfStream() { if (this.managedConn != null) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Unexpected end of data stream"); } releaseConnection(); if (this.connRequestCallback == null) { requestConnection(); } } } protected HttpRoute determineRoute(final HttpHost target, final HttpRequest request, final HttpContext context) throws HttpException { final HttpHost t = target != null ? target : (HttpHost) request.getParams().getParameter(ClientPNames.DEFAULT_HOST); if (t == null) { throw new IllegalStateException("Target host could not be resolved"); } return this.routePlanner.determineRoute(t, request, context); } private RequestWrapper wrapRequest(final HttpRequest request) throws ProtocolException { if (request instanceof HttpEntityEnclosingRequest) { return new EntityEnclosingRequestWrapper((HttpEntityEnclosingRequest) request); } return new RequestWrapper(request); } protected void rewriteRequestURI(final RequestWrapper request, final HttpRoute route) throws ProtocolException { try { URI uri = request.getURI(); if (route.getProxyHost() != null && !route.isTunnelled()) { // Make sure the request URI is absolute if (!uri.isAbsolute()) { final HttpHost target = route.getTargetHost(); uri = URIUtils.rewriteURI(uri, target); request.setURI(uri); } } else { // Make sure the request URI is relative if (uri.isAbsolute()) { uri = URIUtils.rewriteURI(uri, null); request.setURI(uri); } } } catch (final URISyntaxException ex) { throw new ProtocolException("Invalid URI: " + request.getRequestLine().getUri(), ex); } } private AsyncSchemeRegistry getSchemeRegistry(final HttpContext context) { AsyncSchemeRegistry reg = (AsyncSchemeRegistry) context.getAttribute(ClientContext.SCHEME_REGISTRY); if (reg == null) { reg = this.connmgr.getSchemeRegistry(); } return reg; } private HttpRequest createConnectRequest(final HttpRoute route) { // see RFC 2817, section 5.2 and // INTERNET-DRAFT: Tunneling TCP based protocols through // Web proxy servers final HttpHost target = route.getTargetHost(); final String host = target.getHostName(); int port = target.getPort(); if (port < 0) { final AsyncSchemeRegistry registry = getSchemeRegistry(this.localContext); final AsyncScheme scheme = registry.getScheme(target.getSchemeName()); port = scheme.getDefaultPort(); } final StringBuilder buffer = new StringBuilder(host.length() + 6); buffer.append(host); buffer.append(':'); buffer.append(Integer.toString(port)); final ProtocolVersion ver = HttpProtocolParams.getVersion(this.params); final HttpRequest req = new BasicHttpRequest("CONNECT", buffer.toString(), ver); return req; } private RoutedRequest handleResponse() throws HttpException { RoutedRequest followup = null; if (HttpClientParams.isAuthenticating(this.params)) { final CredentialsProvider credsProvider = (CredentialsProvider) this.localContext .getAttribute(ClientContext.CREDS_PROVIDER); if (credsProvider != null) { followup = handleTargetChallenge(credsProvider); if (followup != null) { return followup; } followup = handleProxyChallenge(credsProvider); if (followup != null) { return followup; } } } if (HttpClientParams.isRedirecting(this.params)) { followup = handleRedirect(); if (followup != null) { return followup; } } return null; } private RoutedRequest handleConnectResponse() throws HttpException { RoutedRequest followup = null; if (HttpClientParams.isAuthenticating(this.params)) { final CredentialsProvider credsProvider = (CredentialsProvider) this.localContext .getAttribute(ClientContext.CREDS_PROVIDER); if (credsProvider != null) { followup = handleProxyChallenge(credsProvider); if (followup != null) { return followup; } } } return null; } private RoutedRequest handleRedirect() throws HttpException { if (this.redirectStrategy.isRedirected(this.currentRequest, this.currentResponse, this.localContext)) { final HttpRoute route = this.mainRequest.getRoute(); final RequestWrapper request = this.mainRequest.getRequest(); final int maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100); if (this.redirectCount >= maxRedirects) { throw new RedirectException("Maximum redirects (" + maxRedirects + ") exceeded"); } this.redirectCount++; final HttpUriRequest redirect = this.redirectStrategy.getRedirect(this.currentRequest, this.currentResponse, this.localContext); final HttpRequest orig = request.getOriginal(); redirect.setHeaders(orig.getAllHeaders()); final URI uri = redirect.getURI(); if (uri.getHost() == null) { throw new ProtocolException("Redirect URI does not specify a valid host name: " + uri); } final HttpHost newTarget = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); // Reset auth states if redirecting to another host if (!route.getTargetHost().equals(newTarget)) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Resetting target auth state"); } this.targetAuthState.reset(); final AuthScheme authScheme = this.proxyAuthState.getAuthScheme(); if (authScheme != null && authScheme.isConnectionBased()) { if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Resetting proxy auth state"); } this.proxyAuthState.reset(); } } final RequestWrapper newRequest = wrapRequest(redirect); newRequest.setParams(this.params); final HttpRoute newRoute = determineRoute(newTarget, newRequest, this.localContext); if (this.log.isDebugEnabled()) { this.log.debug("[exchange: " + this.id + "] Redirecting to '" + uri + "' via " + newRoute); } return new RoutedRequest(newRequest, newRoute); } return null; } private RoutedRequest handleTargetChallenge(final CredentialsProvider credsProvider) throws HttpException { final HttpRoute route = this.mainRequest.getRoute(); HttpHost target = (HttpHost) this.localContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST); if (target == null) { target = route.getTargetHost(); } if (this.authenticator.isAuthenticationRequested(target, this.currentResponse, this.targetAuthStrategy, this.targetAuthState, this.localContext)) { if (this.authenticator.authenticate(target, this.currentResponse, this.targetAuthStrategy, this.targetAuthState, this.localContext)) { // Re-try the same request via the same route return this.mainRequest; } return null; } return null; } private RoutedRequest handleProxyChallenge(final CredentialsProvider credsProvider) throws HttpException { final HttpRoute route = this.mainRequest.getRoute(); final HttpHost proxy = route.getProxyHost(); if (this.authenticator.isAuthenticationRequested(proxy, this.currentResponse, this.proxyAuthStrategy, this.proxyAuthState, this.localContext)) { if (this.authenticator.authenticate(proxy, this.currentResponse, this.proxyAuthStrategy, this.proxyAuthState, this.localContext)) { // Re-try the same request via the same route return this.mainRequest; } return null; } return null; } @Override public HttpContext getContext() { return this.localContext; } @Override public HttpProcessor getHttpProcessor() { return this.httppocessor; } @Override public ConnectionReuseStrategy getConnectionReuseStrategy() { return this.reuseStrategy; } }