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 package org.apache.http.impl.client;
29
30 import java.io.IOException;
31 import java.io.InterruptedIOException;
32 import java.net.URI;
33 import java.net.URISyntaxException;
34 import java.util.concurrent.TimeUnit;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.apache.http.ConnectionReuseStrategy;
39 import org.apache.http.HttpEntity;
40 import org.apache.http.HttpEntityEnclosingRequest;
41 import org.apache.http.HttpException;
42 import org.apache.http.HttpHost;
43 import org.apache.http.HttpRequest;
44 import org.apache.http.HttpResponse;
45 import org.apache.http.ProtocolException;
46 import org.apache.http.ProtocolVersion;
47 import org.apache.http.annotation.NotThreadSafe;
48 import org.apache.http.auth.AuthProtocolState;
49 import org.apache.http.auth.AuthScheme;
50 import org.apache.http.auth.AuthState;
51 import org.apache.http.auth.UsernamePasswordCredentials;
52 import org.apache.http.client.AuthenticationHandler;
53 import org.apache.http.client.AuthenticationStrategy;
54 import org.apache.http.client.HttpRequestRetryHandler;
55 import org.apache.http.client.NonRepeatableRequestException;
56 import org.apache.http.client.RedirectException;
57 import org.apache.http.client.RedirectHandler;
58 import org.apache.http.client.RedirectStrategy;
59 import org.apache.http.client.RequestDirector;
60 import org.apache.http.client.UserTokenHandler;
61 import org.apache.http.client.methods.AbortableHttpRequest;
62 import org.apache.http.client.methods.HttpUriRequest;
63 import org.apache.http.client.params.ClientPNames;
64 import org.apache.http.client.params.HttpClientParams;
65 import org.apache.http.client.protocol.ClientContext;
66 import org.apache.http.client.utils.URIUtils;
67 import org.apache.http.conn.BasicManagedEntity;
68 import org.apache.http.conn.ClientConnectionManager;
69 import org.apache.http.conn.ClientConnectionRequest;
70 import org.apache.http.conn.ConnectionKeepAliveStrategy;
71 import org.apache.http.conn.ManagedClientConnection;
72 import org.apache.http.conn.routing.BasicRouteDirector;
73 import org.apache.http.conn.routing.HttpRoute;
74 import org.apache.http.conn.routing.HttpRouteDirector;
75 import org.apache.http.conn.routing.HttpRoutePlanner;
76 import org.apache.http.conn.scheme.Scheme;
77 import org.apache.http.entity.BufferedHttpEntity;
78 import org.apache.http.impl.auth.BasicScheme;
79 import org.apache.http.impl.conn.ConnectionShutdownException;
80 import org.apache.http.message.BasicHttpRequest;
81 import org.apache.http.params.HttpConnectionParams;
82 import org.apache.http.params.HttpParams;
83 import org.apache.http.params.HttpProtocolParams;
84 import org.apache.http.protocol.ExecutionContext;
85 import org.apache.http.protocol.HttpContext;
86 import org.apache.http.protocol.HttpProcessor;
87 import org.apache.http.protocol.HttpRequestExecutor;
88 import org.apache.http.util.Args;
89 import org.apache.http.util.Asserts;
90 import org.apache.http.util.EntityUtils;
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 @Deprecated
135 @NotThreadSafe
136 public class DefaultRequestDirector implements RequestDirector {
137
138 private final Log log;
139
140
141 protected final ClientConnectionManager connManager;
142
143
144 protected final HttpRoutePlanner routePlanner;
145
146
147 protected final ConnectionReuseStrategy reuseStrategy;
148
149
150 protected final ConnectionKeepAliveStrategy keepAliveStrategy;
151
152
153 protected final HttpRequestExecutor requestExec;
154
155
156 protected final HttpProcessor httpProcessor;
157
158
159 protected final HttpRequestRetryHandler retryHandler;
160
161
162 @Deprecated
163 protected final RedirectHandler redirectHandler;
164
165
166 protected final RedirectStrategy redirectStrategy;
167
168
169 @Deprecated
170 protected final AuthenticationHandler targetAuthHandler;
171
172
173 protected final AuthenticationStrategy targetAuthStrategy;
174
175
176 @Deprecated
177 protected final AuthenticationHandler proxyAuthHandler;
178
179
180 protected final AuthenticationStrategy proxyAuthStrategy;
181
182
183 protected final UserTokenHandler userTokenHandler;
184
185
186 protected final HttpParams params;
187
188
189 protected ManagedClientConnection managedConn;
190
191 protected final AuthState targetAuthState;
192
193 protected final AuthState proxyAuthState;
194
195 private final HttpAuthenticator authenticator;
196
197 private int execCount;
198
199 private int redirectCount;
200
201 private final int maxRedirects;
202
203 private HttpHost virtualHost;
204
205 @Deprecated
206 public DefaultRequestDirector(
207 final HttpRequestExecutor requestExec,
208 final ClientConnectionManager conman,
209 final ConnectionReuseStrategy reustrat,
210 final ConnectionKeepAliveStrategy kastrat,
211 final HttpRoutePlanner rouplan,
212 final HttpProcessor httpProcessor,
213 final HttpRequestRetryHandler retryHandler,
214 final RedirectHandler redirectHandler,
215 final AuthenticationHandler targetAuthHandler,
216 final AuthenticationHandler proxyAuthHandler,
217 final UserTokenHandler userTokenHandler,
218 final HttpParams params) {
219 this(LogFactory.getLog(DefaultRequestDirector.class),
220 requestExec, conman, reustrat, kastrat, rouplan, httpProcessor, retryHandler,
221 new DefaultRedirectStrategyAdaptor(redirectHandler),
222 new AuthenticationStrategyAdaptor(targetAuthHandler),
223 new AuthenticationStrategyAdaptor(proxyAuthHandler),
224 userTokenHandler,
225 params);
226 }
227
228
229 @Deprecated
230 public DefaultRequestDirector(
231 final Log log,
232 final HttpRequestExecutor requestExec,
233 final ClientConnectionManager conman,
234 final ConnectionReuseStrategy reustrat,
235 final ConnectionKeepAliveStrategy kastrat,
236 final HttpRoutePlanner rouplan,
237 final HttpProcessor httpProcessor,
238 final HttpRequestRetryHandler retryHandler,
239 final RedirectStrategy redirectStrategy,
240 final AuthenticationHandler targetAuthHandler,
241 final AuthenticationHandler proxyAuthHandler,
242 final UserTokenHandler userTokenHandler,
243 final HttpParams params) {
244 this(LogFactory.getLog(DefaultRequestDirector.class),
245 requestExec, conman, reustrat, kastrat, rouplan, httpProcessor, retryHandler,
246 redirectStrategy,
247 new AuthenticationStrategyAdaptor(targetAuthHandler),
248 new AuthenticationStrategyAdaptor(proxyAuthHandler),
249 userTokenHandler,
250 params);
251 }
252
253
254
255
256 public DefaultRequestDirector(
257 final Log log,
258 final HttpRequestExecutor requestExec,
259 final ClientConnectionManager conman,
260 final ConnectionReuseStrategy reustrat,
261 final ConnectionKeepAliveStrategy kastrat,
262 final HttpRoutePlanner rouplan,
263 final HttpProcessor httpProcessor,
264 final HttpRequestRetryHandler retryHandler,
265 final RedirectStrategy redirectStrategy,
266 final AuthenticationStrategy targetAuthStrategy,
267 final AuthenticationStrategy proxyAuthStrategy,
268 final UserTokenHandler userTokenHandler,
269 final HttpParams params) {
270
271 Args.notNull(log, "Log");
272 Args.notNull(requestExec, "Request executor");
273 Args.notNull(conman, "Client connection manager");
274 Args.notNull(reustrat, "Connection reuse strategy");
275 Args.notNull(kastrat, "Connection keep alive strategy");
276 Args.notNull(rouplan, "Route planner");
277 Args.notNull(httpProcessor, "HTTP protocol processor");
278 Args.notNull(retryHandler, "HTTP request retry handler");
279 Args.notNull(redirectStrategy, "Redirect strategy");
280 Args.notNull(targetAuthStrategy, "Target authentication strategy");
281 Args.notNull(proxyAuthStrategy, "Proxy authentication strategy");
282 Args.notNull(userTokenHandler, "User token handler");
283 Args.notNull(params, "HTTP parameters");
284 this.log = log;
285 this.authenticator = new HttpAuthenticator(log);
286 this.requestExec = requestExec;
287 this.connManager = conman;
288 this.reuseStrategy = reustrat;
289 this.keepAliveStrategy = kastrat;
290 this.routePlanner = rouplan;
291 this.httpProcessor = httpProcessor;
292 this.retryHandler = retryHandler;
293 this.redirectStrategy = redirectStrategy;
294 this.targetAuthStrategy = targetAuthStrategy;
295 this.proxyAuthStrategy = proxyAuthStrategy;
296 this.userTokenHandler = userTokenHandler;
297 this.params = params;
298
299 if (redirectStrategy instanceof DefaultRedirectStrategyAdaptor) {
300 this.redirectHandler = ((DefaultRedirectStrategyAdaptor) redirectStrategy).getHandler();
301 } else {
302 this.redirectHandler = null;
303 }
304 if (targetAuthStrategy instanceof AuthenticationStrategyAdaptor) {
305 this.targetAuthHandler = ((AuthenticationStrategyAdaptor) targetAuthStrategy).getHandler();
306 } else {
307 this.targetAuthHandler = null;
308 }
309 if (proxyAuthStrategy instanceof AuthenticationStrategyAdaptor) {
310 this.proxyAuthHandler = ((AuthenticationStrategyAdaptor) proxyAuthStrategy).getHandler();
311 } else {
312 this.proxyAuthHandler = null;
313 }
314
315 this.managedConn = null;
316
317 this.execCount = 0;
318 this.redirectCount = 0;
319 this.targetAuthState = new AuthState();
320 this.proxyAuthState = new AuthState();
321 this.maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100);
322 }
323
324
325 private RequestWrapper wrapRequest(
326 final HttpRequest request) throws ProtocolException {
327 if (request instanceof HttpEntityEnclosingRequest) {
328 return new EntityEnclosingRequestWrapper(
329 (HttpEntityEnclosingRequest) request);
330 } else {
331 return new RequestWrapper(
332 request);
333 }
334 }
335
336
337 protected void rewriteRequestURI(
338 final RequestWrapper request,
339 final HttpRoute route) throws ProtocolException {
340 try {
341
342 URI uri = request.getURI();
343 if (route.getProxyHost() != null && !route.isTunnelled()) {
344
345 if (!uri.isAbsolute()) {
346 final HttpHost target = route.getTargetHost();
347 uri = URIUtils.rewriteURI(uri, target, true);
348 } else {
349 uri = URIUtils.rewriteURI(uri);
350 }
351 } else {
352
353 if (uri.isAbsolute()) {
354 uri = URIUtils.rewriteURI(uri, null, true);
355 } else {
356 uri = URIUtils.rewriteURI(uri);
357 }
358 }
359 request.setURI(uri);
360
361 } catch (final URISyntaxException ex) {
362 throw new ProtocolException("Invalid URI: " +
363 request.getRequestLine().getUri(), ex);
364 }
365 }
366
367
368
369 public HttpResponse execute(HttpHost target, final HttpRequest request,
370 final HttpContext context)
371 throws HttpException, IOException {
372
373 context.setAttribute(ClientContext.TARGET_AUTH_STATE, targetAuthState);
374 context.setAttribute(ClientContext.PROXY_AUTH_STATE, proxyAuthState);
375
376 final HttpRequest orig = request;
377 final RequestWrapper origWrapper = wrapRequest(orig);
378 origWrapper.setParams(params);
379 final HttpRoute origRoute = determineRoute(target, origWrapper, context);
380
381 virtualHost = (HttpHost) origWrapper.getParams().getParameter(ClientPNames.VIRTUAL_HOST);
382
383
384 if (virtualHost != null && virtualHost.getPort() == -1) {
385 final HttpHost host = (target != null) ? target : origRoute.getTargetHost();
386 final int port = host.getPort();
387 if (port != -1){
388 virtualHost = new HttpHost(virtualHost.getHostName(), port, virtualHost.getSchemeName());
389 }
390 }
391
392 RoutedRequest roureq = new RoutedRequest(origWrapper, origRoute);
393
394 boolean reuse = false;
395 boolean done = false;
396 try {
397 HttpResponse response = null;
398 while (!done) {
399
400
401
402
403
404 final RequestWrapper wrapper = roureq.getRequest();
405 final HttpRoute route = roureq.getRoute();
406 response = null;
407
408
409 Object userToken = context.getAttribute(ClientContext.USER_TOKEN);
410
411
412 if (managedConn == null) {
413 final ClientConnectionRequest connRequest = connManager.requestConnection(
414 route, userToken);
415 if (orig instanceof AbortableHttpRequest) {
416 ((AbortableHttpRequest) orig).setConnectionRequest(connRequest);
417 }
418
419 final long timeout = HttpClientParams.getConnectionManagerTimeout(params);
420 try {
421 managedConn = connRequest.getConnection(timeout, TimeUnit.MILLISECONDS);
422 } catch(final InterruptedException interrupted) {
423 Thread.currentThread().interrupt();
424 throw new InterruptedIOException();
425 }
426
427 if (HttpConnectionParams.isStaleCheckingEnabled(params)) {
428
429 if (managedConn.isOpen()) {
430 this.log.debug("Stale connection check");
431 if (managedConn.isStale()) {
432 this.log.debug("Stale connection detected");
433 managedConn.close();
434 }
435 }
436 }
437 }
438
439 if (orig instanceof AbortableHttpRequest) {
440 ((AbortableHttpRequest) orig).setReleaseTrigger(managedConn);
441 }
442
443 try {
444 tryConnect(roureq, context);
445 } catch (final TunnelRefusedException ex) {
446 if (this.log.isDebugEnabled()) {
447 this.log.debug(ex.getMessage());
448 }
449 response = ex.getResponse();
450 break;
451 }
452
453 final String userinfo = wrapper.getURI().getUserInfo();
454 if (userinfo != null) {
455 targetAuthState.update(
456 new BasicScheme(), new UsernamePasswordCredentials(userinfo));
457 }
458
459
460 if (virtualHost != null) {
461 target = virtualHost;
462 } else {
463 final URI requestURI = wrapper.getURI();
464 if (requestURI.isAbsolute()) {
465 target = new HttpHost(
466 requestURI.getHost(), requestURI.getPort(), requestURI.getScheme());
467 }
468 }
469 if (target == null) {
470 target = route.getTargetHost();
471 }
472
473
474 wrapper.resetHeaders();
475
476 rewriteRequestURI(wrapper, route);
477
478
479 context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
480 context.setAttribute(ClientContext.ROUTE, route);
481 context.setAttribute(ExecutionContext.HTTP_CONNECTION, managedConn);
482
483
484 requestExec.preProcess(wrapper, httpProcessor, context);
485
486 response = tryExecute(roureq, context);
487 if (response == null) {
488
489 continue;
490 }
491
492
493 response.setParams(params);
494 requestExec.postProcess(response, httpProcessor, context);
495
496
497
498 reuse = reuseStrategy.keepAlive(response, context);
499 if (reuse) {
500
501 final long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
502 if (this.log.isDebugEnabled()) {
503 String s;
504 if (duration > 0) {
505 s = "for " + duration + " " + TimeUnit.MILLISECONDS;
506 } else {
507 s = "indefinitely";
508 }
509 this.log.debug("Connection can be kept alive " + s);
510 }
511 managedConn.setIdleDuration(duration, TimeUnit.MILLISECONDS);
512 }
513
514 final RoutedRequest followup = handleResponse(roureq, response, context);
515 if (followup == null) {
516 done = true;
517 } else {
518 if (reuse) {
519
520 final HttpEntity entity = response.getEntity();
521 EntityUtils.consume(entity);
522
523
524 managedConn.markReusable();
525 } else {
526 managedConn.close();
527 if (proxyAuthState.getState().compareTo(AuthProtocolState.CHALLENGED) > 0
528 && proxyAuthState.getAuthScheme() != null
529 && proxyAuthState.getAuthScheme().isConnectionBased()) {
530 this.log.debug("Resetting proxy auth state");
531 proxyAuthState.reset();
532 }
533 if (targetAuthState.getState().compareTo(AuthProtocolState.CHALLENGED) > 0
534 && targetAuthState.getAuthScheme() != null
535 && targetAuthState.getAuthScheme().isConnectionBased()) {
536 this.log.debug("Resetting target auth state");
537 targetAuthState.reset();
538 }
539 }
540
541 if (!followup.getRoute().equals(roureq.getRoute())) {
542 releaseConnection();
543 }
544 roureq = followup;
545 }
546
547 if (managedConn != null) {
548 if (userToken == null) {
549 userToken = userTokenHandler.getUserToken(context);
550 context.setAttribute(ClientContext.USER_TOKEN, userToken);
551 }
552 if (userToken != null) {
553 managedConn.setState(userToken);
554 }
555 }
556
557 }
558
559
560
561 if ((response == null) || (response.getEntity() == null) ||
562 !response.getEntity().isStreaming()) {
563
564 if (reuse) {
565 managedConn.markReusable();
566 }
567 releaseConnection();
568 } else {
569
570 HttpEntity entity = response.getEntity();
571 entity = new BasicManagedEntity(entity, managedConn, reuse);
572 response.setEntity(entity);
573 }
574
575 return response;
576
577 } catch (final ConnectionShutdownException ex) {
578 final InterruptedIOException ioex = new InterruptedIOException(
579 "Connection has been shut down");
580 ioex.initCause(ex);
581 throw ioex;
582 } catch (final HttpException ex) {
583 abortConnection();
584 throw ex;
585 } catch (final IOException ex) {
586 abortConnection();
587 throw ex;
588 } catch (final RuntimeException ex) {
589 abortConnection();
590 throw ex;
591 }
592 }
593
594
595
596
597
598 private void tryConnect(
599 final RoutedRequest req, final HttpContext context) throws HttpException, IOException {
600 final HttpRoute route = req.getRoute();
601 final HttpRequest wrapper = req.getRequest();
602
603 int connectCount = 0;
604 for (;;) {
605 context.setAttribute(ExecutionContext.HTTP_REQUEST, wrapper);
606
607 connectCount++;
608 try {
609 if (!managedConn.isOpen()) {
610 managedConn.open(route, context, params);
611 } else {
612 managedConn.setSocketTimeout(HttpConnectionParams.getSoTimeout(params));
613 }
614 establishRoute(route, context);
615 break;
616 } catch (final IOException ex) {
617 try {
618 managedConn.close();
619 } catch (final IOException ignore) {
620 }
621 if (retryHandler.retryRequest(ex, connectCount, context)) {
622 if (this.log.isInfoEnabled()) {
623 this.log.info("I/O exception ("+ ex.getClass().getName() +
624 ") caught when connecting to the target host: "
625 + ex.getMessage());
626 if (this.log.isDebugEnabled()) {
627 this.log.debug(ex.getMessage(), ex);
628 }
629 this.log.info("Retrying connect");
630 }
631 } else {
632 throw ex;
633 }
634 }
635 }
636 }
637
638
639
640
641 private HttpResponse tryExecute(
642 final RoutedRequest req, final HttpContext context) throws HttpException, IOException {
643 final RequestWrapper wrapper = req.getRequest();
644 final HttpRoute route = req.getRoute();
645 HttpResponse response = null;
646
647 Exception retryReason = null;
648 for (;;) {
649
650 execCount++;
651
652 wrapper.incrementExecCount();
653 if (!wrapper.isRepeatable()) {
654 this.log.debug("Cannot retry non-repeatable request");
655 if (retryReason != null) {
656 throw new NonRepeatableRequestException("Cannot retry request " +
657 "with a non-repeatable request entity. The cause lists the " +
658 "reason the original request failed.", retryReason);
659 } else {
660 throw new NonRepeatableRequestException("Cannot retry request " +
661 "with a non-repeatable request entity.");
662 }
663 }
664
665 try {
666 if (!managedConn.isOpen()) {
667
668
669 if (!route.isTunnelled()) {
670 this.log.debug("Reopening the direct connection.");
671 managedConn.open(route, context, params);
672 } else {
673
674 this.log.debug("Proxied connection. Need to start over.");
675 break;
676 }
677 }
678
679 if (this.log.isDebugEnabled()) {
680 this.log.debug("Attempt " + execCount + " to execute request");
681 }
682 response = requestExec.execute(wrapper, managedConn, context);
683 break;
684
685 } catch (final IOException ex) {
686 this.log.debug("Closing the connection.");
687 try {
688 managedConn.close();
689 } catch (final IOException ignore) {
690 }
691 if (retryHandler.retryRequest(ex, wrapper.getExecCount(), context)) {
692 if (this.log.isInfoEnabled()) {
693 this.log.info("I/O exception ("+ ex.getClass().getName() +
694 ") caught when processing request: "
695 + ex.getMessage());
696 }
697 if (this.log.isDebugEnabled()) {
698 this.log.debug(ex.getMessage(), ex);
699 }
700 this.log.info("Retrying request");
701 retryReason = ex;
702 } else {
703 throw ex;
704 }
705 }
706 }
707 return response;
708 }
709
710
711
712
713
714
715 protected void releaseConnection() {
716
717
718
719 try {
720 managedConn.releaseConnection();
721 } catch(final IOException ignored) {
722 this.log.debug("IOException releasing connection", ignored);
723 }
724 managedConn = null;
725 }
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744 protected HttpRoute determineRoute(HttpHost target,
745 final HttpRequest request,
746 final HttpContext context)
747 throws HttpException {
748
749 if (target == null) {
750 target = (HttpHost) request.getParams().getParameter(
751 ClientPNames.DEFAULT_HOST);
752 }
753 Asserts.notNull(target, "Target host");
754 return this.routePlanner.determineRoute(target, request, context);
755 }
756
757
758
759
760
761
762
763
764
765
766
767 protected void establishRoute(final HttpRoute route, final HttpContext context)
768 throws HttpException, IOException {
769
770 final HttpRouteDirector rowdy = new BasicRouteDirector();
771 int step;
772 do {
773 final HttpRoute fact = managedConn.getRoute();
774 step = rowdy.nextStep(route, fact);
775
776 switch (step) {
777
778 case HttpRouteDirector.CONNECT_TARGET:
779 case HttpRouteDirector.CONNECT_PROXY:
780 managedConn.open(route, context, this.params);
781 break;
782
783 case HttpRouteDirector.TUNNEL_TARGET: {
784 final boolean secure = createTunnelToTarget(route, context);
785 this.log.debug("Tunnel to target created.");
786 managedConn.tunnelTarget(secure, this.params);
787 } break;
788
789 case HttpRouteDirector.TUNNEL_PROXY: {
790
791
792
793
794 final int hop = fact.getHopCount()-1;
795 final boolean secure = createTunnelToProxy(route, hop, context);
796 this.log.debug("Tunnel to proxy created.");
797 managedConn.tunnelProxy(route.getHopTarget(hop),
798 secure, this.params);
799 } break;
800
801
802 case HttpRouteDirector.LAYER_PROTOCOL:
803 managedConn.layerProtocol(context, this.params);
804 break;
805
806 case HttpRouteDirector.UNREACHABLE:
807 throw new HttpException("Unable to establish route: " +
808 "planned = " + route + "; current = " + fact);
809 case HttpRouteDirector.COMPLETE:
810
811 break;
812 default:
813 throw new IllegalStateException("Unknown step indicator "
814 + step + " from RouteDirector.");
815 }
816
817 } while (step > HttpRouteDirector.COMPLETE);
818
819 }
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841 protected boolean createTunnelToTarget(final HttpRoute route,
842 final HttpContext context)
843 throws HttpException, IOException {
844
845 final HttpHost proxy = route.getProxyHost();
846 final HttpHost target = route.getTargetHost();
847 HttpResponse response = null;
848
849 for (;;) {
850 if (!this.managedConn.isOpen()) {
851 this.managedConn.open(route, context, this.params);
852 }
853
854 final HttpRequest connect = createConnectRequest(route, context);
855 connect.setParams(this.params);
856
857
858 context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
859 context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy);
860 context.setAttribute(ExecutionContext.HTTP_CONNECTION, managedConn);
861 context.setAttribute(ExecutionContext.HTTP_REQUEST, connect);
862
863 this.requestExec.preProcess(connect, this.httpProcessor, context);
864
865 response = this.requestExec.execute(connect, this.managedConn, context);
866
867 response.setParams(this.params);
868 this.requestExec.postProcess(response, this.httpProcessor, context);
869
870 final int status = response.getStatusLine().getStatusCode();
871 if (status < 200) {
872 throw new HttpException("Unexpected response to CONNECT request: " +
873 response.getStatusLine());
874 }
875
876 if (HttpClientParams.isAuthenticating(this.params)) {
877 if (this.authenticator.isAuthenticationRequested(proxy, response,
878 this.proxyAuthStrategy, this.proxyAuthState, context)) {
879 if (this.authenticator.authenticate(proxy, response,
880 this.proxyAuthStrategy, this.proxyAuthState, context)) {
881
882 if (this.reuseStrategy.keepAlive(response, context)) {
883 this.log.debug("Connection kept alive");
884
885 final HttpEntity entity = response.getEntity();
886 EntityUtils.consume(entity);
887 } else {
888 this.managedConn.close();
889 }
890 } else {
891 break;
892 }
893 } else {
894 break;
895 }
896 }
897 }
898
899 final int status = response.getStatusLine().getStatusCode();
900
901 if (status > 299) {
902
903
904 final HttpEntity entity = response.getEntity();
905 if (entity != null) {
906 response.setEntity(new BufferedHttpEntity(entity));
907 }
908
909 this.managedConn.close();
910 throw new TunnelRefusedException("CONNECT refused by proxy: " +
911 response.getStatusLine(), response);
912 }
913
914 this.managedConn.markReusable();
915
916
917
918
919
920 return false;
921
922 }
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943 protected boolean createTunnelToProxy(final HttpRoute route, final int hop,
944 final HttpContext context)
945 throws HttpException, IOException {
946
947
948
949
950
951
952
953
954
955
956 throw new HttpException("Proxy chains are not supported.");
957 }
958
959
960
961
962
963
964
965
966
967
968
969
970 protected HttpRequest createConnectRequest(final HttpRoute route,
971 final HttpContext context) {
972
973
974
975
976 final HttpHost target = route.getTargetHost();
977
978 final String host = target.getHostName();
979 int port = target.getPort();
980 if (port < 0) {
981 final Scheme scheme = connManager.getSchemeRegistry().
982 getScheme(target.getSchemeName());
983 port = scheme.getDefaultPort();
984 }
985
986 final StringBuilder buffer = new StringBuilder(host.length() + 6);
987 buffer.append(host);
988 buffer.append(':');
989 buffer.append(Integer.toString(port));
990
991 final String authority = buffer.toString();
992 final ProtocolVersion ver = HttpProtocolParams.getVersion(params);
993 final HttpRequest req = new BasicHttpRequest
994 ("CONNECT", authority, ver);
995
996 return req;
997 }
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013 protected RoutedRequest handleResponse(final RoutedRequest roureq,
1014 final HttpResponse response,
1015 final HttpContext context)
1016 throws HttpException, IOException {
1017
1018 final HttpRoute route = roureq.getRoute();
1019 final RequestWrapper request = roureq.getRequest();
1020
1021 final HttpParams params = request.getParams();
1022
1023 if (HttpClientParams.isAuthenticating(params)) {
1024 HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
1025 if (target == null) {
1026 target = route.getTargetHost();
1027 }
1028 if (target.getPort() < 0) {
1029 final Scheme scheme = connManager.getSchemeRegistry().getScheme(target);
1030 target = new HttpHost(target.getHostName(), scheme.getDefaultPort(), target.getSchemeName());
1031 }
1032 if (this.authenticator.isAuthenticationRequested(target, response,
1033 this.targetAuthStrategy, this.targetAuthState, context)) {
1034 if (this.authenticator.authenticate(target, response,
1035 this.targetAuthStrategy, this.targetAuthState, context)) {
1036
1037 return roureq;
1038 }
1039 }
1040
1041 HttpHost proxy = route.getProxyHost();
1042 if (this.authenticator.isAuthenticationRequested(proxy, response,
1043 this.proxyAuthStrategy, this.proxyAuthState, context)) {
1044
1045 if (proxy == null) {
1046 proxy = route.getTargetHost();
1047 }
1048 if (this.authenticator.authenticate(proxy, response,
1049 this.proxyAuthStrategy, this.proxyAuthState, context)) {
1050
1051 return roureq;
1052 }
1053 }
1054 }
1055
1056 if (HttpClientParams.isRedirecting(params) &&
1057 this.redirectStrategy.isRedirected(request, response, context)) {
1058
1059 if (redirectCount >= maxRedirects) {
1060 throw new RedirectException("Maximum redirects ("
1061 + maxRedirects + ") exceeded");
1062 }
1063 redirectCount++;
1064
1065
1066 virtualHost = null;
1067
1068 final HttpUriRequest redirect = redirectStrategy.getRedirect(request, response, context);
1069 final HttpRequest orig = request.getOriginal();
1070 redirect.setHeaders(orig.getAllHeaders());
1071
1072 final URI uri = redirect.getURI();
1073 final HttpHost newTarget = URIUtils.extractHost(uri);
1074 if (newTarget == null) {
1075 throw new ProtocolException("Redirect URI does not specify a valid host name: " + uri);
1076 }
1077
1078
1079 if (!route.getTargetHost().equals(newTarget)) {
1080 this.log.debug("Resetting target auth state");
1081 targetAuthState.reset();
1082 final AuthScheme authScheme = proxyAuthState.getAuthScheme();
1083 if (authScheme != null && authScheme.isConnectionBased()) {
1084 this.log.debug("Resetting proxy auth state");
1085 proxyAuthState.reset();
1086 }
1087 }
1088
1089 final RequestWrapper wrapper = wrapRequest(redirect);
1090 wrapper.setParams(params);
1091
1092 final HttpRoute newRoute = determineRoute(newTarget, wrapper, context);
1093 final RoutedRequest newRequest = new RoutedRequest(wrapper, newRoute);
1094
1095 if (this.log.isDebugEnabled()) {
1096 this.log.debug("Redirecting to '" + uri + "' via " + newRoute);
1097 }
1098
1099 return newRequest;
1100 }
1101
1102 return null;
1103 }
1104
1105
1106
1107
1108
1109
1110
1111 private void abortConnection() {
1112 final ManagedClientConnection mcc = managedConn;
1113 if (mcc != null) {
1114
1115
1116 managedConn = null;
1117 try {
1118 mcc.abortConnection();
1119 } catch (final IOException ex) {
1120 if (this.log.isDebugEnabled()) {
1121 this.log.debug(ex.getMessage(), ex);
1122 }
1123 }
1124
1125 try {
1126 mcc.releaseConnection();
1127 } catch(final IOException ignored) {
1128 this.log.debug("Error releasing connection", ignored);
1129 }
1130 }
1131 }
1132
1133
1134 }