Package org.apache.http.impl.client

Source Code of org.apache.http.impl.client.DefaultClientRequestDirector

/*
* $HeadURL: http://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/tags/4.0-alpha1/module-client/src/main/java/org/apache/http/impl/client/DefaultClientRequestDirector.java $
* $Revision: 542253 $
* $Date: 2007-05-28 17:56:35 +0200 (Mon, 28 May 2007) $
*
* ====================================================================
* 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.client;

import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
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.HttpVersion;
import org.apache.http.ProtocolException;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.client.AuthState;
import org.apache.http.client.AuthenticationHandler;
import org.apache.http.client.ClientRequestDirector;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.HttpState;
import org.apache.http.client.RedirectException;
import org.apache.http.client.RedirectHandler;
import org.apache.http.client.RoutedRequest;
import org.apache.http.client.methods.AbortableHttpRequest;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.BasicManagedEntity;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.conn.HttpRoute;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.RouteDirector;
import org.apache.http.conn.Scheme;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpParamsLinker;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpExecutionContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.util.CharArrayBuffer;

/**
* Default implementation of a client-side request director.
* <br/>
* This class replaces the <code>HttpMethodDirector</code> in HttpClient 3.
*
* @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
* @author <a href="mailto:oleg at ural.ru">Oleg Kalnichevski</a>
*
* <!-- empty lines to avoid svn diff problems -->
* @version $Revision: 542253 $
*
* @since 4.0
*/
public class DefaultClientRequestDirector
    implements ClientRequestDirector {

    private static final Log LOG = LogFactory.getLog(DefaultClientRequestDirector.class);
   
    /** The connection manager. */
    protected final ClientConnectionManager connManager;

    /** The connection re-use strategy. */
    protected final ConnectionReuseStrategy reuseStrategy;

    /** The request executor. */
    protected final HttpRequestExecutor requestExec;

    /** The HTTP protocol processor. */
    protected final HttpProcessor httpProcessor;
   
    /** The request retry handler. */
    protected final HttpRequestRetryHandler retryHandler;
   
    /** The redirect handler. */
    protected final RedirectHandler redirectHandler;
   
    /** The authentication handler. */
    private final AuthenticationHandler authHandler;
   
    /** The HTTP parameters. */
    protected final HttpParams params;
   
    /** The currently allocated connection. */
    protected ManagedClientConnection managedConn;

    private int redirectCount;

    private int maxRedirects;
   
    private final AuthState targetAuthState;
   
    private final AuthState proxyAuthState;
   
    public DefaultClientRequestDirector(
            final ClientConnectionManager conman,
            final ConnectionReuseStrategy reustrat,
            final HttpProcessor httpProcessor,
            final HttpRequestRetryHandler retryHandler,
            final RedirectHandler redirectHandler,
            final AuthenticationHandler authHandler,
            final HttpParams params) {

        if (conman == null) {
            throw new IllegalArgumentException("Client connection manager may not be null");
        }
        if (reustrat == null) {
            throw new IllegalArgumentException("Connection reuse strategy may not be null");
        }
        if (httpProcessor == null) {
            throw new IllegalArgumentException("HTTP protocol processor may not be null");
        }
        if (retryHandler == null) {
            throw new IllegalArgumentException("HTTP request retry handler may not be null");
        }
        if (redirectHandler == null) {
            throw new IllegalArgumentException("Redirect handler may not be null");
        }
        if (authHandler == null) {
            throw new IllegalArgumentException("Authentication handler may not be null");
        }
        if (params == null) {
            throw new IllegalArgumentException("HTTP parameters may not be null");
        }
        this.connManager   = conman;
        this.reuseStrategy = reustrat;
        this.httpProcessor = httpProcessor;
        this.retryHandler  = retryHandler;
        this.redirectHandler = redirectHandler;
        this.authHandler   = authHandler;
        this.params        = params;
        this.requestExec   = new HttpRequestExecutor();

        this.managedConn   = null;
       
        this.redirectCount = 0;
        this.maxRedirects = this.params.getIntParameter(HttpClientParams.MAX_REDIRECTS, 100);
        this.targetAuthState = new AuthState();
        this.proxyAuthState = new AuthState();
    } // constructor


    // non-javadoc, see interface ClientRequestDirector
    public ManagedClientConnection getConnection() {
        return managedConn;
    }

   
    private RequestWrapper wrapRequest(
            final HttpRequest request) throws ProtocolException {
        try {
            if (request instanceof HttpEntityEnclosingRequest) {
                return new EntityEnclosingRequestWrapper(
                        (HttpEntityEnclosingRequest) request);
            } else {
                return new RequestWrapper(
                        request);
            }
        } catch (URISyntaxException ex) {
            throw new ProtocolException("Invalid URI: " +
                    request.getRequestLine().getUri(), ex);
        }
    }
   
   
    private 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()) {
                    HttpHost target = route.getTargetHost();
                    uri = new URI(
                            target.getSchemeName(),
                            null,
                            target.getHostName(),
                            target.getPort(),
                            uri.getPath(),
                            uri.getQuery(),
                            uri.getFragment());
                    request.setURI(uri);
                }
            } else {
                // Make sure the request URI is relative
                if (uri.isAbsolute()) {
                    uri = new URI(null, null, null, -1,
                            uri.getPath(),
                            uri.getQuery(),
                            uri.getFragment());
                    request.setURI(uri);
                }
            }
           
        } catch (URISyntaxException ex) {
            throw new ProtocolException("Invalid URI: " +
                    request.getRequestLine().getUri(), ex);
        }
    }
   
   
    // non-javadoc, see interface ClientRequestDirector
    public HttpResponse execute(RoutedRequest roureq, HttpContext context)
        throws HttpException, IOException {

        HttpRequest orig = roureq.getRequest();
        HttpParamsLinker.link(orig, this.params);

        // Add default headers
        Collection defHeaders = (Collection) orig.getParams().getParameter(
                HttpClientParams.DEFAULT_HEADERS);
        if (defHeaders != null) {
            for (Iterator it = defHeaders.iterator(); it.hasNext(); ) {
                orig.addHeader((Header) it.next());
            }
        }

        long timeout = HttpClientParams.getConnectionManagerTimeout(params);
       
        int execCount = 0;
       
        HttpResponse response = null;
        boolean done = false;
        try {
            while (!done) {

                HttpRoute route = roureq.getRoute();

                // Allocate connection if needed
                if (managedConn == null) {
                    managedConn = allocateConnection(route, timeout);
                }
                // Reopen connection if needed
                if (!managedConn.isOpen()) {
                    managedConn.open(route, context, params);
                }
               
                try {
                    establishRoute(route, context);
                } catch (TunnelRefusedException ex) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug(ex.getMessage());
                    }
                    response = ex.getResponse();
                    break;
                }

                if (HttpConnectionParams.isStaleCheckingEnabled(params)) {
                    // validate connection
                    LOG.debug("Stale connection check");
                    if (managedConn.isStale()) {
                        LOG.debug("Stale connection detected");
                        managedConn.close();
                        continue;
                    }
                }

                // Wrap the original request
                RequestWrapper request = wrapRequest(roureq.getRequest());
               
                // Re-write request URI if needed
                rewriteRequestURI(request, route);
               
                // Use virtual host if set
                HttpHost target = (HttpHost) request.getParams().getParameter(
                        HttpClientParams.VIRTUAL_HOST);
               
                if (target == null) {
                    target = route.getTargetHost();
                }

                HttpHost proxy = route.getProxyHost();
               
                // Populate the execution context
                context.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST,
                        target);
                context.setAttribute(HttpExecutionContext.HTTP_PROXY_HOST,
                        proxy);
                context.setAttribute(HttpExecutionContext.HTTP_CONNECTION,
                        managedConn);
                context.setAttribute(HttpClientContext.TARGET_AUTH_STATE,
                        targetAuthState);
                context.setAttribute(HttpClientContext.PROXY_AUTH_STATE,
                        proxyAuthState);
                requestExec.preProcess(request, httpProcessor, context);
               
                if (orig instanceof AbortableHttpRequest) {
                    ((AbortableHttpRequest) orig).setReleaseTrigger(managedConn);
                }

                context.setAttribute(HttpExecutionContext.HTTP_REQUEST,
                        request);

                execCount++;
                try {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Attempt " + execCount + " to execute request");
                    }
                    response = requestExec.execute(request, managedConn, context);
                   
                } catch (IOException ex) {
                    LOG.debug("Closing the connection.");
                    managedConn.close();
                    if (retryHandler.retryRequest(ex, execCount, context)) {
                        if (LOG.isInfoEnabled()) {
                            LOG.info("I/O exception ("+ ex.getClass().getName() +
                                    ") caught when processing request: "
                                    + ex.getMessage());
                        }
                        if (LOG.isDebugEnabled()) {
                            LOG.debug(ex.getMessage(), ex);
                        }
                        LOG.info("Retrying request");
                        continue;
                    }
                    throw ex;
                }

                HttpParamsLinker.link(request, this.params);
                requestExec.postProcess(response, httpProcessor, context);
               
                RoutedRequest followup =
                    handleResponse(roureq, request, response, context);
                if (followup == null) {
                    done = true;
                } else {
                    if (this.reuseStrategy.keepAlive(response, context)) {
                        LOG.debug("Connection kept alive");
                        // Make sure the response body is fully consumed, if present
                        HttpEntity entity = response.getEntity();
                        if (entity != null) {
                            entity.consumeContent();
                        }
                    } else {
                        managedConn.close();
                    }
                    // check if we can use the same connection for the followup
                    if ((managedConn != null) &&
                        !followup.getRoute().equals(roureq.getRoute())) {
                        // the followup has a different route, release conn
                        //@@@ need to consume response body first?
                        //@@@ or let that be done in handleResponse(...)?
                        connManager.releaseConnection(managedConn);
                        managedConn = null;
                    }
                    roureq = followup;
                }
            } // while not done

            // The connection is in or can be brought to a re-usable state.
            boolean reuse = reuseStrategy.keepAlive(response, context);

            // check for entity, release connection if possible
            if ((response == null) || (response.getEntity() == null) ||
                !response.getEntity().isStreaming()) {
                // connection not needed and (assumed to be) in re-usable state
                if (reuse)
                    managedConn.markReusable();
                connManager.releaseConnection(managedConn);
                managedConn = null;
            } else {
                // install an auto-release entity
                HttpEntity entity = response.getEntity();
                entity = new BasicManagedEntity(entity, managedConn, reuse);
                response.setEntity(entity);
            }

            return response;
           
        } catch (HttpException ex) {
            abortConnection();
            throw ex;
        } catch (IOException ex) {
            abortConnection();
            throw ex;
        } catch (RuntimeException ex) {
            abortConnection();
            throw ex;
        }
    } // execute


    /**
     * Obtains a connection for the target route.
     *
     * @param route     the route for which to allocate a connection
     *
     * @throws HttpException    in case of a problem
     */
    protected ManagedClientConnection allocateConnection(HttpRoute route, long timeout)
        throws HttpException, ConnectionPoolTimeoutException {

        return connManager.getConnection(route, timeout);

    } // allocateConnection


    /**
     * Establishes the target route.
     *
     * @param route     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(HttpRoute route, HttpContext context)
        throws HttpException, IOException {

        //@@@ how to handle CONNECT requests for tunnelling?
        //@@@ refuse to send external CONNECT via director? special handling?

        //@@@ 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?
        //@@@ probably not, because the parent is replaced
        //@@@ just make sure we don't link parameters to themselves

        RouteDirector rowdy = new RouteDirector();
        int step;
        do {
            HttpRoute fact = managedConn.getRoute();
            step = rowdy.nextStep(route, fact);

            switch (step) {

            case RouteDirector.CONNECT_TARGET:
            case RouteDirector.CONNECT_PROXY:
                managedConn.open(route, context, this.params);
                break;

            case RouteDirector.TUNNEL_TARGET:
                boolean secure = createTunnel(route, context);
                LOG.debug("Tunnel created");
                managedConn.tunnelCreated(secure, this.params);
                break;

            case RouteDirector.TUNNEL_PROXY:
                throw new UnsupportedOperationException
                    ("Proxy chains are not supported.");

            case RouteDirector.LAYER_PROTOCOL:
                managedConn.layerProtocol(context, this.params);
                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, the response received and checked.
     * This method does <i>not</i> update the connection with
     * information about the tunnel, that is left to the caller.
     *
     * @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)
        throws HttpException, IOException {

        HttpHost proxy = route.getProxyHost();
        HttpHost target = route.getTargetHost();
        HttpResponse response = null;
       
        boolean done = false;
        while (!done) {

            done = true;
           
            if (!this.managedConn.isOpen()) {
                this.managedConn.open(route, context, this.params);
            }
           
            HttpRequest connect = createConnectRequest(route, context);
           
            String agent = HttpProtocolParams.getUserAgent(params);
            if (agent != null) {
                connect.addHeader(HTTP.USER_AGENT, agent);
            }
            connect.addHeader(HTTP.TARGET_HOST, target.toHostString());
           
            AuthScheme authScheme = this.proxyAuthState.getAuthScheme();
            AuthScope authScope = this.proxyAuthState.getAuthScope();
            Credentials creds = this.proxyAuthState.getCredentials();
            if (creds != null) {
                if (authScope != null || !authScheme.isConnectionBased()) {
                    try {
                        connect.addHeader(authScheme.authenticate(creds, connect));
                    } catch (AuthenticationException ex) {
                        if (LOG.isErrorEnabled()) {
                            LOG.error("Proxy authentication error: " + ex.getMessage());
                        }
                    }
                }
            }
           
            response = requestExec.execute(connect, this.managedConn, context);
           
            int status = response.getStatusLine().getStatusCode();
            if (status < 200) {
                throw new HttpException("Unexpected response to CONNECT request: " +
                        response.getStatusLine());
            }
           
            HttpState state = (HttpState) context.getAttribute(HttpClientContext.HTTP_STATE);
           
            if (state != null && HttpClientParams.isAuthenticating(params)) {
                if (this.authHandler.isProxyAuthenticationRequested(response, context)) {

                    LOG.debug("Proxy requested authentication");
                    Map challenges = this.authHandler.getProxyChallenges(response, context);
                    try {
                        processChallenges(challenges, this.proxyAuthState, response, context);
                    } catch (AuthenticationException ex) {
                        if (LOG.isWarnEnabled()) {
                            LOG.warn("Authentication error: " +  ex.getMessage());
                            break;
                        }
                    }
                    updateAuthState(this.proxyAuthState, proxy, state);
                   
                    if (this.proxyAuthState.getCredentials() != null) {
                        done = false;

                        // Retry request
                        if (this.reuseStrategy.keepAlive(response, context)) {
                            LOG.debug("Connection kept alive");
                            // Consume response content
                            HttpEntity entity = response.getEntity();
                            if (entity != null) {
                                entity.consumeContent();
                            }               
                        } else {
                            this.managedConn.close();
                        }
                       
                    }
                   
                } else {
                    // Reset proxy auth scope
                    this.proxyAuthState.setAuthScope(null);
                }
            }
        }
       
        int status = response.getStatusLine().getStatusCode();

        if (status > 299) {

            // Buffer response content
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                response.setEntity(new BufferedHttpEntity(entity));
            }               
           
            this.managedConn.close();
            throw new TunnelRefusedException("CONNECT refused by proxy: " +
                    response.getStatusLine(), response);
        }

        this.managedConn.markReusable();
       
        // 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 insecure by default here.
        return false;
    }


    /**
     * Creates the CONNECT request for tunnelling.
     * Called by {@link #createTunnel createTunnel}.
     *
     * @param route     the route to establish
     * @param context   the context for request execution
     *
     * @return  the CONNECT request for tunnelling
     */
    protected HttpRequest createConnectRequest(HttpRoute route,
                                               HttpContext context) {
        // see RFC 2817, section 5.2 and
        // INTERNET-DRAFT: Tunneling TCP based protocols through
        // Web proxy servers
           
        HttpHost target = route.getTargetHost();
       
        String host = target.getHostName();
        int port = target.getPort();
        if (port < 0) {
            Scheme scheme = connManager.getSchemeRegistry().
                getScheme(target.getSchemeName());
            port = scheme.getDefaultPort();
        }
       
        CharArrayBuffer buffer = new CharArrayBuffer(host.length() + 6);
        buffer.append(host);
        buffer.append(":");
        buffer.append(Integer.toString(port));
       
        String authority = buffer.toString();
        HttpVersion ver = HttpProtocolParams.getVersion(params);
        HttpRequest req = new BasicHttpRequest
            ("CONNECT", authority, ver);

        return req;
    }


    /**
     * Analyzes a response to check need for a followup.
     *
     * @param roureq    the request and route. This is the same object as
     *                  was passed to {@link #wrapRequest(HttpRequest)}.
     * @param request   the request that was actually sent. This is the object
     *                  returned by {@link #wrapRequest(HttpRequest)}.
     * @param response  the response to analayze
     * @param context   the context used for the current request execution
     *
     * @return  the followup request and route if there is a followup, or
     *          <code>null</code> if the response should be returned as is
     *
     * @throws HttpException    in case of a problem
     * @throws IOException      in case of an IO problem
     */
    protected RoutedRequest handleResponse(RoutedRequest roureq,
                                           HttpRequest request,
                                           HttpResponse response,
                                           HttpContext context)
        throws HttpException, IOException {

        HttpRoute route = roureq.getRoute();
        HttpHost target = route.getTargetHost();
        HttpHost proxy = route.getProxyHost();
        InetAddress localAddress = route.getLocalAddress();
       
        HttpParams params = request.getParams();
        if (HttpClientParams.isRedirecting(params) &&
                this.redirectHandler.isRedirectRequested(response, context)) {

            if (redirectCount >= maxRedirects) {
                throw new RedirectException("Maximum redirects ("
                        + maxRedirects + ") exceeded");
            }
            redirectCount++;
           
            URI uri;
            try {
                uri = this.redirectHandler.getLocationURI(response, context);
            } catch (ProtocolException ex) {
                if (LOG.isWarnEnabled()) {
                    LOG.warn(ex.getMessage());
                }
                return null;
            }

            HttpHost newTarget = new HttpHost(
                    uri.getHost(),
                    uri.getPort(),
                    uri.getScheme());
           
            Scheme schm = connManager.getSchemeRegistry().
                getScheme(newTarget.getSchemeName());
           
            HttpRoute newRoute = new HttpRoute(
                    newTarget,
                    localAddress,
                    proxy,
                    schm.isLayered(),
                    (proxy != null),
                    (proxy != null));

            HttpGet redirect = new HttpGet(uri);
           
            if (LOG.isDebugEnabled()) {
                LOG.debug("Redirecting to '" + uri + "' via " + newRoute);
            }
           
            return new RoutedRequest.Impl(redirect, newRoute);
        }

        HttpState state = (HttpState) context.getAttribute(HttpClientContext.HTTP_STATE);
       
        if (state != null && HttpClientParams.isAuthenticating(params)) {

            if (this.authHandler.isTargetAuthenticationRequested(response, context)) {

                target = (HttpHost) context.getAttribute(HttpExecutionContext.HTTP_TARGET_HOST);
                if (target == null) {
                    target = route.getTargetHost();
                }
               
                LOG.debug("Target requested authentication");
                Map challenges = this.authHandler.getTargetChallenges(response, context);
                try {
                    processChallenges(challenges, this.targetAuthState, response, context);
                } catch (AuthenticationException ex) {
                    if (LOG.isWarnEnabled()) {
                        LOG.warn("Authentication error: " +  ex.getMessage());
                        return null;
                    }
                }
                updateAuthState(this.targetAuthState, target, state);
               
                if (this.targetAuthState.getCredentials() != null) {
                    // Re-try the same request via the same route
                    return roureq;
                } else {
                    return null;
                }
            } else {
                // Reset target auth scope
                this.targetAuthState.setAuthScope(null);
            }
           
            if (this.authHandler.isProxyAuthenticationRequested(response, context)) {

                LOG.debug("Proxy requested authentication");
                Map challenges = this.authHandler.getProxyChallenges(response, context);
                try {
                    processChallenges(challenges, this.proxyAuthState, response, context);
                } catch (AuthenticationException ex) {
                    if (LOG.isWarnEnabled()) {
                        LOG.warn("Authentication error: " +  ex.getMessage());
                        return null;
                    }
                }
                updateAuthState(this.proxyAuthState, proxy, state);
               
                if (this.proxyAuthState.getCredentials() != null) {
                    // Re-try the same request via the same route
                    return roureq;
                } else {
                    return null;
                }
            } else {
                // Reset proxy auth scope
                this.proxyAuthState.setAuthScope(null);
            }
        }
        return null;
    } // handleResponse


    /**
     * Shuts down the connection.
     * This method is called from a <code>catch</code> block in
     * {@link #execute execute} during exception handling.
     *
     * @throws IOException      in case of an IO problem
     */
    private void abortConnection() throws IOException {

        ManagedClientConnection mcc = managedConn;
        if (mcc != null) {
            // we got here as the result of an exception
            // no response will be returned, release the connection
            managedConn = null;
            //@@@ is the connection in a re-usable state? consume response?
            //@@@ for now, just shut it down
            try {
                mcc.abortConnection();
            } catch (IOException ex) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug(ex.getMessage(), ex);
                }
            }
        }
    } // abortConnection


    private void processChallenges(
            final Map challenges,
            final AuthState authState,
            final HttpResponse response,
            final HttpContext context)
                throws MalformedChallengeException, AuthenticationException {
       
        AuthScheme authScheme = authState.getAuthScheme();
        if (authScheme == null) {
            // Authentication not attempted before
            authScheme = this.authHandler.selectScheme(challenges, response, context);
            authState.setAuthScheme(authScheme);
        }
        AuthScheme authscheme = authState.getAuthScheme();
        String id = authscheme.getSchemeName();

        Header challenge = (Header) challenges.get(id.toLowerCase());
        if (challenge == null) {
            throw new AuthenticationException(id +
                " authorization challenge expected, but not found");
        }
        authscheme.processChallenge(challenge);
        LOG.debug("Authorization challenge processed");
    }
   
   
    private void updateAuthState(
            final AuthState authState,
            final HttpHost host,
            final HttpState state) {
       
        String hostname = host.getHostName();
        int port = host.getPort();
        if (port < 0) {
            Scheme scheme = connManager.getSchemeRegistry().getScheme(host);
            port = scheme.getDefaultPort();
        }
       
        AuthScheme authScheme = authState.getAuthScheme();
        AuthScope authScope = new AuthScope(
                hostname,
                port,
                authScheme.getRealm(),
                authScheme.getSchemeName())
       
        if (LOG.isDebugEnabled()) {
            LOG.debug("Authentication scope: " + authScope);
        }
        Credentials creds = authState.getCredentials();
        if (creds == null) {
            creds = state.getCredentials(authScope);
            if (LOG.isDebugEnabled()) {
                if (creds != null) {
                    LOG.debug("Found credentials");
                } else {
                    LOG.debug("Credentials not found");
                }
            }
        } else {
            if (authScheme.isComplete()) {
                LOG.debug("Authentication failed");
                creds = null;
            }
        }
        authState.setAuthScope(authScope);
        authState.setCredentials(creds);
    }
   
} // class DefaultClientRequestDirector
TOP

Related Classes of org.apache.http.impl.client.DefaultClientRequestDirector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.