View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.impl.nio.client;
28  
29  import java.io.IOException;
30  import java.net.URI;
31  import java.net.URISyntaxException;
32  import java.nio.ByteBuffer;
33  import java.util.List;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.http.HttpException;
38  import org.apache.http.HttpHost;
39  import org.apache.http.HttpRequest;
40  import org.apache.http.HttpResponse;
41  import org.apache.http.HttpStatus;
42  import org.apache.http.HttpVersion;
43  import org.apache.http.ProtocolException;
44  import org.apache.http.auth.AUTH;
45  import org.apache.http.auth.AuthProtocolState;
46  import org.apache.http.auth.AuthScheme;
47  import org.apache.http.auth.AuthScope;
48  import org.apache.http.auth.AuthState;
49  import org.apache.http.auth.UsernamePasswordCredentials;
50  import org.apache.http.client.AuthenticationStrategy;
51  import org.apache.http.client.CredentialsProvider;
52  import org.apache.http.client.NonRepeatableRequestException;
53  import org.apache.http.client.RedirectException;
54  import org.apache.http.client.RedirectStrategy;
55  import org.apache.http.client.UserTokenHandler;
56  import org.apache.http.client.config.RequestConfig;
57  import org.apache.http.client.methods.Configurable;
58  import org.apache.http.client.methods.HttpRequestWrapper;
59  import org.apache.http.client.methods.HttpUriRequest;
60  import org.apache.http.client.protocol.HttpClientContext;
61  import org.apache.http.client.protocol.RequestClientConnControl;
62  import org.apache.http.client.utils.URIUtils;
63  import org.apache.http.conn.routing.BasicRouteDirector;
64  import org.apache.http.conn.routing.HttpRoute;
65  import org.apache.http.conn.routing.HttpRouteDirector;
66  import org.apache.http.conn.routing.HttpRoutePlanner;
67  import org.apache.http.impl.auth.HttpAuthenticator;
68  import org.apache.http.message.BasicHttpRequest;
69  import org.apache.http.nio.ContentDecoder;
70  import org.apache.http.nio.ContentEncoder;
71  import org.apache.http.nio.IOControl;
72  import org.apache.http.nio.NHttpClientConnection;
73  import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
74  import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
75  import org.apache.http.protocol.HttpCoreContext;
76  import org.apache.http.protocol.HttpProcessor;
77  import org.apache.http.protocol.ImmutableHttpProcessor;
78  import org.apache.http.protocol.RequestTargetHost;
79  
80  class MainClientExec implements InternalClientExec {
81  
82      private final Log log = LogFactory.getLog(getClass());
83  
84      private final HttpProcessor httpProcessor;
85      private final HttpProcessor proxyHttpProcessor;
86      private final HttpRoutePlanner routePlanner;
87      private final AuthenticationStrategy targetAuthStrategy;
88      private final AuthenticationStrategy proxyAuthStrategy;
89      private final UserTokenHandler userTokenHandler;
90      private final RedirectStrategy redirectStrategy;
91      private final HttpRouteDirector routeDirector;
92      private final HttpAuthenticator authenticator;
93  
94      public MainClientExec(
95              final HttpProcessor httpProcessor,
96              final HttpRoutePlanner routePlanner,
97              final RedirectStrategy redirectStrategy,
98              final AuthenticationStrategy targetAuthStrategy,
99              final AuthenticationStrategy proxyAuthStrategy,
100             final UserTokenHandler userTokenHandler) {
101         super();
102         this.httpProcessor = httpProcessor;
103         this.proxyHttpProcessor = new ImmutableHttpProcessor(
104                 new RequestTargetHost(), new RequestClientConnControl());
105         this.routePlanner = routePlanner;
106         this.redirectStrategy = redirectStrategy;
107         this.targetAuthStrategy = targetAuthStrategy;
108         this.proxyAuthStrategy = proxyAuthStrategy;
109         this.userTokenHandler = userTokenHandler;
110         this.routeDirector = new BasicRouteDirector();
111         this.authenticator = new HttpAuthenticator(log);
112     }
113 
114     @Override
115     public void prepare(
116             final HttpHost target,
117             final HttpRequest original,
118             final InternalState state,
119             final AbstractClientExchangeHandler handler) throws HttpException, IOException {
120         if (this.log.isDebugEnabled()) {
121             this.log.debug("[exchange: " + state.getId() + "] start execution");
122         }
123 
124         final HttpClientContext localContext = state.getLocalContext();
125 
126         if (original instanceof Configurable) {
127             final RequestConfig config = ((Configurable) original).getConfig();
128             if (config != null) {
129                 localContext.setRequestConfig(config);
130             }
131         }
132 
133         final List<URI> redirectLocations = localContext.getRedirectLocations();
134         if (redirectLocations != null) {
135             redirectLocations.clear();
136         }
137 
138         final HttpRequestWrapper request = HttpRequestWrapper.wrap(original);
139         final HttpRoute route = this.routePlanner.determineRoute(target, request, localContext);
140 
141         handler.setRoute(route);
142 
143         state.setMainRequest(request);
144         handler.setCurrentRequest(request);
145 
146         prepareRequest(state, handler);
147     }
148 
149     @Override
150     public HttpRequest generateRequest(
151             final InternalState state,
152             final AbstractClientExchangeHandler handler) throws IOException, HttpException {
153 
154         final HttpRoute route = handler.getRoute();
155 
156         handler.verifytRoute();
157 
158         if (!handler.isRouteEstablished()) {
159             int step;
160             loop:
161             do {
162                 final HttpRoute fact = handler.getActualRoute();
163                 step = this.routeDirector.nextStep(route, fact);
164                 switch (step) {
165                 case HttpRouteDirector.CONNECT_TARGET:
166                     handler.onRouteToTarget();
167                     break;
168                 case HttpRouteDirector.CONNECT_PROXY:
169                     handler.onRouteToProxy();
170                     break;
171                 case HttpRouteDirector.TUNNEL_TARGET:
172                     if (this.log.isDebugEnabled()) {
173                         this.log.debug("[exchange: " + state.getId() + "] Tunnel required");
174                     }
175                     final HttpRequest connect = createConnectRequest(route, state);
176                     handler.setCurrentRequest(HttpRequestWrapper.wrap(connect));
177                     break loop;
178                 case HttpRouteDirector.TUNNEL_PROXY:
179                     throw new HttpException("Proxy chains are not supported");
180                 case HttpRouteDirector.LAYER_PROTOCOL:
181                     handler.onRouteUpgrade();
182                     break;
183                 case HttpRouteDirector.UNREACHABLE:
184                     throw new HttpException("Unable to establish route: " +
185                             "planned = " + route + "; current = " + fact);
186                 case HttpRouteDirector.COMPLETE:
187                     handler.onRouteComplete();
188                     this.log.debug("[exchange: " + state.getId() + "] Connection route established");
189                     break;
190                 default:
191                     throw new IllegalStateException("Unknown step indicator "
192                             + step + " from RouteDirector.");
193                 }
194             } while (step > HttpRouteDirector.COMPLETE);
195         }
196 
197         final HttpClientContext localContext = state.getLocalContext();
198         HttpRequestWrapper currentRequest = handler.getCurrentRequest();
199         if (currentRequest == null) {
200             currentRequest = state.getMainRequest();
201             handler.setCurrentRequest(currentRequest);
202         }
203 
204         if (handler.isRouteEstablished()) {
205             state.incrementExecCount();
206             if (state.getExecCount() > 1) {
207                 final HttpAsyncRequestProducer requestProducer = state.getRequestProducer();
208                 if (!requestProducer.isRepeatable() && state.isRequestContentProduced()) {
209                     throw new NonRepeatableRequestException("Cannot retry request " +
210                             "with a non-repeatable request entity.");
211                 }
212                 requestProducer.resetRequest();
213             }
214             if (this.log.isDebugEnabled()) {
215                 this.log.debug("[exchange: " + state.getId() + "] Attempt " + state.getExecCount() +
216                     " to execute request");
217             }
218 
219             if (!currentRequest.containsHeader(AUTH.WWW_AUTH_RESP)) {
220                 final AuthState targetAuthState = localContext.getTargetAuthState();
221                 if (this.log.isDebugEnabled()) {
222                     this.log.debug("[exchange: " + state.getId() + "] Target auth state: " + targetAuthState.getState());
223                 }
224                 this.authenticator.generateAuthResponse(currentRequest, targetAuthState, localContext);
225             }
226             if (!currentRequest.containsHeader(AUTH.PROXY_AUTH_RESP) && !route.isTunnelled()) {
227                 final AuthState proxyAuthState = localContext.getProxyAuthState();
228                 if (this.log.isDebugEnabled()) {
229                     this.log.debug("[exchange: " + state.getId() + "] Proxy auth state: " + proxyAuthState.getState());
230                 }
231                 this.authenticator.generateAuthResponse(currentRequest, proxyAuthState, localContext);
232             }
233         } else {
234             if (!currentRequest.containsHeader(AUTH.PROXY_AUTH_RESP)) {
235                 final AuthState proxyAuthState = localContext.getProxyAuthState();
236                 if (this.log.isDebugEnabled()) {
237                     this.log.debug("[exchange: " + state.getId() + "] Proxy auth state: " + proxyAuthState.getState());
238                 }
239                 this.authenticator.generateAuthResponse(currentRequest, proxyAuthState, localContext);
240             }
241         }
242 
243         final NHttpClientConnection managedConn = handler.getConnection();
244         localContext.setAttribute(HttpCoreContext.HTTP_CONNECTION, managedConn);
245         final RequestConfig config = localContext.getRequestConfig();
246         if (config.getSocketTimeout() > 0) {
247             managedConn.setSocketTimeout(config.getSocketTimeout());
248         }
249         return currentRequest;
250     }
251 
252     @Override
253     public void produceContent(
254             final InternalState state,
255             final ContentEncoder encoder,
256             final IOControl ioControl) throws IOException {
257         if (this.log.isDebugEnabled()) {
258             this.log.debug("[exchange: " + state.getId() + "] produce content");
259         }
260         final HttpAsyncRequestProducer requestProducer = state.getRequestProducer();
261         state.setRequestContentProduced();
262         requestProducer.produceContent(encoder, ioControl);
263         if (encoder.isCompleted()) {
264             requestProducer.resetRequest();
265         }
266     }
267 
268     @Override
269     public void requestCompleted(
270             final InternalState state,
271             final AbstractClientExchangeHandler handler) {
272         if (this.log.isDebugEnabled()) {
273             this.log.debug("[exchange: " + state.getId() + "] Request completed");
274         }
275         final HttpClientContext localContext = state.getLocalContext();
276         final HttpAsyncRequestProducer requestProducer = state.getRequestProducer();
277         requestProducer.requestCompleted(localContext);
278     }
279 
280     @Override
281     public void responseReceived(
282             final HttpResponse response,
283             final InternalState state,
284             final AbstractClientExchangeHandler handler) throws IOException, HttpException {
285         if (this.log.isDebugEnabled()) {
286             this.log.debug("[exchange: " + state.getId() + "] Response received " + response.getStatusLine());
287         }
288         final HttpClientContext context = state.getLocalContext();
289         context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
290         this.httpProcessor.process(response, context);
291 
292         handler.setCurrentResponse(response);
293 
294         if (!handler.isRouteEstablished()) {
295             final int status = response.getStatusLine().getStatusCode();
296             if (status < 200) {
297                 throw new HttpException("Unexpected response to CONNECT request: " +
298                         response.getStatusLine());
299             }
300             if (status == HttpStatus.SC_OK) {
301                 handler.onRouteTunnelToTarget();
302                 handler.setCurrentRequest(null);
303             } else {
304                 if (!handleConnectResponse(state, handler)) {
305                     state.setFinalResponse(response);
306                 }
307             }
308         } else {
309             if (!handleResponse(state, handler)) {
310                 state.setFinalResponse(response);
311             }
312         }
313         if (state.getFinalResponse() != null) {
314             final HttpAsyncResponseConsumer<?> responseConsumer = state.getResponseConsumer();
315             responseConsumer.responseReceived(response);
316         }
317     }
318 
319     @Override
320     public void consumeContent(
321             final InternalState state,
322             final ContentDecoder decoder,
323             final IOControl ioControl) throws IOException {
324         if (this.log.isDebugEnabled()) {
325             this.log.debug("[exchange: " + state.getId() + "] Consume content");
326         }
327         if (state.getFinalResponse() != null) {
328             final HttpAsyncResponseConsumer<?> responseConsumer = state.getResponseConsumer();
329             responseConsumer.consumeContent(decoder, ioControl);
330         } else {
331             final ByteBuffer tmpbuf = state.getTmpbuf();
332             tmpbuf.clear();
333             decoder.read(tmpbuf);
334         }
335     }
336 
337     @Override
338     public void responseCompleted(
339             final InternalState state,
340             final AbstractClientExchangeHandler handler) throws IOException, HttpException {
341         final HttpClientContext localContext = state.getLocalContext();
342         final HttpResponse currentResponse = handler.getCurrentResponse();
343 
344         if (!handler.isRouteEstablished()) {
345             final int status = currentResponse.getStatusLine().getStatusCode();
346             if (status == HttpStatus.SC_OK) {
347                 handler.setCurrentResponse(null);
348                 return;
349             }
350         }
351 
352         final boolean keepAlive = handler.manageConnectionPersistence();
353         if (!keepAlive) {
354             handler.releaseConnection();
355             final AuthState proxyAuthState = localContext.getProxyAuthState();
356             if (proxyAuthState.getState() == AuthProtocolState.SUCCESS
357                     && proxyAuthState.getAuthScheme() != null
358                     && proxyAuthState.getAuthScheme().isConnectionBased()) {
359                 if (this.log.isDebugEnabled()) {
360                     this.log.debug("[exchange: " + state.getId() + "] Resetting proxy auth state");
361                 }
362                 proxyAuthState.reset();
363             }
364             final AuthState targetAuthState = localContext.getTargetAuthState();
365             if (targetAuthState.getState() == AuthProtocolState.SUCCESS
366                     && targetAuthState.getAuthScheme() != null
367                     && targetAuthState.getAuthScheme().isConnectionBased()) {
368                 if (this.log.isDebugEnabled()) {
369                     this.log.debug("[exchange: " + state.getId() + "] Resetting target auth state");
370                 }
371                 targetAuthState.reset();
372             }
373         }
374 
375         Object userToken = localContext.getUserToken();
376         if (userToken == null) {
377             userToken = this.userTokenHandler.getUserToken(localContext);
378             localContext.setAttribute(HttpClientContext.USER_TOKEN, userToken);
379         }
380 
381         if (state.getFinalResponse() != null) {
382             final HttpAsyncResponseConsumer<?> responseConsumer = state.getResponseConsumer();
383             responseConsumer.responseCompleted(localContext);
384             if (this.log.isDebugEnabled()) {
385                 this.log.debug("[exchange: " + state.getId() + "] Response processed");
386             }
387             handler.releaseConnection();
388         } else {
389             if (state.getRedirect() != null) {
390                 final HttpUriRequest redirect = state.getRedirect();
391                 final URI uri = redirect.getURI();
392                 if (this.log.isDebugEnabled()) {
393                     this.log.debug("[exchange: " + state.getId() + "] Redirecting to '" + uri + "'");
394                 }
395                 state.setRedirect(null);
396 
397                 final HttpHost newTarget = URIUtils.extractHost(uri);
398                 if (newTarget == null) {
399                     throw new ProtocolException("Redirect URI does not specify a valid host name: " + uri);
400                 }
401 
402                 // Reset auth states if redirecting to another host
403                 final HttpRoute route = handler.getRoute();
404                 if (!route.getTargetHost().equals(newTarget)) {
405                     final AuthState targetAuthState = localContext.getTargetAuthState();
406                     if (this.log.isDebugEnabled()) {
407                         this.log.debug("[exchange: " + state.getId() + "] Resetting target auth state");
408                     }
409                     targetAuthState.reset();
410                     final AuthState proxyAuthState = localContext.getProxyAuthState();
411                     final AuthScheme authScheme = proxyAuthState.getAuthScheme();
412                     if (authScheme != null && authScheme.isConnectionBased()) {
413                         if (this.log.isDebugEnabled()) {
414                             this.log.debug("[exchange: " + state.getId() + "] Resetting proxy auth state");
415                         }
416                         proxyAuthState.reset();
417                     }
418                 }
419 
420                 if (!redirect.headerIterator().hasNext()) {
421                     final HttpRequest original = state.getMainRequest().getOriginal();
422                     redirect.setHeaders(original.getAllHeaders());
423                 }
424 
425                 final HttpRequestWrapper newRequest = HttpRequestWrapper.wrap(redirect);
426                 final HttpRoute newRoute = this.routePlanner.determineRoute(
427                     newTarget, newRequest, localContext);
428                 if (!route.equals(newRoute)) {
429                     handler.releaseConnection();
430                 }
431                 handler.setRoute(newRoute);
432                 handler.setCurrentRequest(newRequest);
433                 state.setMainRequest(newRequest);
434                 prepareRequest(state, handler);
435             }
436         }
437         handler.setCurrentResponse(null);
438     }
439 
440     private void rewriteRequestURI(
441             final HttpRequestWrapper request,
442             final HttpRoute route) throws ProtocolException {
443         try {
444             URI uri = request.getURI();
445             if (uri != null) {
446                 if (route.getProxyHost() != null && !route.isTunnelled()) {
447                     // Make sure the request URI is absolute
448                     if (!uri.isAbsolute()) {
449                         final HttpHost target = route.getTargetHost();
450                         uri = URIUtils.rewriteURI(uri, target, true);
451                     } else {
452                         uri = URIUtils.rewriteURI(uri);
453                     }
454                 } else {
455                     // Make sure the request URI is relative
456                     if (uri.isAbsolute()) {
457                         uri = URIUtils.rewriteURI(uri, null, true);
458                     } else {
459                         uri = URIUtils.rewriteURI(uri);
460                     }
461                 }
462                 request.setURI(uri);
463             }
464         } catch (final URISyntaxException ex) {
465             throw new ProtocolException("Invalid URI: " +
466                     request.getRequestLine().getUri(), ex);
467         }
468     }
469 
470     private void prepareRequest(
471             final InternalState state,
472             final AbstractClientExchangeHandler handler) throws IOException, HttpException {
473         final HttpClientContext localContext = state.getLocalContext();
474         final HttpRequestWrapper currentRequest = handler.getCurrentRequest();
475         final HttpRoute route = handler.getRoute();
476 
477         final HttpRequest original = currentRequest.getOriginal();
478         URI uri = null;
479         if (original instanceof HttpUriRequest) {
480             uri = ((HttpUriRequest) original).getURI();
481         } else {
482             final String uriString = original.getRequestLine().getUri();
483             try {
484                 uri = URI.create(uriString);
485             } catch (final IllegalArgumentException ex) {
486                 if (this.log.isDebugEnabled()) {
487                     this.log.debug("[exchange: " + state.getId() + "] Unable to parse '" + uriString +
488                             "' as a valid URI; request URI and Host header may be inconsistent", ex);
489                 }
490             }
491 
492         }
493         currentRequest.setURI(uri);
494 
495         // Re-write request URI if needed
496         rewriteRequestURI(currentRequest, route);
497 
498         HttpHost target = null;
499         if (uri != null && uri.isAbsolute() && uri.getHost() != null) {
500             target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
501         }
502         if (target == null) {
503             target = route.getTargetHost();
504         }
505 
506         // Get user info from the URI
507         if (uri != null) {
508             final String userinfo = uri.getUserInfo();
509             if (userinfo != null) {
510                 final CredentialsProvider credsProvider = localContext.getCredentialsProvider();
511                 credsProvider.setCredentials(
512                         new AuthScope(target),
513                         new UsernamePasswordCredentials(userinfo));
514             }
515         }
516 
517         localContext.setAttribute(HttpCoreContext.HTTP_REQUEST, currentRequest);
518         localContext.setAttribute(HttpCoreContext.HTTP_TARGET_HOST, target);
519         localContext.setAttribute(HttpClientContext.HTTP_ROUTE, route);
520         this.httpProcessor.process(currentRequest, localContext);
521     }
522 
523     private HttpRequest createConnectRequest(
524             final HttpRoute route, final InternalState state) throws IOException, HttpException {
525         // see RFC 2817, section 5.2 and
526         // INTERNET-DRAFT: Tunneling TCP based protocols through
527         // Web proxy servers
528         final HttpHost target = route.getTargetHost();
529         final String host = target.getHostName();
530         final int port = target.getPort();
531         final StringBuilder buffer = new StringBuilder(host.length() + 6);
532         buffer.append(host);
533         buffer.append(':');
534         buffer.append(Integer.toString(port));
535         final HttpRequest request = new BasicHttpRequest("CONNECT", buffer.toString(), HttpVersion.HTTP_1_1);
536         final HttpClientContext localContext = state.getLocalContext();
537         this.proxyHttpProcessor.process(request, localContext);
538         return request;
539     }
540 
541     private boolean handleConnectResponse(
542             final InternalState state,
543             final AbstractClientExchangeHandler handler) {
544         final HttpClientContext localContext = state.getLocalContext();
545         final RequestConfig config = localContext.getRequestConfig();
546         if (config.isAuthenticationEnabled()) {
547             final CredentialsProvider credsProvider = localContext.getCredentialsProvider();
548             if (credsProvider != null) {
549                 final HttpRoute route = handler.getRoute();
550                 final HttpHost proxy = route.getProxyHost();
551                 final HttpResponse currentResponse = handler.getCurrentResponse();
552                 final AuthState proxyAuthState = localContext.getProxyAuthState();
553                 if (this.authenticator.isAuthenticationRequested(proxy, currentResponse,
554                         this.proxyAuthStrategy, proxyAuthState, localContext)) {
555                     return this.authenticator.handleAuthChallenge(proxy, currentResponse,
556                             this.proxyAuthStrategy, proxyAuthState, localContext);
557                 }
558             }
559         }
560         return false;
561     }
562 
563     private boolean handleResponse(
564             final InternalState state,
565             final AbstractClientExchangeHandler handler) throws HttpException {
566         final HttpClientContext localContext = state.getLocalContext();
567         final RequestConfig config = localContext.getRequestConfig();
568         if (config.isAuthenticationEnabled()) {
569             if (needAuthentication(state, handler)) {
570                 // discard previous auth headers
571                 final HttpRequestWrapper currentRequest = handler.getCurrentRequest();
572                 final HttpRequest original = currentRequest.getOriginal();
573                 if (!original.containsHeader(AUTH.WWW_AUTH_RESP)) {
574                     currentRequest.removeHeaders(AUTH.WWW_AUTH_RESP);
575                 }
576                 if (!original.containsHeader(AUTH.PROXY_AUTH_RESP)) {
577                     currentRequest.removeHeaders(AUTH.PROXY_AUTH_RESP);
578                 }
579                 return true;
580             }
581         }
582         if (config.isRedirectsEnabled()) {
583             final HttpRequestWrapper currentRequest = handler.getCurrentRequest();
584             final HttpResponse currentResponse = handler.getCurrentResponse();
585             if (this.redirectStrategy.isRedirected(currentRequest, currentResponse, localContext)) {
586                 final int maxRedirects = config.getMaxRedirects() >= 0 ? config.getMaxRedirects() : 100;
587                 if (state.getRedirectCount() >= maxRedirects) {
588                     throw new RedirectException("Maximum redirects (" + maxRedirects + ") exceeded");
589                 }
590                 state.incrementRedirectCount();
591                 final HttpUriRequest redirect = this.redirectStrategy.getRedirect(currentRequest.getOriginal(), currentResponse,
592                     localContext);
593                 state.setRedirect(redirect);
594                 return true;
595             }
596         }
597         return false;
598     }
599 
600     private boolean needAuthentication(
601             final InternalState state,
602             final AbstractClientExchangeHandler handler) {
603         final HttpClientContext localContext = state.getLocalContext();
604         final CredentialsProvider credsProvider = localContext.getCredentialsProvider();
605         if (credsProvider != null) {
606             final HttpRoute route = handler.getRoute();
607             final HttpResponse currentResponse = handler.getCurrentResponse();
608             HttpHost target = localContext.getTargetHost();
609             if (target == null) {
610                 target = route.getTargetHost();
611             }
612             if (target.getPort() < 0) {
613                 target = new HttpHost(
614                         target.getHostName(),
615                         route.getTargetHost().getPort(),
616                         target.getSchemeName());
617             }
618             final AuthState targetAuthState = localContext.getTargetAuthState();
619             final AuthState proxyAuthState = localContext.getProxyAuthState();
620 
621             final boolean targetAuthRequested = this.authenticator.isAuthenticationRequested(
622                     target, currentResponse, this.targetAuthStrategy, targetAuthState, localContext);
623 
624             HttpHost proxy = route.getProxyHost();
625             // if proxy is not set use target host instead
626             if (proxy == null) {
627                 proxy = route.getTargetHost();
628             }
629             final boolean proxyAuthRequested = this.authenticator.isAuthenticationRequested(
630                     proxy, currentResponse, this.proxyAuthStrategy, proxyAuthState, localContext);
631 
632             if (targetAuthRequested) {
633                 return this.authenticator.handleAuthChallenge(target, currentResponse,
634                         this.targetAuthStrategy, targetAuthState, localContext);
635             }
636             if (proxyAuthRequested) {
637                 return this.authenticator.handleAuthChallenge(proxy, currentResponse,
638                         this.proxyAuthStrategy, proxyAuthState, localContext);
639             }
640         }
641         return false;
642     }
643 
644 }