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  
28  package org.apache.http.impl.client;
29  
30  import java.io.IOException;
31  import java.lang.reflect.UndeclaredThrowableException;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.http.ConnectionReuseStrategy;
36  import org.apache.http.HttpException;
37  import org.apache.http.HttpHost;
38  import org.apache.http.HttpRequest;
39  import org.apache.http.HttpRequestInterceptor;
40  import org.apache.http.HttpResponseInterceptor;
41  import org.apache.http.annotation.Contract;
42  import org.apache.http.annotation.ThreadingBehavior;
43  import org.apache.http.auth.AuthSchemeRegistry;
44  import org.apache.http.client.AuthenticationHandler;
45  import org.apache.http.client.AuthenticationStrategy;
46  import org.apache.http.client.BackoffManager;
47  import org.apache.http.client.ClientProtocolException;
48  import org.apache.http.client.ConnectionBackoffStrategy;
49  import org.apache.http.client.CookieStore;
50  import org.apache.http.client.CredentialsProvider;
51  import org.apache.http.client.HttpRequestRetryHandler;
52  import org.apache.http.client.RedirectHandler;
53  import org.apache.http.client.RedirectStrategy;
54  import org.apache.http.client.RequestDirector;
55  import org.apache.http.client.UserTokenHandler;
56  import org.apache.http.client.config.CookieSpecs;
57  import org.apache.http.client.config.RequestConfig;
58  import org.apache.http.client.methods.CloseableHttpResponse;
59  import org.apache.http.client.params.AuthPolicy;
60  import org.apache.http.client.params.ClientPNames;
61  import org.apache.http.client.params.CookiePolicy;
62  import org.apache.http.client.params.HttpClientParamConfig;
63  import org.apache.http.client.protocol.ClientContext;
64  import org.apache.http.conn.ClientConnectionManager;
65  import org.apache.http.conn.ClientConnectionManagerFactory;
66  import org.apache.http.conn.ConnectionKeepAliveStrategy;
67  import org.apache.http.conn.routing.HttpRoute;
68  import org.apache.http.conn.routing.HttpRoutePlanner;
69  import org.apache.http.conn.scheme.SchemeRegistry;
70  import org.apache.http.cookie.CookieSpecRegistry;
71  import org.apache.http.impl.DefaultConnectionReuseStrategy;
72  import org.apache.http.impl.auth.BasicSchemeFactory;
73  import org.apache.http.impl.auth.DigestSchemeFactory;
74  import org.apache.http.impl.auth.KerberosSchemeFactory;
75  import org.apache.http.impl.auth.NTLMSchemeFactory;
76  import org.apache.http.impl.auth.SPNegoSchemeFactory;
77  import org.apache.http.impl.conn.BasicClientConnectionManager;
78  import org.apache.http.impl.conn.DefaultHttpRoutePlanner;
79  import org.apache.http.impl.conn.SchemeRegistryFactory;
80  import org.apache.http.impl.cookie.BestMatchSpecFactory;
81  import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
82  import org.apache.http.impl.cookie.IgnoreSpecFactory;
83  import org.apache.http.impl.cookie.NetscapeDraftSpecFactory;
84  import org.apache.http.impl.cookie.RFC2109SpecFactory;
85  import org.apache.http.impl.cookie.RFC2965SpecFactory;
86  import org.apache.http.params.HttpParams;
87  import org.apache.http.protocol.BasicHttpContext;
88  import org.apache.http.protocol.BasicHttpProcessor;
89  import org.apache.http.protocol.DefaultedHttpContext;
90  import org.apache.http.protocol.HttpContext;
91  import org.apache.http.protocol.HttpProcessor;
92  import org.apache.http.protocol.HttpRequestExecutor;
93  import org.apache.http.protocol.ImmutableHttpProcessor;
94  import org.apache.http.util.Args;
95  
96  /**
97   * Base class for {@link org.apache.http.client.HttpClient} implementations.
98   * This class acts as a facade to a number of special purpose handler or
99   * strategy implementations responsible for handling of a particular aspect
100  * of the HTTP protocol such as redirect or authentication handling or
101  * making decision about connection persistence and keep alive duration.
102  * This enables the users to selectively replace default implementation
103  * of those aspects with custom, application specific ones. This class
104  * also provides factory methods to instantiate those objects:
105  * <ul>
106  *   <li>{@link HttpRequestExecutor} object used to transmit messages
107  *    over HTTP connections. The {@link #createRequestExecutor()} must be
108  *    implemented by concrete super classes to instantiate this object.
109  *    </li>
110  *   <li>{@link BasicHttpProcessor} object to manage a list of protocol
111  *    interceptors and apply cross-cutting protocol logic to all incoming
112  *    and outgoing HTTP messages. The {@link #createHttpProcessor()} must be
113  *    implemented by concrete super classes to instantiate this object.
114  *    </li>
115  *   <li>{@link HttpRequestRetryHandler} object used to decide whether
116  *    or not a failed HTTP request is safe to retry automatically.
117  *    The {@link #createHttpRequestRetryHandler()} must be
118  *    implemented by concrete super classes to instantiate this object.
119  *    </li>
120  *   <li>{@link ClientConnectionManager} object used to manage
121  *    persistent HTTP connections.
122  *    </li>
123  *   <li>{@link ConnectionReuseStrategy} object used to decide whether
124  *    or not a HTTP connection can be kept alive and re-used for subsequent
125  *    HTTP requests. The {@link #createConnectionReuseStrategy()} must be
126  *    implemented by concrete super classes to instantiate this object.
127  *    </li>
128  *   <li>{@link ConnectionKeepAliveStrategy} object used to decide how
129  *    long a persistent HTTP connection can be kept alive.
130  *    The {@link #createConnectionKeepAliveStrategy()} must be
131  *    implemented by concrete super classes to instantiate this object.
132  *    </li>
133  *   <li>{@link CookieSpecRegistry} object used to maintain a list of
134  *    supported cookie specifications.
135  *    The {@link #createCookieSpecRegistry()} must be implemented by concrete
136  *    super classes to instantiate this object.
137  *    </li>
138  *   <li>{@link CookieStore} object used to maintain a collection of
139  *    cookies. The {@link #createCookieStore()} must be implemented by
140  *    concrete super classes to instantiate this object.
141  *    </li>
142  *   <li>{@link AuthSchemeRegistry} object used to maintain a list of
143  *    supported authentication schemes.
144  *    The {@link #createAuthSchemeRegistry()} must be implemented by concrete
145  *    super classes to instantiate this object.
146  *    </li>
147  *   <li>{@link CredentialsProvider} object used to maintain
148  *    a collection user credentials. The {@link #createCredentialsProvider()}
149  *    must be implemented by concrete super classes to instantiate
150  *    this object.
151  *    </li>
152  *   <li>{@link AuthenticationStrategy} object used to authenticate
153  *    against the target host.
154  *    The {@link #createTargetAuthenticationStrategy()} must be implemented
155  *    by concrete super classes to instantiate this object.
156  *    </li>
157  *   <li>{@link AuthenticationStrategy} object used to authenticate
158  *    against the proxy host.
159  *    The {@link #createProxyAuthenticationStrategy()} must be implemented
160  *    by concrete super classes to instantiate this object.
161  *    </li>
162  *   <li>{@link HttpRoutePlanner} object used to calculate a route
163  *    for establishing a connection to the target host. The route
164  *    may involve multiple intermediate hops.
165  *    The {@link #createHttpRoutePlanner()} must be implemented
166  *    by concrete super classes to instantiate this object.
167  *    </li>
168  *   <li>{@link RedirectStrategy} object used to determine if an HTTP
169  *    request should be redirected to a new location in response to an HTTP
170  *    response received from the target server.
171  *    </li>
172  *   <li>{@link UserTokenHandler} object used to determine if the
173  *    execution context is user identity specific.
174  *    The {@link #createUserTokenHandler()} must be implemented by
175  *    concrete super classes to instantiate this object.
176  *    </li>
177  * </ul>
178  * <p>
179  *   This class also maintains a list of protocol interceptors intended
180  *   for processing outgoing requests and incoming responses and provides
181  *   methods for managing those interceptors. New protocol interceptors can be
182  *   introduced to the protocol processor chain or removed from it if needed.
183  *   Internally protocol interceptors are stored in a simple
184  *   {@link java.util.ArrayList}. They are executed in the same natural order
185  *   as they are added to the list.
186  * <p>
187  *   AbstractHttpClient is thread safe. It is recommended that the same
188  *   instance of this class is reused for multiple request executions.
189  *   When an instance of DefaultHttpClient is no longer needed and is about
190  *   to go out of scope the connection manager associated with it must be
191  *   shut down by calling {@link ClientConnectionManager#shutdown()}!
192  *
193  * @since 4.0
194  *
195  * @deprecated (4.3) use {@link HttpClientBuilder}.
196  */
197 @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
198 @Deprecated
199 public abstract class AbstractHttpClient extends CloseableHttpClient {
200 
201     private final Log log = LogFactory.getLog(getClass());
202 
203     private HttpParams defaultParams;
204     private HttpRequestExecutor requestExec;
205     private ClientConnectionManager connManager;
206     private ConnectionReuseStrategy reuseStrategy;
207     private ConnectionKeepAliveStrategy keepAliveStrategy;
208     private CookieSpecRegistry supportedCookieSpecs;
209     private AuthSchemeRegistry supportedAuthSchemes;
210     private BasicHttpProcessor mutableProcessor;
211     private ImmutableHttpProcessor protocolProcessor;
212     private HttpRequestRetryHandler retryHandler;
213     private RedirectStrategy redirectStrategy;
214     private AuthenticationStrategy targetAuthStrategy;
215     private AuthenticationStrategy proxyAuthStrategy;
216     private CookieStore cookieStore;
217     private CredentialsProvider credsProvider;
218     private HttpRoutePlanner routePlanner;
219     private UserTokenHandler userTokenHandler;
220     private ConnectionBackoffStrategy connectionBackoffStrategy;
221     private BackoffManager backoffManager;
222 
223     /**
224      * Creates a new HTTP client.
225      *
226      * @param conman    the connection manager
227      * @param params    the parameters
228      */
229     protected AbstractHttpClient(
230             final ClientConnectionManager conman,
231             final HttpParams params) {
232         super();
233         defaultParams        = params;
234         connManager          = conman;
235     } // constructor
236 
237 
238     protected abstract HttpParams createHttpParams();
239 
240 
241     protected abstract BasicHttpProcessor createHttpProcessor();
242 
243 
244     protected HttpContext createHttpContext() {
245         final HttpContext context = new BasicHttpContext();
246         context.setAttribute(
247                 ClientContext.SCHEME_REGISTRY,
248                 getConnectionManager().getSchemeRegistry());
249         context.setAttribute(
250                 ClientContext.AUTHSCHEME_REGISTRY,
251                 getAuthSchemes());
252         context.setAttribute(
253                 ClientContext.COOKIESPEC_REGISTRY,
254                 getCookieSpecs());
255         context.setAttribute(
256                 ClientContext.COOKIE_STORE,
257                 getCookieStore());
258         context.setAttribute(
259                 ClientContext.CREDS_PROVIDER,
260                 getCredentialsProvider());
261         return context;
262     }
263 
264 
265     protected ClientConnectionManager createClientConnectionManager() {
266         final SchemeRegistry registry = SchemeRegistryFactory.createDefault();
267 
268         ClientConnectionManager connManager = null;
269         final HttpParams params = getParams();
270 
271         ClientConnectionManagerFactory factory = null;
272 
273         final String className = (String) params.getParameter(
274                 ClientPNames.CONNECTION_MANAGER_FACTORY_CLASS_NAME);
275         final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
276         if (className != null) {
277             try {
278                 final Class<?> clazz;
279                 if (contextLoader != null) {
280                     clazz = Class.forName(className, true, contextLoader);
281                 } else {
282                     clazz = Class.forName(className);
283                 }
284                 factory = (ClientConnectionManagerFactory) clazz.newInstance();
285             } catch (final ClassNotFoundException ex) {
286                 throw new IllegalStateException("Invalid class name: " + className);
287             } catch (final IllegalAccessException ex) {
288                 throw new IllegalAccessError(ex.getMessage());
289             } catch (final InstantiationException ex) {
290                 throw new InstantiationError(ex.getMessage());
291             }
292         }
293         if (factory != null) {
294             connManager = factory.newInstance(params, registry);
295         } else {
296             connManager = new BasicClientConnectionManager(registry);
297         }
298 
299         return connManager;
300     }
301 
302 
303     protected AuthSchemeRegistry createAuthSchemeRegistry() {
304         final AuthSchemeRegistry registry = new AuthSchemeRegistry();
305         registry.register(
306                 AuthPolicy.BASIC,
307                 new BasicSchemeFactory());
308         registry.register(
309                 AuthPolicy.DIGEST,
310                 new DigestSchemeFactory());
311         registry.register(
312                 AuthPolicy.NTLM,
313                 new NTLMSchemeFactory());
314         registry.register(
315                 AuthPolicy.SPNEGO,
316                 new SPNegoSchemeFactory());
317         registry.register(
318                 AuthPolicy.KERBEROS,
319                 new KerberosSchemeFactory());
320         return registry;
321     }
322 
323 
324     protected CookieSpecRegistry createCookieSpecRegistry() {
325         final CookieSpecRegistry registry = new CookieSpecRegistry();
326         registry.register(
327                 CookieSpecs.DEFAULT,
328                 new BestMatchSpecFactory());
329         registry.register(
330                 CookiePolicy.BEST_MATCH,
331                 new BestMatchSpecFactory());
332         registry.register(
333                 CookiePolicy.BROWSER_COMPATIBILITY,
334                 new BrowserCompatSpecFactory());
335         registry.register(
336                 CookiePolicy.NETSCAPE,
337                 new NetscapeDraftSpecFactory());
338         registry.register(
339                 CookiePolicy.RFC_2109,
340                 new RFC2109SpecFactory());
341         registry.register(
342                 CookiePolicy.RFC_2965,
343                 new RFC2965SpecFactory());
344         registry.register(
345                 CookiePolicy.IGNORE_COOKIES,
346                 new IgnoreSpecFactory());
347         return registry;
348     }
349 
350     protected HttpRequestExecutor createRequestExecutor() {
351         return new HttpRequestExecutor();
352     }
353 
354     protected ConnectionReuseStrategy createConnectionReuseStrategy() {
355         return new DefaultConnectionReuseStrategy();
356     }
357 
358     protected ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy() {
359         return new DefaultConnectionKeepAliveStrategy();
360     }
361 
362     protected HttpRequestRetryHandler createHttpRequestRetryHandler() {
363         return new DefaultHttpRequestRetryHandler();
364     }
365 
366     /**
367      * @deprecated (4.1) do not use
368      */
369     @Deprecated
370     protected RedirectHandler createRedirectHandler() {
371         return new DefaultRedirectHandler();
372     }
373 
374     protected AuthenticationStrategy createTargetAuthenticationStrategy() {
375         return new TargetAuthenticationStrategy();
376     }
377 
378     /**
379      * @deprecated (4.2) do not use
380      */
381     @Deprecated
382     protected AuthenticationHandler createTargetAuthenticationHandler() {
383         return new DefaultTargetAuthenticationHandler();
384     }
385 
386     protected AuthenticationStrategy createProxyAuthenticationStrategy() {
387         return new ProxyAuthenticationStrategy();
388     }
389 
390     /**
391      * @deprecated (4.2) do not use
392      */
393     @Deprecated
394     protected AuthenticationHandler createProxyAuthenticationHandler() {
395         return new DefaultProxyAuthenticationHandler();
396     }
397 
398     protected CookieStore createCookieStore() {
399         return new BasicCookieStore();
400     }
401 
402     protected CredentialsProvider createCredentialsProvider() {
403         return new BasicCredentialsProvider();
404     }
405 
406     protected HttpRoutePlanner createHttpRoutePlanner() {
407         return new DefaultHttpRoutePlanner(getConnectionManager().getSchemeRegistry());
408     }
409 
410     protected UserTokenHandler createUserTokenHandler() {
411         return new DefaultUserTokenHandler();
412     }
413 
414     // non-javadoc, see interface HttpClient
415     @Override
416     public synchronized final HttpParams getParams() {
417         if (defaultParams == null) {
418             defaultParams = createHttpParams();
419         }
420         return defaultParams;
421     }
422 
423     /**
424      * Replaces the parameters.
425      * The implementation here does not update parameters of dependent objects.
426      *
427      * @param params    the new default parameters
428      */
429     public synchronized void setParams(final HttpParams params) {
430         defaultParams = params;
431     }
432 
433 
434     @Override
435     public synchronized final ClientConnectionManager getConnectionManager() {
436         if (connManager == null) {
437             connManager = createClientConnectionManager();
438         }
439         return connManager;
440     }
441 
442 
443     public synchronized final HttpRequestExecutor getRequestExecutor() {
444         if (requestExec == null) {
445             requestExec = createRequestExecutor();
446         }
447         return requestExec;
448     }
449 
450 
451     public synchronized final AuthSchemeRegistry getAuthSchemes() {
452         if (supportedAuthSchemes == null) {
453             supportedAuthSchemes = createAuthSchemeRegistry();
454         }
455         return supportedAuthSchemes;
456     }
457 
458     public synchronized void setAuthSchemes(final AuthSchemeRegistry registry) {
459         supportedAuthSchemes = registry;
460     }
461 
462     public synchronized final ConnectionBackoffStrategy getConnectionBackoffStrategy() {
463         return connectionBackoffStrategy;
464     }
465 
466     public synchronized void setConnectionBackoffStrategy(final ConnectionBackoffStrategy strategy) {
467         connectionBackoffStrategy = strategy;
468     }
469 
470     public synchronized final CookieSpecRegistry getCookieSpecs() {
471         if (supportedCookieSpecs == null) {
472             supportedCookieSpecs = createCookieSpecRegistry();
473         }
474         return supportedCookieSpecs;
475     }
476 
477     public synchronized final BackoffManager getBackoffManager() {
478         return backoffManager;
479     }
480 
481     public synchronized void setBackoffManager(final BackoffManager manager) {
482         backoffManager = manager;
483     }
484 
485     public synchronized void setCookieSpecs(final CookieSpecRegistry registry) {
486         supportedCookieSpecs = registry;
487     }
488 
489     public synchronized final ConnectionReuseStrategy getConnectionReuseStrategy() {
490         if (reuseStrategy == null) {
491             reuseStrategy = createConnectionReuseStrategy();
492         }
493         return reuseStrategy;
494     }
495 
496 
497     public synchronized void setReuseStrategy(final ConnectionReuseStrategy strategy) {
498         this.reuseStrategy = strategy;
499     }
500 
501 
502     public synchronized final ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
503         if (keepAliveStrategy == null) {
504             keepAliveStrategy = createConnectionKeepAliveStrategy();
505         }
506         return keepAliveStrategy;
507     }
508 
509 
510     public synchronized void setKeepAliveStrategy(final ConnectionKeepAliveStrategy strategy) {
511         this.keepAliveStrategy = strategy;
512     }
513 
514 
515     public synchronized final HttpRequestRetryHandler getHttpRequestRetryHandler() {
516         if (retryHandler == null) {
517             retryHandler = createHttpRequestRetryHandler();
518         }
519         return retryHandler;
520     }
521 
522     public synchronized void setHttpRequestRetryHandler(final HttpRequestRetryHandler handler) {
523         this.retryHandler = handler;
524     }
525 
526     /**
527      * @deprecated (4.1) do not use
528      */
529     @Deprecated
530     public synchronized final RedirectHandler getRedirectHandler() {
531         return createRedirectHandler();
532     }
533 
534     /**
535      * @deprecated (4.1) do not use
536      */
537     @Deprecated
538     public synchronized void setRedirectHandler(final RedirectHandler handler) {
539         this.redirectStrategy = new DefaultRedirectStrategyAdaptor(handler);
540     }
541 
542     /**
543      * @since 4.1
544      */
545     public synchronized final RedirectStrategy getRedirectStrategy() {
546         if (redirectStrategy == null) {
547             redirectStrategy = new DefaultRedirectStrategy();
548         }
549         return redirectStrategy;
550     }
551 
552     /**
553      * @since 4.1
554      */
555     public synchronized void setRedirectStrategy(final RedirectStrategy strategy) {
556         this.redirectStrategy = strategy;
557     }
558 
559     /**
560      * @deprecated (4.2) do not use
561      */
562     @Deprecated
563     public synchronized final AuthenticationHandler getTargetAuthenticationHandler() {
564         return createTargetAuthenticationHandler();
565     }
566 
567     /**
568      * @deprecated (4.2) do not use
569      */
570     @Deprecated
571     public synchronized void setTargetAuthenticationHandler(final AuthenticationHandler handler) {
572         this.targetAuthStrategy = new AuthenticationStrategyAdaptor(handler);
573     }
574 
575     /**
576      * @since 4.2
577      */
578     public synchronized final AuthenticationStrategy getTargetAuthenticationStrategy() {
579         if (targetAuthStrategy == null) {
580             targetAuthStrategy = createTargetAuthenticationStrategy();
581         }
582         return targetAuthStrategy;
583     }
584 
585     /**
586      * @since 4.2
587      */
588     public synchronized void setTargetAuthenticationStrategy(final AuthenticationStrategy strategy) {
589         this.targetAuthStrategy = strategy;
590     }
591 
592     /**
593      * @deprecated (4.2) do not use
594      */
595     @Deprecated
596     public synchronized final AuthenticationHandler getProxyAuthenticationHandler() {
597         return createProxyAuthenticationHandler();
598     }
599 
600     /**
601      * @deprecated (4.2) do not use
602      */
603     @Deprecated
604     public synchronized void setProxyAuthenticationHandler(final AuthenticationHandler handler) {
605         this.proxyAuthStrategy = new AuthenticationStrategyAdaptor(handler);
606     }
607 
608     /**
609      * @since 4.2
610      */
611     public synchronized final AuthenticationStrategy getProxyAuthenticationStrategy() {
612         if (proxyAuthStrategy == null) {
613             proxyAuthStrategy = createProxyAuthenticationStrategy();
614         }
615         return proxyAuthStrategy;
616     }
617 
618     /**
619      * @since 4.2
620      */
621     public synchronized void setProxyAuthenticationStrategy(final AuthenticationStrategy strategy) {
622         this.proxyAuthStrategy = strategy;
623     }
624 
625     public synchronized final CookieStore getCookieStore() {
626         if (cookieStore == null) {
627             cookieStore = createCookieStore();
628         }
629         return cookieStore;
630     }
631 
632     public synchronized void setCookieStore(final CookieStore cookieStore) {
633         this.cookieStore = cookieStore;
634     }
635 
636     public synchronized final CredentialsProvider getCredentialsProvider() {
637         if (credsProvider == null) {
638             credsProvider = createCredentialsProvider();
639         }
640         return credsProvider;
641     }
642 
643     public synchronized void setCredentialsProvider(final CredentialsProvider credsProvider) {
644         this.credsProvider = credsProvider;
645     }
646 
647     public synchronized final HttpRoutePlanner getRoutePlanner() {
648         if (this.routePlanner == null) {
649             this.routePlanner = createHttpRoutePlanner();
650         }
651         return this.routePlanner;
652     }
653 
654     public synchronized void setRoutePlanner(final HttpRoutePlanner routePlanner) {
655         this.routePlanner = routePlanner;
656     }
657 
658     public synchronized final UserTokenHandler getUserTokenHandler() {
659         if (this.userTokenHandler == null) {
660             this.userTokenHandler = createUserTokenHandler();
661         }
662         return this.userTokenHandler;
663     }
664 
665     public synchronized void setUserTokenHandler(final UserTokenHandler handler) {
666         this.userTokenHandler = handler;
667     }
668 
669     protected synchronized final BasicHttpProcessor getHttpProcessor() {
670         if (mutableProcessor == null) {
671             mutableProcessor = createHttpProcessor();
672         }
673         return mutableProcessor;
674     }
675 
676     private synchronized HttpProcessor getProtocolProcessor() {
677         if (protocolProcessor == null) {
678             // Get mutable HTTP processor
679             final BasicHttpProcessor proc = getHttpProcessor();
680             // and create an immutable copy of it
681             final int reqc = proc.getRequestInterceptorCount();
682             final HttpRequestInterceptor[] reqinterceptors = new HttpRequestInterceptor[reqc];
683             for (int i = 0; i < reqc; i++) {
684                 reqinterceptors[i] = proc.getRequestInterceptor(i);
685             }
686             final int resc = proc.getResponseInterceptorCount();
687             final HttpResponseInterceptor[] resinterceptors = new HttpResponseInterceptor[resc];
688             for (int i = 0; i < resc; i++) {
689                 resinterceptors[i] = proc.getResponseInterceptor(i);
690             }
691             protocolProcessor = new ImmutableHttpProcessor(reqinterceptors, resinterceptors);
692         }
693         return protocolProcessor;
694     }
695 
696     public synchronized int getResponseInterceptorCount() {
697         return getHttpProcessor().getResponseInterceptorCount();
698     }
699 
700     public synchronized HttpResponseInterceptor getResponseInterceptor(final int index) {
701         return getHttpProcessor().getResponseInterceptor(index);
702     }
703 
704     public synchronized HttpRequestInterceptor getRequestInterceptor(final int index) {
705         return getHttpProcessor().getRequestInterceptor(index);
706     }
707 
708     public synchronized int getRequestInterceptorCount() {
709         return getHttpProcessor().getRequestInterceptorCount();
710     }
711 
712     public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp) {
713         getHttpProcessor().addInterceptor(itcp);
714         protocolProcessor = null;
715     }
716 
717     public synchronized void addResponseInterceptor(final HttpResponseInterceptor itcp, final int index) {
718         getHttpProcessor().addInterceptor(itcp, index);
719         protocolProcessor = null;
720     }
721 
722     public synchronized void clearResponseInterceptors() {
723         getHttpProcessor().clearResponseInterceptors();
724         protocolProcessor = null;
725     }
726 
727     public synchronized void removeResponseInterceptorByClass(final Class<? extends HttpResponseInterceptor> clazz) {
728         getHttpProcessor().removeResponseInterceptorByClass(clazz);
729         protocolProcessor = null;
730     }
731 
732     public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp) {
733         getHttpProcessor().addInterceptor(itcp);
734         protocolProcessor = null;
735     }
736 
737     public synchronized void addRequestInterceptor(final HttpRequestInterceptor itcp, final int index) {
738         getHttpProcessor().addInterceptor(itcp, index);
739         protocolProcessor = null;
740     }
741 
742     public synchronized void clearRequestInterceptors() {
743         getHttpProcessor().clearRequestInterceptors();
744         protocolProcessor = null;
745     }
746 
747     public synchronized void removeRequestInterceptorByClass(final Class<? extends HttpRequestInterceptor> clazz) {
748         getHttpProcessor().removeRequestInterceptorByClass(clazz);
749         protocolProcessor = null;
750     }
751 
752     @Override
753     protected final CloseableHttpResponse doExecute(final HttpHost target, final HttpRequest request,
754                                       final HttpContext context)
755         throws IOException, ClientProtocolException {
756 
757         Args.notNull(request, "HTTP request");
758         // a null target may be acceptable, this depends on the route planner
759         // a null context is acceptable, default context created below
760 
761         HttpContext execContext = null;
762         RequestDirector director = null;
763         HttpRoutePlanner routePlanner = null;
764         ConnectionBackoffStrategy connectionBackoffStrategy = null;
765         BackoffManager backoffManager = null;
766 
767         // Initialize the request execution context making copies of
768         // all shared objects that are potentially threading unsafe.
769         synchronized (this) {
770 
771             final HttpContext defaultContext = createHttpContext();
772             if (context == null) {
773                 execContext = defaultContext;
774             } else {
775                 execContext = new DefaultedHttpContext(context, defaultContext);
776             }
777             final HttpParams params = determineParams(request);
778             final RequestConfig config = HttpClientParamConfig.getRequestConfig(params);
779             execContext.setAttribute(ClientContext.REQUEST_CONFIG, config);
780 
781             // Create a director for this request
782             director = createClientRequestDirector(
783                     getRequestExecutor(),
784                     getConnectionManager(),
785                     getConnectionReuseStrategy(),
786                     getConnectionKeepAliveStrategy(),
787                     getRoutePlanner(),
788                     getProtocolProcessor(),
789                     getHttpRequestRetryHandler(),
790                     getRedirectStrategy(),
791                     getTargetAuthenticationStrategy(),
792                     getProxyAuthenticationStrategy(),
793                     getUserTokenHandler(),
794                     params);
795             routePlanner = getRoutePlanner();
796             connectionBackoffStrategy = getConnectionBackoffStrategy();
797             backoffManager = getBackoffManager();
798         }
799 
800         try {
801             if (connectionBackoffStrategy != null && backoffManager != null) {
802                 final HttpHost targetForRoute = (target != null) ? target
803                         : (HttpHost) determineParams(request).getParameter(
804                                 ClientPNames.DEFAULT_HOST);
805                 final HttpRoute route = routePlanner.determineRoute(targetForRoute, request, execContext);
806 
807                 final CloseableHttpResponse out;
808                 try {
809                     out = CloseableHttpResponseProxy.newProxy(
810                             director.execute(target, request, execContext));
811                 } catch (final RuntimeException re) {
812                     if (connectionBackoffStrategy.shouldBackoff(re)) {
813                         backoffManager.backOff(route);
814                     }
815                     throw re;
816                 } catch (final Exception e) {
817                     if (connectionBackoffStrategy.shouldBackoff(e)) {
818                         backoffManager.backOff(route);
819                     }
820                     if (e instanceof HttpException) {
821                         throw (HttpException)e;
822                     }
823                     if (e instanceof IOException) {
824                         throw (IOException)e;
825                     }
826                     throw new UndeclaredThrowableException(e);
827                 }
828                 if (connectionBackoffStrategy.shouldBackoff(out)) {
829                     backoffManager.backOff(route);
830                 } else {
831                     backoffManager.probe(route);
832                 }
833                 return out;
834             } else {
835                 return CloseableHttpResponseProxy.newProxy(
836                         director.execute(target, request, execContext));
837             }
838         } catch(final HttpException httpException) {
839             throw new ClientProtocolException(httpException);
840         }
841     }
842 
843     /**
844      * @deprecated (4.1) do not use
845      */
846     @Deprecated
847     protected RequestDirector createClientRequestDirector(
848             final HttpRequestExecutor requestExec,
849             final ClientConnectionManager conman,
850             final ConnectionReuseStrategy reustrat,
851             final ConnectionKeepAliveStrategy kastrat,
852             final HttpRoutePlanner rouplan,
853             final HttpProcessor httpProcessor,
854             final HttpRequestRetryHandler retryHandler,
855             final RedirectHandler redirectHandler,
856             final AuthenticationHandler targetAuthHandler,
857             final AuthenticationHandler proxyAuthHandler,
858             final UserTokenHandler userTokenHandler,
859             final HttpParams params) {
860         return new DefaultRequestDirector(
861                 requestExec,
862                 conman,
863                 reustrat,
864                 kastrat,
865                 rouplan,
866                 httpProcessor,
867                 retryHandler,
868                 redirectHandler,
869                 targetAuthHandler,
870                 proxyAuthHandler,
871                 userTokenHandler,
872                 params);
873     }
874 
875     /**
876      * @deprecated (4.2) do not use
877      */
878     @Deprecated
879     protected RequestDirector createClientRequestDirector(
880             final HttpRequestExecutor requestExec,
881             final ClientConnectionManager conman,
882             final ConnectionReuseStrategy reustrat,
883             final ConnectionKeepAliveStrategy kastrat,
884             final HttpRoutePlanner rouplan,
885             final HttpProcessor httpProcessor,
886             final HttpRequestRetryHandler retryHandler,
887             final RedirectStrategy redirectStrategy,
888             final AuthenticationHandler targetAuthHandler,
889             final AuthenticationHandler proxyAuthHandler,
890             final UserTokenHandler userTokenHandler,
891             final HttpParams params) {
892         return new DefaultRequestDirector(
893                 log,
894                 requestExec,
895                 conman,
896                 reustrat,
897                 kastrat,
898                 rouplan,
899                 httpProcessor,
900                 retryHandler,
901                 redirectStrategy,
902                 targetAuthHandler,
903                 proxyAuthHandler,
904                 userTokenHandler,
905                 params);
906     }
907 
908 
909     /**
910      * @since 4.2
911      */
912     protected RequestDirector createClientRequestDirector(
913             final HttpRequestExecutor requestExec,
914             final ClientConnectionManager conman,
915             final ConnectionReuseStrategy reustrat,
916             final ConnectionKeepAliveStrategy kastrat,
917             final HttpRoutePlanner rouplan,
918             final HttpProcessor httpProcessor,
919             final HttpRequestRetryHandler retryHandler,
920             final RedirectStrategy redirectStrategy,
921             final AuthenticationStrategy targetAuthStrategy,
922             final AuthenticationStrategy proxyAuthStrategy,
923             final UserTokenHandler userTokenHandler,
924             final HttpParams params) {
925         return new DefaultRequestDirector(
926                 log,
927                 requestExec,
928                 conman,
929                 reustrat,
930                 kastrat,
931                 rouplan,
932                 httpProcessor,
933                 retryHandler,
934                 redirectStrategy,
935                 targetAuthStrategy,
936                 proxyAuthStrategy,
937                 userTokenHandler,
938                 params);
939     }
940 
941     /**
942      * Obtains parameters for executing a request.
943      * The default implementation in this class creates a new
944      * {@link ClientParamsStack} from the request parameters
945      * and the client parameters.
946      * <p>
947      * This method is called by the default implementation of
948      * {@link #execute(HttpHost,HttpRequest,HttpContext)}
949      * to obtain the parameters for the
950      * {@link DefaultRequestDirector}.
951      * </p>
952      *
953      * @param req    the request that will be executed
954      *
955      * @return  the parameters to use
956      */
957     protected HttpParams determineParams(final HttpRequest req) {
958         return new ClientParamsStack
959             (null, getParams(), req.getParams(), null);
960     }
961 
962 
963     @Override
964     public void close() {
965         getConnectionManager().shutdown();
966     }
967 
968 }