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