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 package org.apache.http.impl.nio.client;
28
29 import java.io.IOException;
30 import java.net.URI;
31 import java.net.URISyntaxException;
32 import java.nio.ByteBuffer;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.atomic.AtomicLong;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.http.ConnectionClosedException;
38 import org.apache.http.ConnectionReuseStrategy;
39 import org.apache.http.HttpEntityEnclosingRequest;
40 import org.apache.http.HttpException;
41 import org.apache.http.HttpHost;
42 import org.apache.http.HttpRequest;
43 import org.apache.http.HttpResponse;
44 import org.apache.http.HttpStatus;
45 import org.apache.http.ProtocolException;
46 import org.apache.http.ProtocolVersion;
47 import org.apache.http.auth.AuthProtocolState;
48 import org.apache.http.auth.AuthScheme;
49 import org.apache.http.auth.AuthState;
50 import org.apache.http.auth.UsernamePasswordCredentials;
51 import org.apache.http.client.AuthenticationStrategy;
52 import org.apache.http.client.CredentialsProvider;
53 import org.apache.http.client.NonRepeatableRequestException;
54 import org.apache.http.client.RedirectException;
55 import org.apache.http.client.RedirectStrategy;
56 import org.apache.http.client.UserTokenHandler;
57 import org.apache.http.client.config.RequestConfig;
58 import org.apache.http.client.methods.AbortableHttpRequest;
59 import org.apache.http.client.methods.HttpUriRequest;
60 import org.apache.http.client.params.ClientPNames;
61 import org.apache.http.client.params.HttpClientParams;
62 import org.apache.http.client.protocol.ClientContext;
63 import org.apache.http.client.utils.URIUtils;
64 import org.apache.http.concurrent.FutureCallback;
65 import org.apache.http.conn.ConnectionKeepAliveStrategy;
66 import org.apache.http.conn.ConnectionReleaseTrigger;
67 import org.apache.http.conn.routing.BasicRouteDirector;
68 import org.apache.http.conn.routing.HttpRoute;
69 import org.apache.http.conn.routing.HttpRouteDirector;
70 import org.apache.http.conn.routing.HttpRoutePlanner;
71 import org.apache.http.impl.auth.BasicScheme;
72 import org.apache.http.impl.client.ClientParamsStack;
73 import org.apache.http.impl.client.EntityEnclosingRequestWrapper;
74 import org.apache.http.impl.client.HttpAuthenticator;
75 import org.apache.http.impl.client.RequestWrapper;
76 import org.apache.http.impl.client.RoutedRequest;
77 import org.apache.http.message.BasicHttpRequest;
78 import org.apache.http.nio.ContentDecoder;
79 import org.apache.http.nio.ContentEncoder;
80 import org.apache.http.nio.IOControl;
81 import org.apache.http.nio.conn.ClientAsyncConnectionManager;
82 import org.apache.http.nio.conn.ManagedClientAsyncConnection;
83 import org.apache.http.nio.conn.scheme.AsyncScheme;
84 import org.apache.http.nio.conn.scheme.AsyncSchemeRegistry;
85 import org.apache.http.nio.protocol.HttpAsyncRequestExecutionHandler;
86 import org.apache.http.nio.protocol.HttpAsyncRequestExecutor;
87 import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
88 import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
89 import org.apache.http.params.HttpConnectionParams;
90 import org.apache.http.params.HttpParams;
91 import org.apache.http.params.HttpProtocolParams;
92 import org.apache.http.protocol.ExecutionContext;
93 import org.apache.http.protocol.HttpContext;
94 import org.apache.http.protocol.HttpProcessor;
95
96 @Deprecated
97 class DefaultAsyncRequestDirector<T> implements HttpAsyncRequestExecutionHandler<T> {
98
99 private static final AtomicLong COUNTER = new AtomicLong(1);
100
101 private final Log log;
102
103 private final HttpAsyncRequestProducer requestProducer;
104 private final HttpAsyncResponseConsumer<T> responseConsumer;
105 private final HttpContext localContext;
106 private final ResultCallback<T> resultCallback;
107 private final ClientAsyncConnectionManager connmgr;
108 private final HttpProcessor httpPocessor;
109 private final HttpRoutePlanner routePlanner;
110 private final HttpRouteDirector routeDirector;
111 private final ConnectionReuseStrategy reuseStrategy;
112 private final ConnectionKeepAliveStrategy keepaliveStrategy;
113 private final RedirectStrategy redirectStrategy;
114 private final AuthenticationStrategy targetAuthStrategy;
115 private final AuthenticationStrategy proxyAuthStrategy;
116 private final UserTokenHandler userTokenHandler;
117 private final AuthState targetAuthState;
118 private final AuthState proxyAuthState;
119 private final HttpAuthenticator authenticator;
120 private final HttpParams clientParams;
121 private final long id;
122
123 private volatile boolean closed;
124 private volatile InternalFutureCallback connRequestCallback;
125 private volatile ManagedClientAsyncConnection managedConn;
126
127 private RoutedRequest mainRequest;
128 private RoutedRequest followup;
129 private HttpResponse finalResponse;
130
131 private ClientParamsStack params;
132 private RequestWrapper currentRequest;
133 private HttpResponse currentResponse;
134 private boolean routeEstablished;
135 private int redirectCount;
136 private ByteBuffer tmpbuf;
137 private boolean requestContentProduced;
138 private boolean requestSent;
139 private int execCount;
140
141 public DefaultAsyncRequestDirector(
142 final Log log,
143 final HttpAsyncRequestProducer requestProducer,
144 final HttpAsyncResponseConsumer<T> responseConsumer,
145 final HttpContext localContext,
146 final ResultCallback<T> callback,
147 final ClientAsyncConnectionManager connmgr,
148 final HttpProcessor httpPocessor,
149 final HttpRoutePlanner routePlanner,
150 final ConnectionReuseStrategy reuseStrategy,
151 final ConnectionKeepAliveStrategy keepaliveStrategy,
152 final RedirectStrategy redirectStrategy,
153 final AuthenticationStrategy targetAuthStrategy,
154 final AuthenticationStrategy proxyAuthStrategy,
155 final UserTokenHandler userTokenHandler,
156 final HttpParams clientParams) {
157 super();
158 this.log = log;
159 this.requestProducer = requestProducer;
160 this.responseConsumer = responseConsumer;
161 this.localContext = localContext;
162 this.resultCallback = callback;
163 this.connmgr = connmgr;
164 this.httpPocessor = httpPocessor;
165 this.routePlanner = routePlanner;
166 this.reuseStrategy = reuseStrategy;
167 this.keepaliveStrategy = keepaliveStrategy;
168 this.redirectStrategy = redirectStrategy;
169 this.routeDirector = new BasicRouteDirector();
170 this.targetAuthStrategy = targetAuthStrategy;
171 this.proxyAuthStrategy = proxyAuthStrategy;
172 this.userTokenHandler = userTokenHandler;
173 this.targetAuthState = new AuthState();
174 this.proxyAuthState = new AuthState();
175 this.authenticator = new HttpAuthenticator(log);
176 this.clientParams = clientParams;
177 this.id = COUNTER.getAndIncrement();
178 }
179
180 @Override
181 public void close() {
182 if (this.closed) {
183 return;
184 }
185 this.closed = true;
186 final ManagedClientAsyncConnection localConn = this.managedConn;
187 if (localConn != null) {
188 if (this.log.isDebugEnabled()) {
189 this.log.debug("[exchange: " + this.id + "] aborting connection " + localConn);
190 }
191 try {
192 localConn.abortConnection();
193 } catch (final IOException ioex) {
194 this.log.debug("I/O error releasing connection", ioex);
195 }
196 }
197 try {
198 this.requestProducer.close();
199 } catch (final IOException ex) {
200 this.log.debug("I/O error closing request producer", ex);
201 }
202 try {
203 this.responseConsumer.close();
204 } catch (final IOException ex) {
205 this.log.debug("I/O error closing response consumer", ex);
206 }
207 }
208
209 public synchronized void start() {
210 try {
211 if (this.log.isDebugEnabled()) {
212 this.log.debug("[exchange: " + this.id + "] start execution");
213 }
214 this.localContext.setAttribute(ClientContext.TARGET_AUTH_STATE, this.targetAuthState);
215 this.localContext.setAttribute(ClientContext.PROXY_AUTH_STATE, this.proxyAuthState);
216
217 final HttpHost target = this.requestProducer.getTarget();
218 final HttpRequest request = this.requestProducer.generateRequest();
219 if (request instanceof AbortableHttpRequest) {
220 ((AbortableHttpRequest) request).setReleaseTrigger(new ConnectionReleaseTrigger() {
221
222 @Override
223 public void releaseConnection() throws IOException {
224 }
225
226 @Override
227 public void abortConnection() throws IOException {
228 cancel();
229 }
230
231 });
232 }
233 this.params = new ClientParamsStack(null, this.clientParams, request.getParams(), null);
234 final RequestWrapper wrapper = wrapRequest(request);
235 wrapper.setParams(this.params);
236 final HttpRoute route = determineRoute(target, wrapper, this.localContext);
237 this.mainRequest = new RoutedRequest(wrapper, route);
238 final RequestConfig config = ParamConfig.getRequestConfig(params);
239 this.localContext.setAttribute(ClientContext.REQUEST_CONFIG, config);
240 this.requestContentProduced = false;
241 requestConnection();
242 } catch (final Exception ex) {
243 failed(ex);
244 }
245 }
246
247 @Override
248 public HttpHost getTarget() {
249 return this.requestProducer.getTarget();
250 }
251
252 @Override
253 public synchronized HttpRequest generateRequest() throws IOException, HttpException {
254 final HttpRoute route = this.mainRequest.getRoute();
255 if (!this.routeEstablished) {
256 int step;
257 do {
258 final HttpRoute fact = this.managedConn.getRoute();
259 step = this.routeDirector.nextStep(route, fact);
260 switch (step) {
261 case HttpRouteDirector.CONNECT_TARGET:
262 case HttpRouteDirector.CONNECT_PROXY:
263 break;
264 case HttpRouteDirector.TUNNEL_TARGET:
265 if (this.log.isDebugEnabled()) {
266 this.log.debug("[exchange: " + this.id + "] Tunnel required");
267 }
268 final HttpRequest connect = createConnectRequest(route);
269 this.currentRequest = wrapRequest(connect);
270 this.currentRequest.setParams(this.params);
271 break;
272 case HttpRouteDirector.TUNNEL_PROXY:
273 throw new HttpException("Proxy chains are not supported");
274 case HttpRouteDirector.LAYER_PROTOCOL:
275 managedConn.layerProtocol(this.localContext, this.params);
276 break;
277 case HttpRouteDirector.UNREACHABLE:
278 throw new HttpException("Unable to establish route: " +
279 "planned = " + route + "; current = " + fact);
280 case HttpRouteDirector.COMPLETE:
281 this.routeEstablished = true;
282 break;
283 default:
284 throw new IllegalStateException("Unknown step indicator "
285 + step + " from RouteDirector.");
286 }
287 } while (step > HttpRouteDirector.COMPLETE && this.currentRequest == null);
288 }
289
290 HttpHost target = (HttpHost) this.params.getParameter(ClientPNames.VIRTUAL_HOST);
291 if (target == null) {
292 target = route.getTargetHost();
293 }
294 final HttpHost proxy = route.getProxyHost();
295 this.localContext.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
296 this.localContext.setAttribute(ExecutionContext.HTTP_PROXY_HOST, proxy);
297 this.localContext.setAttribute(ExecutionContext.HTTP_CONNECTION, this.managedConn);
298 this.localContext.setAttribute(ClientContext.ROUTE, route);
299
300 if (this.currentRequest == null) {
301 this.currentRequest = this.mainRequest.getRequest();
302
303 final String userinfo = this.currentRequest.getURI().getUserInfo();
304 if (userinfo != null) {
305 this.targetAuthState.update(
306 new BasicScheme(), new UsernamePasswordCredentials(userinfo));
307 }
308
309
310 rewriteRequestURI(this.currentRequest, route);
311 }
312
313 this.currentRequest.resetHeaders();
314
315 this.currentRequest.incrementExecCount();
316 if (this.currentRequest.getExecCount() > 1
317 && !this.requestProducer.isRepeatable()
318 && this.requestContentProduced) {
319 throw new NonRepeatableRequestException("Cannot retry request " +
320 "with a non-repeatable request entity.");
321 }
322 this.execCount++;
323 if (this.log.isDebugEnabled()) {
324 this.log.debug("[exchange: " + this.id + "] Attempt " + this.execCount + " to execute request");
325 }
326 return this.currentRequest;
327 }
328
329 @Override
330 public synchronized void produceContent(
331 final ContentEncoder encoder, final IOControl ioControl) throws IOException {
332 if (this.log.isDebugEnabled()) {
333 this.log.debug("[exchange: " + this.id + "] produce content");
334 }
335 this.requestContentProduced = true;
336 this.requestProducer.produceContent(encoder, ioControl);
337 if (encoder.isCompleted()) {
338 this.requestProducer.resetRequest();
339 }
340 }
341
342 @Override
343 public void requestCompleted(final HttpContext context) {
344 if (this.log.isDebugEnabled()) {
345 this.log.debug("[exchange: " + this.id + "] Request completed");
346 }
347 this.requestSent = true;
348 this.requestProducer.requestCompleted(context);
349 }
350
351 @Override
352 public boolean isRepeatable() {
353 return this.requestProducer.isRepeatable();
354 }
355
356 @Override
357 public void resetRequest() throws IOException {
358 this.requestSent = false;
359 this.requestProducer.resetRequest();
360 }
361
362 @Override
363 public synchronized void responseReceived(
364 final HttpResponse response) throws IOException, HttpException {
365 if (this.log.isDebugEnabled()) {
366 this.log.debug("[exchange: " + this.id + "] Response received " + response.getStatusLine());
367 }
368 this.currentResponse = response;
369 this.currentResponse.setParams(this.params);
370
371 final int status = this.currentResponse.getStatusLine().getStatusCode();
372
373 if (!this.routeEstablished) {
374 final String method = this.currentRequest.getMethod();
375 if (method.equalsIgnoreCase("CONNECT") && status == HttpStatus.SC_OK) {
376 this.managedConn.tunnelTarget(this.params);
377 } else {
378 this.followup = handleConnectResponse();
379 if (this.followup == null) {
380 this.finalResponse = response;
381 }
382 }
383 } else {
384 this.followup = handleResponse();
385 if (this.followup == null) {
386 this.finalResponse = response;
387 }
388
389 Object userToken = this.localContext.getAttribute(ClientContext.USER_TOKEN);
390 if (managedConn != null) {
391 if (userToken == null) {
392 userToken = userTokenHandler.getUserToken(this.localContext);
393 this.localContext.setAttribute(ClientContext.USER_TOKEN, userToken);
394 }
395 if (userToken != null) {
396 managedConn.setState(userToken);
397 }
398 }
399 }
400 if (this.finalResponse != null) {
401 this.responseConsumer.responseReceived(response);
402 }
403 }
404
405 @Override
406 public synchronized void consumeContent(
407 final ContentDecoder decoder, final IOControl ioControl) throws IOException {
408 if (this.log.isDebugEnabled()) {
409 this.log.debug("[exchange: " + this.id + "] Consume content");
410 }
411 if (this.finalResponse != null) {
412 this.responseConsumer.consumeContent(decoder, ioControl);
413 } else {
414 if (this.tmpbuf == null) {
415 this.tmpbuf = ByteBuffer.allocate(2048);
416 }
417 this.tmpbuf.clear();
418 decoder.read(this.tmpbuf);
419 }
420 }
421
422 private void releaseConnection() {
423 if (this.managedConn != null) {
424 if (this.log.isDebugEnabled()) {
425 this.log.debug("[exchange: " + this.id + "] releasing connection " + this.managedConn);
426 }
427 try {
428 this.managedConn.getContext().removeAttribute(HttpAsyncRequestExecutor.HTTP_HANDLER);
429 this.managedConn.releaseConnection();
430 } catch (final IOException ioex) {
431 this.log.debug("I/O error releasing connection", ioex);
432 }
433 this.managedConn = null;
434 }
435 }
436
437 @Override
438 public synchronized void failed(final Exception ex) {
439 try {
440 if (!this.requestSent) {
441 this.requestProducer.failed(ex);
442 }
443 this.responseConsumer.failed(ex);
444 } finally {
445 try {
446 this.resultCallback.failed(ex, this);
447 } finally {
448 close();
449 }
450 }
451 }
452
453 @Override
454 public synchronized void responseCompleted(final HttpContext context) {
455 if (this.log.isDebugEnabled()) {
456 this.log.debug("[exchange: " + this.id + "] Response fully read");
457 }
458 try {
459 if (this.resultCallback.isDone()) {
460 return;
461 }
462 if (this.managedConn.isOpen()) {
463 final long duration = this.keepaliveStrategy.getKeepAliveDuration(
464 this.currentResponse, this.localContext);
465 if (this.log.isDebugEnabled()) {
466 final String s;
467 if (duration > 0) {
468 s = "for " + duration + " " + TimeUnit.MILLISECONDS;
469 } else {
470 s = "indefinitely";
471 }
472 this.log.debug("[exchange: " + this.id + "] Connection can be kept alive " + s);
473 }
474 this.managedConn.setIdleDuration(duration, TimeUnit.MILLISECONDS);
475 } else {
476 if (this.log.isDebugEnabled()) {
477 this.log.debug("[exchange: " + this.id + "] Connection cannot be kept alive");
478 }
479 this.managedConn.unmarkReusable();
480 if (this.proxyAuthState.getState() == AuthProtocolState.SUCCESS
481 && this.proxyAuthState.getAuthScheme() != null
482 && this.proxyAuthState.getAuthScheme().isConnectionBased()) {
483 if (this.log.isDebugEnabled()) {
484 this.log.debug("[exchange: " + this.id + "] Resetting proxy auth state");
485 }
486 this.proxyAuthState.reset();
487 }
488 if (this.targetAuthState.getState() == AuthProtocolState.SUCCESS
489 && this.targetAuthState.getAuthScheme() != null
490 && this.targetAuthState.getAuthScheme().isConnectionBased()) {
491 if (this.log.isDebugEnabled()) {
492 this.log.debug("[exchange: " + this.id + "] Resetting target auth state");
493 }
494 this.targetAuthState.reset();
495 }
496 }
497
498 if (this.finalResponse != null) {
499 this.responseConsumer.responseCompleted(this.localContext);
500 if (this.log.isDebugEnabled()) {
501 this.log.debug("[exchange: " + this.id + "] Response processed");
502 }
503 releaseConnection();
504 final T result = this.responseConsumer.getResult();
505 final Exception ex = this.responseConsumer.getException();
506 if (ex == null) {
507 this.resultCallback.completed(result, this);
508 } else {
509 this.resultCallback.failed(ex, this);
510 }
511 } else {
512 if (this.followup != null) {
513 final HttpRoute actualRoute = this.mainRequest.getRoute();
514 final HttpRoute newRoute = this.followup.getRoute();
515 if (!actualRoute.equals(newRoute)) {
516 releaseConnection();
517 }
518 this.mainRequest = this.followup;
519 }
520 if (this.managedConn != null && !this.managedConn.isOpen()) {
521 releaseConnection();
522 }
523 if (this.managedConn != null) {
524 this.managedConn.requestOutput();
525 } else {
526 requestConnection();
527 }
528 }
529 this.followup = null;
530 this.currentRequest = null;
531 this.currentResponse = null;
532 } catch (final RuntimeException runex) {
533 failed(runex);
534 throw runex;
535 }
536 }
537
538 @Override
539 public synchronized boolean cancel() {
540 if (this.log.isDebugEnabled()) {
541 this.log.debug("[exchange: " + this.id + "] Cancelled");
542 }
543 try {
544 final boolean cancelled = this.responseConsumer.cancel();
545
546 final T result = this.responseConsumer.getResult();
547 final Exception ex = this.responseConsumer.getException();
548 if (ex != null) {
549 this.resultCallback.failed(ex, this);
550 } else if (result != null) {
551 this.resultCallback.completed(result, this);
552 } else {
553 this.resultCallback.cancelled(this);
554 }
555 return cancelled;
556 } catch (final RuntimeException runex) {
557 this.resultCallback.failed(runex, this);
558 throw runex;
559 } finally {
560 close();
561 }
562 }
563
564 @Override
565 public boolean isDone() {
566 return this.resultCallback.isDone();
567 }
568
569 @Override
570 public T getResult() {
571 return this.responseConsumer.getResult();
572 }
573
574 @Override
575 public Exception getException() {
576 return this.responseConsumer.getException();
577 }
578
579 private synchronized void connectionRequestCompleted(final ManagedClientAsyncConnection conn) {
580 if (this.log.isDebugEnabled()) {
581 this.log.debug("[exchange: " + this.id + "] Connection allocated: " + conn);
582 }
583 this.connRequestCallback = null;
584 try {
585 this.managedConn = conn;
586 if (this.closed) {
587 conn.releaseConnection();
588 return;
589 }
590 final HttpRoute route = this.mainRequest.getRoute();
591 if (!conn.isOpen()) {
592 conn.open(route, this.localContext, this.params);
593 }
594 conn.getContext().setAttribute(HttpAsyncRequestExecutor.HTTP_HANDLER, this);
595 conn.requestOutput();
596 this.routeEstablished = route.equals(conn.getRoute());
597 if (!conn.isOpen()) {
598 throw new ConnectionClosedException("Connection closed");
599 }
600 } catch (final IOException ex) {
601 failed(ex);
602 } catch (final RuntimeException runex) {
603 failed(runex);
604 throw runex;
605 }
606 }
607
608 private synchronized void connectionRequestFailed(final Exception ex) {
609 if (this.log.isDebugEnabled()) {
610 this.log.debug("[exchange: " + this.id + "] connection request failed");
611 }
612 this.connRequestCallback = null;
613 try {
614 this.resultCallback.failed(ex, this);
615 } finally {
616 close();
617 }
618 }
619
620 private synchronized void connectionRequestCancelled() {
621 if (this.log.isDebugEnabled()) {
622 this.log.debug("[exchange: " + this.id + "] Connection request cancelled");
623 }
624 this.connRequestCallback = null;
625 try {
626 this.resultCallback.cancelled(this);
627 } finally {
628 close();
629 }
630 }
631
632 class InternalFutureCallback implements FutureCallback<ManagedClientAsyncConnection> {
633
634 @Override
635 public void completed(final ManagedClientAsyncConnection session) {
636 connectionRequestCompleted(session);
637 }
638
639 @Override
640 public void failed(final Exception ex) {
641 connectionRequestFailed(ex);
642 }
643
644 @Override
645 public void cancelled() {
646 connectionRequestCancelled();
647 }
648
649 }
650
651 private void requestConnection() {
652 final HttpRoute route = this.mainRequest.getRoute();
653 if (this.log.isDebugEnabled()) {
654 this.log.debug("[exchange: " + this.id + "] Request connection for " + route);
655 }
656 final long connectTimeout = HttpConnectionParams.getConnectionTimeout(this.params);
657 final Object userToken = this.localContext.getAttribute(ClientContext.USER_TOKEN);
658 this.connRequestCallback = new InternalFutureCallback();
659 this.connmgr.leaseConnection(
660 route, userToken,
661 connectTimeout, TimeUnit.MILLISECONDS,
662 this.connRequestCallback);
663 }
664
665 public synchronized void endOfStream() {
666 if (this.managedConn != null) {
667 if (this.log.isDebugEnabled()) {
668 this.log.debug("[exchange: " + this.id + "] Unexpected end of data stream");
669 }
670 releaseConnection();
671 if (this.connRequestCallback == null) {
672 requestConnection();
673 }
674 }
675 }
676
677 protected HttpRoute determineRoute(
678 final HttpHost target,
679 final HttpRequest request,
680 final HttpContext context) throws HttpException {
681 final HttpHost t = target != null ? target :
682 (HttpHost) request.getParams().getParameter(ClientPNames.DEFAULT_HOST);
683 if (t == null) {
684 throw new IllegalStateException("Target host could not be resolved");
685 }
686 return this.routePlanner.determineRoute(t, request, context);
687 }
688
689 private RequestWrapper wrapRequest(final HttpRequest request) throws ProtocolException {
690 return request instanceof HttpEntityEnclosingRequest
691 ? new EntityEnclosingRequestWrapper((HttpEntityEnclosingRequest) request)
692 : new RequestWrapper(request);
693 }
694
695 protected void rewriteRequestURI(
696 final RequestWrapper request, final HttpRoute route) throws ProtocolException {
697 try {
698 URI uri = request.getURI();
699 if (route.getProxyHost() != null && !route.isTunnelled()) {
700
701 if (!uri.isAbsolute()) {
702 final HttpHost target = route.getTargetHost();
703 uri = URIUtils.rewriteURI(uri, target);
704 request.setURI(uri);
705 }
706 } else {
707
708 if (uri.isAbsolute()) {
709 uri = URIUtils.rewriteURI(uri, null);
710 request.setURI(uri);
711 }
712 }
713 } catch (final URISyntaxException ex) {
714 throw new ProtocolException("Invalid URI: " + request.getRequestLine().getUri(), ex);
715 }
716 }
717
718 private AsyncSchemeRegistry getSchemeRegistry(final HttpContext context) {
719 AsyncSchemeRegistry./../../org/apache/http/nio/conn/scheme/AsyncSchemeRegistry.html#AsyncSchemeRegistry">AsyncSchemeRegistry reg = (AsyncSchemeRegistry) context.getAttribute(
720 ClientContext.SCHEME_REGISTRY);
721 if (reg == null) {
722 reg = this.connmgr.getSchemeRegistry();
723 }
724 return reg;
725 }
726
727 private HttpRequest createConnectRequest(final HttpRoute route) {
728
729
730
731 final HttpHost target = route.getTargetHost();
732 final String host = target.getHostName();
733 int port = target.getPort();
734 if (port < 0) {
735 final AsyncSchemeRegistry registry = getSchemeRegistry(this.localContext);
736 final AsyncScheme scheme = registry.getScheme(target.getSchemeName());
737 port = scheme.getDefaultPort();
738 }
739 final StringBuilder buffer = new StringBuilder(host.length() + 6);
740 buffer.append(host);
741 buffer.append(':');
742 buffer.append(Integer.toString(port));
743 final ProtocolVersion ver = HttpProtocolParams.getVersion(this.params);
744 final HttpRequest req = new BasicHttpRequest("CONNECT", buffer.toString(), ver);
745 return req;
746 }
747
748 private RoutedRequest handleResponse() throws HttpException {
749 RoutedRequest followup = null;
750 if (HttpClientParams.isAuthenticating(this.params)) {
751 final CredentialsProvider credsProvider = (CredentialsProvider) this.localContext.getAttribute(
752 ClientContext.CREDS_PROVIDER);
753 if (credsProvider != null) {
754 followup = handleTargetChallenge(credsProvider);
755 if (followup != null) {
756 return followup;
757 }
758 followup = handleProxyChallenge(credsProvider);
759 if (followup != null) {
760 return followup;
761 }
762 }
763 }
764 if (HttpClientParams.isRedirecting(this.params)) {
765 followup = handleRedirect();
766 if (followup != null) {
767 return followup;
768 }
769 }
770 return null;
771 }
772
773 private RoutedRequest handleConnectResponse() {
774 RoutedRequest followup = null;
775 if (HttpClientParams.isAuthenticating(this.params)) {
776 final CredentialsProvider credsProvider = (CredentialsProvider) this.localContext.getAttribute(
777 ClientContext.CREDS_PROVIDER);
778 if (credsProvider != null) {
779 followup = handleProxyChallenge(credsProvider);
780 if (followup != null) {
781 return followup;
782 }
783 }
784 }
785 return null;
786 }
787
788 private RoutedRequest handleRedirect() throws HttpException {
789 if (this.redirectStrategy.isRedirected(
790 this.currentRequest, this.currentResponse, this.localContext)) {
791
792 final HttpRoute route = this.mainRequest.getRoute();
793 final RequestWrapper request = this.mainRequest.getRequest();
794
795 final int maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100);
796 if (this.redirectCount >= maxRedirects) {
797 throw new RedirectException("Maximum redirects ("
798 + maxRedirects + ") exceeded");
799 }
800 this.redirectCount++;
801
802 final HttpUriRequest redirect = this.redirectStrategy.getRedirect(
803 this.currentRequest, this.currentResponse, this.localContext);
804 final HttpRequest orig = request.getOriginal();
805 redirect.setHeaders(orig.getAllHeaders());
806
807 final URI uri = redirect.getURI();
808 if (uri.getHost() == null) {
809 throw new ProtocolException("Redirect URI does not specify a valid host name: " + uri);
810 }
811 final HttpHost newTarget = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
812
813
814 if (!route.getTargetHost().equals(newTarget)) {
815 if (this.log.isDebugEnabled()) {
816 this.log.debug("[exchange: " + this.id + "] Resetting target auth state");
817 }
818 this.targetAuthState.reset();
819 final AuthScheme authScheme = this.proxyAuthState.getAuthScheme();
820 if (authScheme != null && authScheme.isConnectionBased()) {
821 if (this.log.isDebugEnabled()) {
822 this.log.debug("[exchange: " + this.id + "] Resetting proxy auth state");
823 }
824 this.proxyAuthState.reset();
825 }
826 }
827
828 final RequestWrapper newRequest = wrapRequest(redirect);
829 newRequest.setParams(this.params);
830
831 final HttpRoute newRoute = determineRoute(newTarget, newRequest, this.localContext);
832
833 if (this.log.isDebugEnabled()) {
834 this.log.debug("[exchange: " + this.id + "] Redirecting to '" + uri + "' via " + newRoute);
835 }
836 return new RoutedRequest(newRequest, newRoute);
837 }
838 return null;
839 }
840
841 private RoutedRequest handleTargetChallenge(
842 final CredentialsProvider credsProvider) {
843 final HttpRoute route = this.mainRequest.getRoute();
844 HttpHost target = (HttpHost) this.localContext.getAttribute(
845 ExecutionContext.HTTP_TARGET_HOST);
846 if (target == null) {
847 target = route.getTargetHost();
848 }
849 if (this.authenticator.isAuthenticationRequested(target, this.currentResponse,
850 this.targetAuthStrategy, this.targetAuthState, this.localContext)) {
851 if (this.authenticator.authenticate(target, this.currentResponse,
852 this.targetAuthStrategy, this.targetAuthState, this.localContext)) {
853
854 return this.mainRequest;
855 }
856 return null;
857 }
858 return null;
859 }
860
861 private RoutedRequest handleProxyChallenge(
862 final CredentialsProvider credsProvider) {
863 final HttpRoute route = this.mainRequest.getRoute();
864 final HttpHost proxy = route.getProxyHost();
865 if (this.authenticator.isAuthenticationRequested(proxy, this.currentResponse,
866 this.proxyAuthStrategy, this.proxyAuthState, this.localContext)) {
867 if (this.authenticator.authenticate(proxy, this.currentResponse,
868 this.proxyAuthStrategy, this.proxyAuthState, this.localContext)) {
869
870 return this.mainRequest;
871 }
872 return null;
873 }
874 return null;
875 }
876
877 @Override
878 public HttpContext getContext() {
879 return this.localContext;
880 }
881
882 @Override
883 public HttpProcessor getHttpProcessor() {
884 return this.httpPocessor;
885 }
886
887 @Override
888 public ConnectionReuseStrategy getConnectionReuseStrategy() {
889 return this.reuseStrategy;
890 }
891
892 }