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