1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package org.apache.commons.httpclient;
31
32 import java.io.IOException;
33 import java.util.HashSet;
34 import java.util.Map;
35 import java.util.Set;
36
37 import org.apache.commons.httpclient.auth.AuthChallengeException;
38 import org.apache.commons.httpclient.auth.AuthChallengeParser;
39 import org.apache.commons.httpclient.auth.AuthChallengeProcessor;
40 import org.apache.commons.httpclient.auth.AuthScheme;
41 import org.apache.commons.httpclient.auth.AuthState;
42 import org.apache.commons.httpclient.auth.AuthenticationException;
43 import org.apache.commons.httpclient.auth.CredentialsProvider;
44 import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
45 import org.apache.commons.httpclient.auth.HttpAuthRealm;
46 import org.apache.commons.httpclient.auth.MalformedChallengeException;
47 import org.apache.commons.httpclient.params.HttpClientParams;
48 import org.apache.commons.httpclient.params.HttpConnectionParams;
49 import org.apache.commons.httpclient.params.HttpMethodParams;
50 import org.apache.commons.httpclient.params.HttpParams;
51 import org.apache.commons.logging.Log;
52 import org.apache.commons.logging.LogFactory;
53
54 /***
55 * Handles the process of executing a method including authentication, redirection and retries.
56 *
57 * @since 3.0
58 */
59 class HttpMethodDirector {
60
61 /*** The www authenticate challange header. */
62 public static final String WWW_AUTH_CHALLENGE = "WWW-Authenticate";
63
64 /*** The www authenticate response header. */
65 public static final String WWW_AUTH_RESP = "Authorization";
66
67 /*** The proxy authenticate challange header. */
68 public static final String PROXY_AUTH_CHALLENGE = "Proxy-Authenticate";
69
70 /*** The proxy authenticate response header. */
71 public static final String PROXY_AUTH_RESP = "Proxy-Authorization";
72
73 private static final Log LOG = LogFactory.getLog(HttpMethodDirector.class);
74
75 private ConnectMethod connectMethod;
76
77 private HttpState state;
78
79 private HostConfiguration hostConfiguration;
80
81 private HttpConnectionManager connectionManager;
82
83 private HttpClientParams params;
84
85 private HttpConnection conn;
86
87 /*** A flag to indicate if the connection should be released after the method is executed. */
88 private boolean releaseConnection = false;
89
90 private static final int AUTH_UNINITIATED = 0;
91 private static final int AUTH_PREEMPTIVE = 1;
92 private static final int AUTH_WWW_REQUIRED = 2;
93 private static final int AUTH_PROXY_REQUIRED = 3;
94 private static final int AUTH_NOT_REQUIRED = Integer.MAX_VALUE;
95
96 /*** Actual state of authentication process */
97 private int authProcess = AUTH_UNINITIATED;
98
99 /*** Authentication processor */
100 private AuthChallengeProcessor authProcessor = null;
101
102 private Set redirectLocations = null;
103
104 public HttpMethodDirector(
105 final HttpConnectionManager connectionManager,
106 final HostConfiguration hostConfiguration,
107 final HttpClientParams params,
108 final HttpState state
109 ) {
110 super();
111 this.connectionManager = connectionManager;
112 this.hostConfiguration = hostConfiguration;
113 this.params = params;
114 this.state = state;
115 this.authProcessor = new AuthChallengeProcessor(this.params);
116 }
117
118
119 /***
120 * Executes the method associated with this method director.
121 *
122 * @throws IOException
123 * @throws HttpException
124 */
125 public void executeMethod(final HttpMethod method) throws IOException, HttpException {
126 if (method == null) {
127 throw new IllegalArgumentException("Method may not be null");
128 }
129
130
131 this.hostConfiguration.getParams().setDefaults(this.params);
132 method.getParams().setDefaults(this.hostConfiguration.getParams());
133 try {
134 int maxRedirects = this.params.getIntParameter(HttpClientParams.MAX_REDIRECTS, 100);
135
136 for (int redirectCount = 0;;) {
137
138
139 if (this.conn != null && !hostConfiguration.hostEquals(this.conn)) {
140 this.conn.setLocked(false);
141 this.conn.releaseConnection();
142 this.conn = null;
143 }
144
145
146 if (this.conn == null) {
147 this.conn = connectionManager.getConnectionWithTimeout(
148 hostConfiguration,
149 this.params.getConnectionManagerTimeout()
150 );
151 this.conn.setLocked(true);
152 if (this.params.isAuthenticationPreemptive()
153 || this.state.isAuthenticationPreemptive())
154 {
155 LOG.debug("Preemptively sending default basic credentials");
156 this.authProcess = AUTH_PREEMPTIVE;
157 method.getHostAuthState().setPreemptive();
158 if (this.conn.isProxied()) {
159 method.getProxyAuthState().setPreemptive();
160 }
161 }
162 }
163 authenticate(method);
164 executeWithRetry(method);
165 if (this.connectMethod != null) {
166 fakeResponse(method);
167 break;
168 }
169
170 boolean retry = false;
171 if (isRedirectNeeded(method)) {
172 if (processRedirectResponse(method)) {
173 retry = true;
174 ++redirectCount;
175 if (redirectCount >= maxRedirects) {
176 LOG.error("Narrowly avoided an infinite loop in execute");
177 throw new RedirectException("Maximum redirects ("
178 + maxRedirects + ") exceeded");
179 }
180 if (LOG.isDebugEnabled()) {
181 LOG.debug("Execute redirect " + redirectCount + " of " + maxRedirects);
182 }
183 }
184 }
185 if (isAuthenticationNeeded(method)) {
186 if (processAuthenticationResponse(method)) {
187 retry = true;
188 }
189 } else {
190 this.authProcess = AUTH_NOT_REQUIRED;
191 }
192 if (!retry) {
193 break;
194 }
195
196
197
198 if (method.getResponseBodyAsStream() != null) {
199 method.getResponseBodyAsStream().close();
200 }
201
202 }
203 } finally {
204 if (this.conn != null) {
205 this.conn.setLocked(false);
206 }
207
208
209
210
211
212 if (
213 (releaseConnection || method.getResponseBodyAsStream() == null)
214 && this.conn != null
215 ) {
216 this.conn.releaseConnection();
217 }
218 }
219
220 }
221
222
223 private void authenticate(final HttpMethod method) {
224 try {
225 authenticateProxy(method);
226 authenticateHost(method);
227 } catch (AuthenticationException e) {
228 LOG.error(e.getMessage(), e);
229 }
230 }
231
232
233 private boolean cleanAuthHeaders(final HttpMethod method, final String name) {
234 Header[] authheaders = method.getRequestHeaders(name);
235 boolean clean = true;
236 for (int i = 0; i < authheaders.length; i++) {
237 Header authheader = authheaders[i];
238 if (authheader.isAutogenerated()) {
239 method.removeRequestHeader(authheader);
240 } else {
241 clean = false;
242 }
243 }
244 return clean;
245 }
246
247
248 private void authenticateHost(final HttpMethod method) throws AuthenticationException {
249
250 if (!cleanAuthHeaders(method, WWW_AUTH_RESP)) {
251
252 return;
253 }
254 AuthScheme authscheme = method.getHostAuthState().getAuthScheme();
255 if (authscheme == null) {
256 return;
257 }
258 if ((this.authProcess == AUTH_WWW_REQUIRED) || (!authscheme.isConnectionBased())) {
259 String host = conn.getVirtualHost();
260 if (host == null) {
261 host = conn.getHost();
262 }
263 int port = conn.getPort();
264 HttpAuthRealm realm = new HttpAuthRealm(
265 host, port,
266 authscheme.getRealm(),
267 authscheme.getSchemeName());
268 if (LOG.isDebugEnabled()) {
269 LOG.debug("Authenticating with " + realm);
270 }
271 Credentials credentials = this.state.getCredentials(realm);
272 if (credentials != null) {
273 String authstring = authscheme.authenticate(credentials, method);
274 if (authstring != null) {
275 method.addRequestHeader(new Header(WWW_AUTH_RESP, authstring, true));
276 }
277 } else {
278 LOG.warn("Required credentials not available");
279 }
280 }
281 }
282
283
284 private void authenticateProxy(final HttpMethod method) throws AuthenticationException {
285
286 if (!cleanAuthHeaders(method, PROXY_AUTH_RESP)) {
287
288 return;
289 }
290 AuthScheme authscheme = method.getProxyAuthState().getAuthScheme();
291 if (authscheme == null) {
292 return;
293 }
294 if ((this.authProcess == AUTH_PROXY_REQUIRED) || (!authscheme.isConnectionBased())) {
295 HttpAuthRealm realm = new HttpAuthRealm(
296 conn.getProxyHost(), conn.getProxyPort(),
297 authscheme.getRealm(),
298 authscheme.getSchemeName());
299 if (LOG.isDebugEnabled()) {
300 LOG.debug("Authenticating with " + realm);
301 }
302 Credentials credentials = this.state.getProxyCredentials(realm);
303 if (credentials != null) {
304 String authstring = authscheme.authenticate(credentials, method);
305 if (authstring != null) {
306 method.addRequestHeader(new Header(PROXY_AUTH_RESP, authstring, true));
307 }
308 } else {
309 LOG.warn("Required credentials not available");
310 }
311 }
312 }
313
314
315 /***
316 * Executes a method with the current hostConfiguration.
317 *
318 * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
319 * can be recovered from.
320 * @throws HttpException if a protocol exception occurs. Usually protocol exceptions
321 * cannot be recovered from.
322 */
323 private void executeWithRetry(final HttpMethod method)
324 throws IOException, HttpException {
325
326 /*** How many times did this transparently handle a recoverable exception? */
327 int recoverableExceptionCount = 0;
328 int execCount = 0;
329
330 boolean requestSent = false;
331
332
333
334 try {
335 while (true) {
336 execCount++;
337 requestSent = false;
338
339 if (LOG.isTraceEnabled()) {
340 LOG.trace("Attempt number " + execCount + " to process request");
341 }
342 if (!this.conn.isOpen()) {
343
344
345 this.conn.open();
346 if (this.conn.isProxied() && this.conn.isSecure()
347 && !(method instanceof ConnectMethod)) {
348
349 if (!executeConnect()) {
350
351 return;
352 }
353 }
354 }
355 int timeout = 0;
356
357 Object param = this.params.getParameter(HttpMethodParams.SO_TIMEOUT);
358 if (param == null) {
359
360 param = this.conn.getParams().getParameter(HttpConnectionParams.SO_TIMEOUT);
361 }
362 if (param != null) {
363 timeout = ((Integer)param).intValue();
364 }
365 this.conn.setSocketTimeout(timeout);
366
367 try {
368 method.execute(state, this.conn);
369 break;
370 } catch (HttpRecoverableException httpre) {
371 LOG.debug("Closing the connection.");
372 this.conn.close();
373 LOG.info("Recoverable exception caught when processing request");
374
375 recoverableExceptionCount++;
376
377
378 MethodRetryHandler handler = method.getMethodRetryHandler();
379 if (handler == null) {
380 handler = new DefaultMethodRetryHandler();
381 }
382 if (!handler.retryMethod(
383 method,
384 this.conn,
385 httpre,
386 execCount,
387 requestSent)
388 ) {
389 LOG.warn(
390 "Recoverable exception caught but MethodRetryHandler.retryMethod() "
391 + "returned false, rethrowing exception"
392 );
393 throw httpre;
394 }
395 }
396 }
397 } catch (IOException e) {
398 if (this.conn.isOpen()) {
399 LOG.debug("Closing the connection.");
400 this.conn.close();
401 }
402 releaseConnection = true;
403 throw e;
404 } catch (RuntimeException e) {
405 if (this.conn.isOpen()) {
406 LOG.debug("Closing the connection.");
407 this.conn.close();
408 }
409 releaseConnection = true;
410 throw e;
411 }
412 }
413
414 /***
415 * Executes a ConnectMethod to establish a tunneled connection.
416 *
417 * @return <code>true</code> if the connect was successful
418 *
419 * @throws IOException
420 * @throws HttpException
421 */
422 private boolean executeConnect()
423 throws IOException, HttpException {
424
425 this.connectMethod = new ConnectMethod();
426 this.connectMethod.getParams().setDefaults(this.params);
427
428 int code;
429 for (;;) {
430 try {
431 authenticateProxy(this.connectMethod);
432 } catch (AuthenticationException e) {
433 LOG.error(e.getMessage(), e);
434 }
435 executeWithRetry(this.connectMethod);
436 code = this.connectMethod.getStatusCode();
437 boolean retry = false;
438 if (code == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) {
439 if (processAuthenticationResponse(this.connectMethod)) {
440 retry = true;
441 }
442 } else {
443 this.authProcess = AUTH_NOT_REQUIRED;
444 }
445 if (!retry) {
446 break;
447 }
448 if (this.connectMethod.getResponseBodyAsStream() != null) {
449 this.connectMethod.getResponseBodyAsStream().close();
450 }
451 }
452 if ((code >= 200) && (code < 300)) {
453 this.conn.tunnelCreated();
454
455 this.connectMethod = null;
456 return true;
457 } else {
458 return false;
459 }
460 }
461
462 /***
463 * Fake response
464 * @param method
465 * @return
466 */
467
468 private void fakeResponse(final HttpMethod method)
469 throws IOException, HttpException {
470
471
472
473
474
475
476
477
478
479
480 LOG.debug("CONNECT failed, fake the response for the original method");
481
482
483
484
485
486
487
488 if (method instanceof HttpMethodBase) {
489 ((HttpMethodBase) method).fakeResponse(
490 this.connectMethod.getStatusLine(),
491 this.connectMethod.getResponseHeaderGroup(),
492 this.connectMethod.getResponseBodyAsStream()
493 );
494 method.getProxyAuthState().setAuthScheme(
495 this.connectMethod.getProxyAuthState().getAuthScheme());
496 this.connectMethod = null;
497 } else {
498 releaseConnection = true;
499 LOG.warn(
500 "Unable to fake response on method as it is not derived from HttpMethodBase.");
501 }
502 }
503
504 /***
505 * Process the redirect response.
506 *
507 * @return <code>true</code> if the redirect was successful
508 */
509 private boolean processRedirectResponse(final HttpMethod method)
510 throws RedirectException
511 {
512
513 Header locationHeader = method.getResponseHeader("location");
514 if (locationHeader == null) {
515
516 LOG.error("Received redirect response " + method.getStatusCode()
517 + " but no location header");
518 return false;
519 }
520 String location = locationHeader.getValue();
521 if (LOG.isDebugEnabled()) {
522 LOG.debug("Redirect requested to location '" + location + "'");
523 }
524
525
526
527 URI redirectUri = null;
528 URI currentUri = null;
529
530 try {
531 currentUri = new URI(
532 this.conn.getProtocol().getScheme(),
533 null,
534 this.conn.getHost(),
535 this.conn.getPort(),
536 method.getPath()
537 );
538 redirectUri = new URI(location, true);
539 if (redirectUri.isRelativeURI()) {
540 if (this.params.isParameterTrue(HttpClientParams.REJECT_RELATIVE_REDIRECT)) {
541 LOG.warn("Relative redirect location '" + location + "' not allowed");
542 return false;
543 } else {
544
545 LOG.debug("Redirect URI is not absolute - parsing as relative");
546 redirectUri = new URI(currentUri, redirectUri);
547 }
548 }
549 method.setURI(redirectUri);
550 hostConfiguration.setHost(redirectUri);
551 } catch (URIException e) {
552 LOG.warn("Redirected location '" + location + "' is malformed");
553 return false;
554 }
555
556 if (this.params.isParameterFalse(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS)) {
557 if (this.redirectLocations == null) {
558 this.redirectLocations = new HashSet();
559 }
560 this.redirectLocations.add(currentUri);
561 if (this.redirectLocations.contains(redirectUri)) {
562 throw new RedirectException("Circular redirect to '" +
563 redirectUri + "'");
564 }
565 }
566
567 if (LOG.isDebugEnabled()) {
568 LOG.debug("Redirecting from '" + currentUri.getEscapedURI()
569 + "' to '" + redirectUri.getEscapedURI());
570 }
571
572
573 method.getHostAuthState().invalidate();
574 return true;
575 }
576
577 /***
578 * Processes a response that requires authentication
579 *
580 * @param method the current {@link HttpMethod HTTP method}
581 *
582 * @return <tt>true</tt> if the authentication challenge can be responsed to,
583 * (that is, at least one of the requested authentication scheme is supported,
584 * and matching credentials have been found), <tt>false</tt> otherwise.
585 */
586 private boolean processAuthenticationResponse(final HttpMethod method) {
587 LOG.trace("enter HttpMethodBase.processAuthenticationResponse("
588 + "HttpState, HttpConnection)");
589
590 try {
591 switch (method.getStatusCode()) {
592 case HttpStatus.SC_UNAUTHORIZED:
593 return processWWWAuthChallenge(method);
594 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
595 return processProxyAuthChallenge(method);
596 default:
597 return false;
598 }
599 } catch (Exception e) {
600 if (LOG.isErrorEnabled()) {
601 LOG.error(e.getMessage(), e);
602 }
603 return false;
604 }
605 }
606
607 private boolean processWWWAuthChallenge(final HttpMethod method)
608 throws MalformedChallengeException, AuthenticationException
609 {
610 AuthState authstate = method.getHostAuthState();
611 if (authstate.isPreemptive()) {
612 authstate.invalidate();
613 }
614 Map challenges = AuthChallengeParser.parseChallenges(
615 method.getResponseHeaders(WWW_AUTH_CHALLENGE));
616 if (challenges.isEmpty()) {
617 return false;
618 }
619 AuthScheme authscheme = null;
620 try {
621 authscheme = this.authProcessor.processChallenge(authstate, challenges);
622 } catch (AuthChallengeException e) {
623 if (LOG.isWarnEnabled()) {
624 LOG.warn(e.getMessage());
625 }
626 }
627 if (authscheme == null) {
628 return false;
629 }
630 String host = conn.getVirtualHost();
631 if (host == null) {
632 host = conn.getHost();
633 }
634 int port = conn.getPort();
635 HttpAuthRealm realm = new HttpAuthRealm(
636 host, port,
637 authscheme.getRealm(),
638 authscheme.getSchemeName());
639
640 if ((this.authProcess == AUTH_WWW_REQUIRED) && (authscheme.isComplete())) {
641
642 Credentials credentials = promptForCredentials(
643 authscheme, method.getParams(), realm);
644 if (credentials == null) {
645 if (LOG.isInfoEnabled()) {
646 LOG.info("Failure authenticating with " + realm);
647 }
648 return false;
649 } else {
650 return true;
651 }
652 } else {
653 this.authProcess = AUTH_WWW_REQUIRED;
654
655 Credentials credentials = this.state.getCredentials(realm);
656 if (credentials == null) {
657 credentials = promptForCredentials(
658 authscheme, method.getParams(), realm);
659 }
660 if (credentials == null) {
661 if (LOG.isInfoEnabled()) {
662 LOG.info("No credentials available for the " + realm);
663 }
664 return false;
665 } else {
666 return true;
667 }
668 }
669 }
670
671 private boolean processProxyAuthChallenge(final HttpMethod method)
672 throws MalformedChallengeException, AuthenticationException
673 {
674 AuthState authstate = method.getProxyAuthState();
675 if (authstate.isPreemptive()) {
676 authstate.invalidate();
677 }
678 Map proxyChallenges = AuthChallengeParser.parseChallenges(
679 method.getResponseHeaders(PROXY_AUTH_CHALLENGE));
680 if (proxyChallenges.isEmpty()) {
681 return false;
682 }
683 AuthScheme authscheme = null;
684 try {
685 authscheme = this.authProcessor.processChallenge(authstate, proxyChallenges);
686 } catch (AuthChallengeException e) {
687 if (LOG.isWarnEnabled()) {
688 LOG.warn(e.getMessage());
689 }
690 }
691 if (authscheme == null) {
692 return false;
693 }
694 HttpAuthRealm realm = new HttpAuthRealm(
695 conn.getProxyHost(), conn.getProxyPort(),
696 authscheme.getRealm(),
697 authscheme.getSchemeName());
698
699 if ((this.authProcess == AUTH_PROXY_REQUIRED) && (authscheme.isComplete())) {
700
701 Credentials credentials = promptForProxyCredentials(
702 authscheme, method.getParams(), realm);
703 if (credentials == null) {
704 if (LOG.isInfoEnabled()) {
705 LOG.info("Failure authenticating with " + realm);
706 }
707 return false;
708 } else {
709 return true;
710 }
711 } else {
712 this.authProcess = AUTH_PROXY_REQUIRED;
713
714 Credentials credentials = this.state.getProxyCredentials(realm);
715 if (credentials == null) {
716 credentials = promptForProxyCredentials(
717 authscheme, method.getParams(), realm);
718 }
719 if (credentials == null) {
720 if (LOG.isInfoEnabled()) {
721 LOG.info("No proxy credentials available for the " + realm);
722 }
723 return false;
724 } else {
725 return true;
726 }
727 }
728 }
729
730 /***
731 * Tests if the {@link HttpMethod method} requires a redirect to another location.
732 *
733 * @param method HTTP method
734 *
735 * @return boolean <tt>true</tt> if a retry is needed, <tt>false</tt> otherwise.
736 */
737 private boolean isRedirectNeeded(final HttpMethod method) {
738 switch (method.getStatusCode()) {
739 case HttpStatus.SC_MOVED_TEMPORARILY:
740 case HttpStatus.SC_MOVED_PERMANENTLY:
741 case HttpStatus.SC_SEE_OTHER:
742 case HttpStatus.SC_TEMPORARY_REDIRECT:
743 LOG.debug("Redirect required");
744 if (method.getFollowRedirects()) {
745 return true;
746 } else {
747 LOG.info("Redirect requested but followRedirects is "
748 + "disabled");
749 return false;
750 }
751 default:
752 return false;
753 }
754 }
755
756 /***
757 * Tests if the {@link HttpMethod method} requires authentication.
758 *
759 * @param method HTTP method
760 *
761 * @return boolean <tt>true</tt> if a retry is needed, <tt>false</tt> otherwise.
762 */
763 private boolean isAuthenticationNeeded(final HttpMethod method) {
764 switch (method.getStatusCode()) {
765 case HttpStatus.SC_UNAUTHORIZED:
766 case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
767 LOG.debug("Authorization required");
768 if (method.getDoAuthentication()) {
769 return true;
770 } else {
771 LOG.info("Authentication requested but doAuthentication is "
772 + "disabled");
773 return false;
774 }
775 default:
776 return false;
777 }
778 }
779
780 private Credentials promptForCredentials(
781 final AuthScheme authScheme,
782 final HttpParams params,
783 final HttpAuthRealm realm)
784 {
785 Credentials creds = null;
786 CredentialsProvider credProvider =
787 (CredentialsProvider)params.getParameter(CredentialsProvider.PROVIDER);
788 if (credProvider != null) {
789 try {
790 creds = credProvider.getCredentials(
791 authScheme, realm.getHost(), realm.getPort(), false);
792 } catch (CredentialsNotAvailableException e) {
793 LOG.warn(e.getMessage());
794 }
795 if (creds != null) {
796 this.state.setCredentials(realm, creds);
797 if (LOG.isDebugEnabled()) {
798 LOG.debug("New credentials for " + realm);
799 }
800 }
801 }
802 return creds;
803 }
804
805 private Credentials promptForProxyCredentials(
806 final AuthScheme authScheme,
807 final HttpParams params,
808 final HttpAuthRealm realm)
809 {
810 Credentials creds = null;
811 CredentialsProvider credProvider =
812 (CredentialsProvider)params.getParameter(CredentialsProvider.PROVIDER);
813 if (credProvider != null) {
814 try {
815 creds = credProvider.getCredentials(
816 authScheme, realm.getHost(), realm.getPort(), true);
817 } catch (CredentialsNotAvailableException e) {
818 LOG.warn(e.getMessage());
819 }
820 if (creds != null) {
821 this.state.setProxyCredentials(realm, creds);
822 if (LOG.isDebugEnabled()) {
823 LOG.debug("New proxy credentials for " + realm);
824 }
825 }
826 }
827 return creds;
828 }
829
830 /***
831 * @return
832 */
833 public HostConfiguration getHostConfiguration() {
834 return hostConfiguration;
835 }
836
837 /***
838 * @return
839 */
840 public HttpState getState() {
841 return state;
842 }
843
844 /***
845 * @return
846 */
847 public HttpConnectionManager getConnectionManager() {
848 return connectionManager;
849 }
850
851 /***
852 * @return
853 */
854 public HttpParams getParams() {
855 return this.params;
856 }
857 }