View Javadoc

1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.nio.client.integration;
28  
29  import java.io.IOException;
30  import java.net.InetSocketAddress;
31  import java.net.URISyntaxException;
32  import java.util.ArrayList;
33  import java.util.List;
34  import java.util.Queue;
35  import java.util.concurrent.ConcurrentLinkedQueue;
36  import java.util.concurrent.ExecutionException;
37  import java.util.concurrent.Future;
38  
39  import org.apache.http.Header;
40  import org.apache.http.HttpAsyncTestBase;
41  import org.apache.http.HttpException;
42  import org.apache.http.HttpHost;
43  import org.apache.http.HttpInetConnection;
44  import org.apache.http.HttpRequest;
45  import org.apache.http.HttpResponse;
46  import org.apache.http.HttpStatus;
47  import org.apache.http.ProtocolException;
48  import org.apache.http.ProtocolVersion;
49  import org.apache.http.client.CircularRedirectException;
50  import org.apache.http.client.CookieStore;
51  import org.apache.http.client.RedirectException;
52  import org.apache.http.client.config.RequestConfig;
53  import org.apache.http.client.methods.HttpGet;
54  import org.apache.http.client.methods.HttpPost;
55  import org.apache.http.client.protocol.HttpClientContext;
56  import org.apache.http.client.utils.URIBuilder;
57  import org.apache.http.config.ConnectionConfig;
58  import org.apache.http.cookie.SM;
59  import org.apache.http.entity.StringEntity;
60  import org.apache.http.impl.DefaultConnectionReuseStrategy;
61  import org.apache.http.impl.DefaultHttpResponseFactory;
62  import org.apache.http.impl.client.BasicCookieStore;
63  import org.apache.http.impl.cookie.BasicClientCookie;
64  import org.apache.http.impl.nio.DefaultNHttpServerConnection;
65  import org.apache.http.impl.nio.DefaultNHttpServerConnectionFactory;
66  import org.apache.http.impl.nio.client.HttpAsyncClients;
67  import org.apache.http.localserver.HttpServerNio;
68  import org.apache.http.localserver.RandomHandler;
69  import org.apache.http.message.BasicHeader;
70  import org.apache.http.nio.NHttpConnectionFactory;
71  import org.apache.http.nio.entity.NStringEntity;
72  import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
73  import org.apache.http.nio.protocol.HttpAsyncExpectationVerifier;
74  import org.apache.http.nio.protocol.HttpAsyncRequestHandlerMapper;
75  import org.apache.http.nio.protocol.HttpAsyncService;
76  import org.apache.http.nio.protocol.UriHttpAsyncRequestHandlerMapper;
77  import org.apache.http.nio.reactor.IOReactorStatus;
78  import org.apache.http.nio.reactor.ListenerEndpoint;
79  import org.apache.http.protocol.HTTP;
80  import org.apache.http.protocol.HttpContext;
81  import org.apache.http.protocol.HttpCoreContext;
82  import org.apache.http.protocol.HttpRequestHandler;
83  import org.junit.After;
84  import org.junit.Assert;
85  import org.junit.Before;
86  import org.junit.Test;
87  
88  /**
89   * Redirection test cases.
90   */
91  public class TestRedirects extends HttpAsyncTestBase {
92  
93      @Before
94      public void setUp() throws Exception {
95          initServer();
96          initConnectionManager();
97          this.httpclient = HttpAsyncClients.custom()
98                  .setConnectionManager(this.connMgr)
99                  .build();
100     }
101 
102     @After
103     public void tearDown() throws Exception {
104         shutDownClient();
105         shutDownServer();
106     }
107 
108     @Override
109     protected NHttpConnectionFactory<DefaultNHttpServerConnection> createServerConnectionFactory(
110             final ConnectionConfig config) throws Exception {
111         return new DefaultNHttpServerConnectionFactory(config);
112     }
113 
114     @Override
115     protected String getSchemeName() {
116         return "http";
117     }
118 
119     private HttpHost start(
120             final HttpAsyncRequestHandlerMapper requestHandlerResolver,
121             final HttpAsyncExpectationVerifier expectationVerifier) throws Exception {
122         final HttpAsyncService serviceHandler = new HttpAsyncService(
123                 this.serverHttpProc,
124                 DefaultConnectionReuseStrategy.INSTANCE,
125                 DefaultHttpResponseFactory.INSTANCE,
126                 requestHandlerResolver,
127                 expectationVerifier);
128         this.server.start(serviceHandler);
129         this.httpclient.start();
130 
131         final ListenerEndpoint endpoint = this.server.getListenerEndpoint();
132         endpoint.waitFor();
133 
134         Assert.assertEquals("Test server status", IOReactorStatus.ACTIVE, this.server.getStatus());
135         final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
136         return new HttpHost("localhost", address.getPort(), getSchemeName());
137     }
138 
139     static class BasicRedirectService implements HttpRequestHandler {
140 
141         private final String schemeName;
142         private final int statuscode;
143 
144         public BasicRedirectService(final String schemeName, final int statuscode) {
145             super();
146             this.schemeName = schemeName;
147             this.statuscode = statuscode;
148         }
149 
150         public void handle(
151                 final HttpRequest request,
152                 final HttpResponse response,
153                 final HttpContext context) throws HttpException, IOException {
154             final HttpInetConnection conn = (HttpInetConnection) context.getAttribute(
155                     HttpCoreContext.HTTP_CONNECTION);
156             final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
157             final String uri = request.getRequestLine().getUri();
158             if (uri.equals("/oldlocation/")) {
159                 final String redirectUrl = this.schemeName + "://localhost:" + conn.getLocalPort() + "/newlocation/";
160                 response.setStatusLine(ver, this.statuscode);
161                 response.addHeader(new BasicHeader("Location", redirectUrl));
162                 response.addHeader(new BasicHeader("Connection", "close"));
163             } else if (uri.equals("/newlocation/")) {
164                 response.setStatusLine(ver, HttpStatus.SC_OK);
165                 final StringEntity entity = new StringEntity("Successful redirect");
166                 response.setEntity(entity);
167             } else {
168                 response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
169             }
170         }
171     }
172 
173     static class CircularRedirectService implements HttpRequestHandler {
174 
175         public CircularRedirectService() {
176             super();
177         }
178 
179         public void handle(
180                 final HttpRequest request,
181                 final HttpResponse response,
182                 final HttpContext context) throws HttpException, IOException {
183             final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
184             final String uri = request.getRequestLine().getUri();
185             if (uri.startsWith("/circular-oldlocation")) {
186                 response.setStatusLine(ver, HttpStatus.SC_MOVED_TEMPORARILY);
187                 response.addHeader(new BasicHeader("Location", "/circular-location2"));
188             } else if (uri.startsWith("/circular-location2")) {
189                 response.setStatusLine(ver, HttpStatus.SC_MOVED_TEMPORARILY);
190                 response.addHeader(new BasicHeader("Location", "/circular-oldlocation"));
191             } else {
192                 response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
193             }
194         }
195     }
196 
197     static class RelativeRedirectService implements HttpRequestHandler {
198 
199         public RelativeRedirectService() {
200             super();
201         }
202 
203         public void handle(
204                 final HttpRequest request,
205                 final HttpResponse response,
206                 final HttpContext context) throws HttpException, IOException {
207             final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
208             final String uri = request.getRequestLine().getUri();
209             if (uri.equals("/oldlocation/")) {
210                 response.setStatusLine(ver, HttpStatus.SC_MOVED_TEMPORARILY);
211                 response.addHeader(new BasicHeader("Location", "/relativelocation/"));
212             } else if (uri.equals("/relativelocation/")) {
213                 response.setStatusLine(ver, HttpStatus.SC_OK);
214                 final StringEntity entity = new StringEntity("Successful redirect");
215                 response.setEntity(entity);
216             } else {
217                 response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
218             }
219         }
220     }
221 
222     static class RelativeRedirectService2 implements HttpRequestHandler {
223 
224         public RelativeRedirectService2() {
225             super();
226         }
227 
228         public void handle(
229                 final HttpRequest request,
230                 final HttpResponse response,
231                 final HttpContext context) throws HttpException, IOException {
232             final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
233             final String uri = request.getRequestLine().getUri();
234             if (uri.equals("/test/oldlocation")) {
235                 response.setStatusLine(ver, HttpStatus.SC_MOVED_TEMPORARILY);
236                 response.addHeader(new BasicHeader("Location", "relativelocation"));
237             } else if (uri.equals("/test/relativelocation")) {
238                 response.setStatusLine(ver, HttpStatus.SC_OK);
239                 final StringEntity entity = new StringEntity("Successful redirect");
240                 response.setEntity(entity);
241             } else {
242                 response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
243             }
244         }
245     }
246 
247     static class BogusRedirectService implements HttpRequestHandler {
248 
249         private final String schemeName;
250         private final String url;
251         private final boolean absolute;
252 
253         public BogusRedirectService(final String schemeName, final String url, final boolean absolute) {
254             super();
255             this.schemeName = schemeName;
256             this.url = url;
257             this.absolute = absolute;
258         }
259 
260         public void handle(
261                 final HttpRequest request,
262                 final HttpResponse response,
263                 final HttpContext context) throws HttpException, IOException {
264             final HttpInetConnection conn = (HttpInetConnection) context.getAttribute(
265                     HttpCoreContext.HTTP_CONNECTION);
266             String redirectUrl = this.url;
267             if (!this.absolute) {
268                 redirectUrl = this.schemeName + "://localhost:" + conn.getLocalPort() + redirectUrl;
269             }
270 
271             final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
272             final String uri = request.getRequestLine().getUri();
273             if (uri.equals("/oldlocation/")) {
274                 response.setStatusLine(ver, HttpStatus.SC_MOVED_TEMPORARILY);
275                 response.addHeader(new BasicHeader("Location", redirectUrl));
276             } else if (uri.equals("/relativelocation/")) {
277                 response.setStatusLine(ver, HttpStatus.SC_OK);
278                 final StringEntity entity = new StringEntity("Successful redirect");
279                 response.setEntity(entity);
280             } else {
281                 response.setStatusLine(ver, HttpStatus.SC_NOT_FOUND);
282             }
283         }
284     }
285 
286     private static class RomeRedirectService implements HttpRequestHandler {
287 
288         public RomeRedirectService() {
289             super();
290         }
291 
292         public void handle(
293                 final HttpRequest request,
294                 final HttpResponse response,
295                 final HttpContext context) throws HttpException, IOException {
296             final String uri = request.getRequestLine().getUri();
297             if (uri.equals("/rome")) {
298                 response.setStatusCode(HttpStatus.SC_OK);
299                 final StringEntity entity = new StringEntity("Successful redirect");
300                 response.setEntity(entity);
301             } else {
302                 response.setStatusCode(HttpStatus.SC_MOVED_TEMPORARILY);
303                 response.addHeader(new BasicHeader("Location", "/rome"));
304             }
305         }
306     }
307 
308     @Test
309     public void testBasicRedirect300() throws Exception {
310         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
311         registry.register("*", new BasicAsyncRequestHandler(
312                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_MULTIPLE_CHOICES)));
313         final HttpHost target = start(registry, null);
314 
315         final HttpClientContext context = HttpClientContext.create();
316 
317         final HttpGet httpget = new HttpGet("/oldlocation/");
318 
319         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
320         final HttpResponse response = future.get();
321         Assert.assertNotNull(response);
322 
323         final HttpRequest reqWrapper = context.getRequest();
324 
325         Assert.assertEquals(HttpStatus.SC_MULTIPLE_CHOICES, response.getStatusLine().getStatusCode());
326         Assert.assertEquals("/oldlocation/", reqWrapper.getRequestLine().getUri());
327     }
328 
329     @Test
330     public void testBasicRedirect301() throws Exception {
331         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
332         registry.register("*", new BasicAsyncRequestHandler(
333                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_MOVED_PERMANENTLY)));
334         final HttpHost target = start(registry, null);
335 
336         final HttpClientContext context = HttpClientContext.create();
337 
338         final HttpGet httpget = new HttpGet("/oldlocation/");
339 
340         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
341         final HttpResponse response = future.get();
342         Assert.assertNotNull(response);
343 
344         final HttpRequest reqWrapper = context.getRequest();
345         final HttpHost host = context.getTargetHost();
346 
347         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
348         Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
349         Assert.assertEquals(target, host);
350     }
351 
352     @Test
353     public void testBasicRedirect302() throws Exception {
354         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
355         registry.register("*", new BasicAsyncRequestHandler(
356                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_MOVED_TEMPORARILY)));
357         final HttpHost target = start(registry, null);
358 
359         final HttpClientContext context = HttpClientContext.create();
360 
361         final HttpGet httpget = new HttpGet("/oldlocation/");
362 
363         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
364         final HttpResponse response = future.get();
365         Assert.assertNotNull(response);
366 
367         final HttpRequest reqWrapper = context.getRequest();
368         final HttpHost host = context.getTargetHost();
369 
370         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
371         Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
372         Assert.assertEquals(target, host);
373     }
374 
375     @Test
376     public void testBasicRedirect302NoLocation() throws Exception {
377         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
378         registry.register("*", new BasicAsyncRequestHandler(new HttpRequestHandler() {
379 
380             public void handle(
381                     final HttpRequest request,
382                     final HttpResponse response,
383                     final HttpContext context) throws HttpException, IOException {
384                 response.setStatusCode(HttpStatus.SC_MOVED_TEMPORARILY);
385             }
386 
387         }));
388         final HttpHost target = start(registry, null);
389 
390         final HttpClientContext context = HttpClientContext.create();
391 
392         final HttpGet httpget = new HttpGet("/oldlocation/");
393 
394         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
395         final HttpResponse response = future.get();
396         Assert.assertNotNull(response);
397 
398         final HttpRequest reqWrapper = context.getRequest();
399         final HttpHost host = context.getTargetHost();
400 
401         Assert.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
402         Assert.assertEquals("/oldlocation/", reqWrapper.getRequestLine().getUri());
403         Assert.assertEquals(target, host);
404     }
405 
406     @Test
407     public void testBasicRedirect303() throws Exception {
408         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
409         registry.register("*", new BasicAsyncRequestHandler(
410                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_SEE_OTHER)));
411         final HttpHost target = start(registry, null);
412 
413         final HttpClientContext context = HttpClientContext.create();
414 
415         final HttpGet httpget = new HttpGet("/oldlocation/");
416 
417         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
418         final HttpResponse response = future.get();
419         Assert.assertNotNull(response);
420 
421         final HttpRequest reqWrapper = context.getRequest();
422         final HttpHost host = context.getTargetHost();
423 
424         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
425         Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
426         Assert.assertEquals(target, host);
427     }
428 
429     @Test
430     public void testBasicRedirect304() throws Exception {
431         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
432         registry.register("*", new BasicAsyncRequestHandler(
433                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_NOT_MODIFIED)));
434         final HttpHost target = start(registry, null);
435 
436         final HttpClientContext context = HttpClientContext.create();
437 
438         final HttpGet httpget = new HttpGet("/oldlocation/");
439 
440         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
441         final HttpResponse response = future.get();
442         Assert.assertNotNull(response);
443 
444         final HttpRequest reqWrapper = context.getRequest();
445 
446         Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, response.getStatusLine().getStatusCode());
447         Assert.assertEquals("/oldlocation/", reqWrapper.getRequestLine().getUri());
448     }
449 
450     @Test
451     public void testBasicRedirect305() throws Exception {
452         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
453         registry.register("*", new BasicAsyncRequestHandler(
454                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_USE_PROXY)));
455         final HttpHost target = start(registry, null);
456 
457         final HttpClientContext context = HttpClientContext.create();
458 
459         final HttpGet httpget = new HttpGet("/oldlocation/");
460 
461         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
462         final HttpResponse response = future.get();
463         Assert.assertNotNull(response);
464 
465         final HttpRequest reqWrapper = context.getRequest();
466 
467         Assert.assertEquals(HttpStatus.SC_USE_PROXY, response.getStatusLine().getStatusCode());
468         Assert.assertEquals("/oldlocation/", reqWrapper.getRequestLine().getUri());
469     }
470 
471     @Test
472     public void testBasicRedirect307() throws Exception {
473         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
474         registry.register("*", new BasicAsyncRequestHandler(
475                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_TEMPORARY_REDIRECT)));
476         final HttpHost target = start(registry, null);
477 
478         final HttpClientContext context = HttpClientContext.create();
479 
480         final HttpGet httpget = new HttpGet("/oldlocation/");
481 
482         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
483         final HttpResponse response = future.get();
484         Assert.assertNotNull(response);
485 
486         final HttpRequest reqWrapper = context.getRequest();
487         final HttpHost host = context.getTargetHost();
488 
489         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
490         Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
491         Assert.assertEquals(target, host);
492     }
493 
494     @Test(expected=ExecutionException.class)
495     public void testMaxRedirectCheck() throws Exception {
496         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
497         registry.register("*", new BasicAsyncRequestHandler(new CircularRedirectService()));
498         final HttpHost target = start(registry, null);
499 
500         final RequestConfig config = RequestConfig.custom()
501                 .setCircularRedirectsAllowed(true)
502                 .setMaxRedirects(5).build();
503 
504         final HttpGet httpget = new HttpGet("/circular-oldlocation/");
505         httpget.setConfig(config);
506         try {
507             final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
508             future.get();
509         } catch (final ExecutionException e) {
510             Assert.assertTrue(e.getCause() instanceof RedirectException);
511             throw e;
512         }
513     }
514 
515     @Test(expected=ExecutionException.class)
516     public void testCircularRedirect() throws Exception {
517         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
518         registry.register("*", new BasicAsyncRequestHandler(new CircularRedirectService()));
519         final HttpHost target = start(registry, null);
520 
521         final RequestConfig config = RequestConfig.custom()
522                 .setCircularRedirectsAllowed(false)
523                 .setRelativeRedirectsAllowed(true)
524                 .build();
525 
526         final HttpGet httpget = new HttpGet("/circular-oldlocation/");
527         httpget.setConfig(config);
528         try {
529             final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
530             future.get();
531         } catch (final ExecutionException e) {
532             Assert.assertTrue(e.getCause() instanceof CircularRedirectException);
533             throw e;
534         }
535     }
536 
537     @Test
538     public void testPostNoRedirect() throws Exception {
539         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
540         registry.register("*", new BasicAsyncRequestHandler(
541                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_MOVED_TEMPORARILY)));
542         final HttpHost target = start(registry, null);
543 
544         final HttpClientContext context = HttpClientContext.create();
545 
546         final HttpPost httppost = new HttpPost("/oldlocation/");
547         httppost.setEntity(new NStringEntity("stuff"));
548 
549         final Future<HttpResponse> future = this.httpclient.execute(target, httppost, context, null);
550         final HttpResponse response = future.get();
551         Assert.assertNotNull(response);
552 
553         final HttpRequest reqWrapper = context.getRequest();
554 
555         Assert.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
556         Assert.assertEquals("/oldlocation/", reqWrapper.getRequestLine().getUri());
557         Assert.assertEquals("POST", reqWrapper.getRequestLine().getMethod());
558     }
559 
560     @Test
561     public void testPostRedirectSeeOther() throws Exception {
562         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
563         registry.register("*", new BasicAsyncRequestHandler(
564                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_SEE_OTHER)));
565         final HttpHost target = start(registry, null);
566 
567         final HttpClientContext context = HttpClientContext.create();
568 
569         final HttpPost httppost = new HttpPost("/oldlocation/");
570         httppost.setEntity(new NStringEntity("stuff"));
571 
572         final Future<HttpResponse> future = this.httpclient.execute(target, httppost, context, null);
573         final HttpResponse response = future.get();
574         Assert.assertNotNull(response);
575 
576         final HttpRequest reqWrapper = context.getRequest();
577 
578         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
579         Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
580         Assert.assertEquals("GET", reqWrapper.getRequestLine().getMethod());
581     }
582 
583     @Test
584     public void testRelativeRedirect() throws Exception {
585         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
586         registry.register("*", new BasicAsyncRequestHandler(new RelativeRedirectService()));
587         final HttpHost target = start(registry, null);
588 
589         final HttpClientContext context = HttpClientContext.create();
590 
591         final RequestConfig config = RequestConfig.custom()
592                 .setRelativeRedirectsAllowed(true)
593                 .build();
594 
595         final HttpGet httpget = new HttpGet("/oldlocation/");
596         httpget.setConfig(config);
597 
598         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
599         final HttpResponse response = future.get();
600         Assert.assertNotNull(response);
601 
602         final HttpRequest reqWrapper = context.getRequest();
603         final HttpHost host = context.getTargetHost();
604 
605         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
606         Assert.assertEquals("/relativelocation/", reqWrapper.getRequestLine().getUri());
607         Assert.assertEquals(target, host);
608     }
609 
610     @Test
611     public void testRelativeRedirect2() throws Exception {
612         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
613         registry.register("*", new BasicAsyncRequestHandler(new RelativeRedirectService2()));
614         final HttpHost target = start(registry, null);
615 
616         final HttpClientContext context = HttpClientContext.create();
617 
618         final RequestConfig config = RequestConfig.custom()
619                 .setRelativeRedirectsAllowed(true)
620                 .build();
621 
622         final HttpGet httpget = new HttpGet("/test/oldlocation");
623         httpget.setConfig(config);
624 
625         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
626         final HttpResponse response = future.get();
627         Assert.assertNotNull(response);
628 
629         final HttpRequest reqWrapper = context.getRequest();
630         final HttpHost host = context.getTargetHost();
631 
632         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
633         Assert.assertEquals("/test/relativelocation", reqWrapper.getRequestLine().getUri());
634         Assert.assertEquals(target, host);
635     }
636 
637     @Test(expected=ExecutionException.class)
638     public void testRejectRelativeRedirect() throws Exception {
639         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
640         registry.register("*", new BasicAsyncRequestHandler(new RelativeRedirectService()));
641         final HttpHost target = start(registry, null);
642 
643         final RequestConfig config = RequestConfig.custom()
644                 .setRelativeRedirectsAllowed(false)
645                 .build();
646 
647         final HttpGet httpget = new HttpGet("/oldlocation/");
648         httpget.setConfig(config);
649         try {
650             final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
651             future.get();
652         } catch (final ExecutionException e) {
653             Assert.assertTrue(e.getCause() instanceof ProtocolException);
654             throw e;
655         }
656     }
657 
658     @Test(expected=ExecutionException.class)
659     public void testRejectBogusRedirectLocation() throws Exception {
660         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
661         registry.register("*", new BasicAsyncRequestHandler(
662                 new BogusRedirectService(getSchemeName(), "xxx://bogus", true)));
663         final HttpHost target = start(registry, null);
664 
665         final HttpGet httpget = new HttpGet("/oldlocation/");
666 
667         try {
668             final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
669             future.get();
670         } catch (final ExecutionException ex) {
671             Assert.assertTrue(ex.getCause() instanceof HttpException);
672             throw ex;
673         }
674     }
675 
676     @Test(expected=ExecutionException.class)
677     public void testRejectInvalidRedirectLocation() throws Exception {
678         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
679         registry.register("*", new BasicAsyncRequestHandler(
680                 new BogusRedirectService(getSchemeName(), "/newlocation/?p=I have spaces", false)));
681         final HttpHost target = start(registry, null);
682 
683         final HttpGet httpget = new HttpGet("/oldlocation/");
684         try {
685             final Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
686             future.get();
687         } catch (final ExecutionException e) {
688             Assert.assertTrue(e.getCause() instanceof ProtocolException);
689             throw e;
690         }
691     }
692 
693     @Test
694     public void testRedirectWithCookie() throws Exception {
695         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
696         registry.register("*", new BasicAsyncRequestHandler(
697                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_MOVED_TEMPORARILY)));
698         final HttpHost target = start(registry, null);
699 
700         final CookieStore cookieStore = new BasicCookieStore();
701         final HttpClientContext context = HttpClientContext.create();
702         context.setCookieStore(cookieStore);
703 
704         final BasicClientCookie cookie = new BasicClientCookie("name", "value");
705         cookie.setDomain(target.getHostName());
706         cookie.setPath("/");
707 
708         cookieStore.addCookie(cookie);
709 
710         final HttpGet httpget = new HttpGet("/oldlocation/");
711 
712         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
713         final HttpResponse response = future.get();
714         Assert.assertNotNull(response);
715 
716         final HttpRequest reqWrapper = context.getRequest();
717 
718         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
719         Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
720 
721         final Header[] headers = reqWrapper.getHeaders(SM.COOKIE);
722         Assert.assertEquals("There can only be one (cookie)", 1, headers.length);
723     }
724 
725     @Test
726     public void testDefaultHeadersRedirect() throws Exception {
727         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
728         registry.register("*", new BasicAsyncRequestHandler(
729                 new BasicRedirectService(getSchemeName(), HttpStatus.SC_MOVED_TEMPORARILY)));
730 
731         final List<Header> defaultHeaders = new ArrayList<Header>(1);
732         defaultHeaders.add(new BasicHeader(HTTP.USER_AGENT, "my-test-client"));
733 
734         this.httpclient = HttpAsyncClients.custom()
735                 .setConnectionManager(this.connMgr)
736                 .setDefaultHeaders(defaultHeaders)
737                 .build();
738 
739         final HttpHost target = start(registry, null);
740 
741         final HttpClientContext context = HttpClientContext.create();
742 
743         final HttpGet httpget = new HttpGet("/oldlocation/");
744 
745         final Future<HttpResponse> future = this.httpclient.execute(target, httpget, context, null);
746         final HttpResponse response = future.get();
747         Assert.assertNotNull(response);
748 
749         final HttpRequest reqWrapper = context.getRequest();
750 
751         Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
752         Assert.assertEquals("/newlocation/", reqWrapper.getRequestLine().getUri());
753 
754         final Header header = reqWrapper.getFirstHeader(HTTP.USER_AGENT);
755         Assert.assertEquals("my-test-client", header.getValue());
756     }
757 
758     static class CrossSiteRedirectService implements HttpRequestHandler {
759 
760         private final HttpHost host;
761 
762         public CrossSiteRedirectService(final HttpHost host) {
763             super();
764             this.host = host;
765         }
766 
767         public void handle(
768                 final HttpRequest request,
769                 final HttpResponse response,
770                 final HttpContext context) throws HttpException, IOException {
771             final ProtocolVersion ver = request.getRequestLine().getProtocolVersion();
772             final String location;
773             try {
774                 final URIBuilder uribuilder = new URIBuilder(request.getRequestLine().getUri());
775                 uribuilder.setScheme(this.host.getSchemeName());
776                 uribuilder.setHost(this.host.getHostName());
777                 uribuilder.setPort(this.host.getPort());
778                 uribuilder.setPath("/random/1024");
779                 location = uribuilder.build().toASCIIString();
780             } catch (final URISyntaxException ex) {
781                 throw new ProtocolException("Invalid request URI", ex);
782             }
783             response.setStatusLine(ver, HttpStatus.SC_TEMPORARY_REDIRECT);
784             response.addHeader(new BasicHeader("Location", location));
785         }
786     }
787 
788     @Test
789     public void testCrossSiteRedirect() throws Exception {
790         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
791         registry.register("/random/*", new BasicAsyncRequestHandler(
792                 new RandomHandler()));
793         final HttpHost redirectTarget = start(registry, null);
794 
795         final UriHttpAsyncRequestHandlerMapper registry2 = new UriHttpAsyncRequestHandlerMapper();
796         registry2.register("/redirect/*", new BasicAsyncRequestHandler(
797                 new CrossSiteRedirectService(redirectTarget)));
798         final HttpServerNio secondServer = new HttpServerNio(this.serverReactorConfig,
799                 createServerConnectionFactory(this.serverConnectionConfig));
800         secondServer.setExceptionHandler(new SimpleIOReactorExceptionHandler());
801         final HttpAsyncService serviceHandler = new HttpAsyncService(
802                 this.serverHttpProc,
803                 new DefaultConnectionReuseStrategy(),
804                 new DefaultHttpResponseFactory(),
805                 registry2,
806                 null);
807         secondServer.start(serviceHandler);
808         try {
809             final ListenerEndpoint endpoint2 = secondServer.getListenerEndpoint();
810             endpoint2.waitFor();
811 
812             Assert.assertEquals("Test server status", IOReactorStatus.ACTIVE, secondServer.getStatus());
813             final InetSocketAddress address2 = (InetSocketAddress) endpoint2.getAddress();
814             final HttpHost initialTarget = new HttpHost("localhost", address2.getPort(), getSchemeName());
815 
816             final Queue<Future<HttpResponse>> queue = new ConcurrentLinkedQueue<Future<HttpResponse>>();
817             for (int i = 0; i < 4; i++) {
818                 final HttpClientContext context = HttpClientContext.create();
819                 final HttpGet httpget = new HttpGet("/redirect/anywhere");
820                 queue.add(this.httpclient.execute(initialTarget, httpget, context, null));
821             }
822             while (!queue.isEmpty()) {
823                 final Future<HttpResponse> future = queue.remove();
824                 final HttpResponse response = future.get();
825                 Assert.assertNotNull(response);
826                 Assert.assertEquals(200, response.getStatusLine().getStatusCode());
827             }
828         } finally {
829             this.server.shutdown();
830         }
831     }
832 
833     @Test
834     public void testRepeatRequest() throws Exception {
835         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
836         registry.register("*", new BasicAsyncRequestHandler(new RomeRedirectService()));
837         final HttpHost target = start(registry, null);
838 
839         final HttpClientContext context = HttpClientContext.create();
840 
841         final RequestConfig config = RequestConfig.custom().setRelativeRedirectsAllowed(true).build();
842         final HttpGet first = new HttpGet("/rome");
843         first.setConfig(config);
844 
845         final Future<HttpResponse> future1 = this.httpclient.execute(target, first, context, null);
846         final HttpResponse response1 = future1.get();
847         Assert.assertNotNull(response1);
848 
849         final HttpGet second = new HttpGet("/rome");
850         second.setConfig(config);
851 
852         final Future<HttpResponse> future2 = this.httpclient.execute(target, second, context, null);
853         final HttpResponse response2 = future2.get();
854         Assert.assertNotNull(response2);
855 
856         final HttpRequest reqWrapper = context.getRequest();
857         final HttpHost host = context.getTargetHost();
858 
859         Assert.assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
860         Assert.assertEquals("/rome", reqWrapper.getRequestLine().getUri());
861         Assert.assertEquals(host, target);
862     }
863 
864     @Test
865     public void testRepeatRequestRedirect() throws Exception {
866         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
867         registry.register("*", new BasicAsyncRequestHandler(new RomeRedirectService()));
868         final HttpHost target = start(registry, null);
869 
870         final HttpClientContext context = HttpClientContext.create();
871 
872         final RequestConfig config = RequestConfig.custom().setRelativeRedirectsAllowed(true).build();
873         final HttpGet first = new HttpGet("/lille");
874         first.setConfig(config);
875 
876         final Future<HttpResponse> future1 = this.httpclient.execute(target, first, context, null);
877         final HttpResponse response1 = future1.get();
878         Assert.assertNotNull(response1);
879 
880         final HttpGet second = new HttpGet("/lille");
881         second.setConfig(config);
882 
883         final Future<HttpResponse> future2 = this.httpclient.execute(target, second, context, null);
884         final HttpResponse response2 = future2.get();
885         Assert.assertNotNull(response2);
886 
887         final HttpRequest reqWrapper = context.getRequest();
888         final HttpHost host = context.getTargetHost();
889 
890         Assert.assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
891         Assert.assertEquals("/rome", reqWrapper.getRequestLine().getUri());
892         Assert.assertEquals(host, target);
893     }
894 
895     @Test
896     public void testDifferentRequestSameRedirect() throws Exception {
897         final UriHttpAsyncRequestHandlerMapper registry = new UriHttpAsyncRequestHandlerMapper();
898         registry.register("*", new BasicAsyncRequestHandler(new RomeRedirectService()));
899         final HttpHost target = start(registry, null);
900 
901         final HttpClientContext context = HttpClientContext.create();
902 
903         final RequestConfig config = RequestConfig.custom().setRelativeRedirectsAllowed(true).build();
904         final HttpGet first = new HttpGet("/alian");
905         first.setConfig(config);
906 
907         final Future<HttpResponse> future1 = this.httpclient.execute(target, first, context, null);
908         final HttpResponse response1 = future1.get();
909         Assert.assertNotNull(response1);
910 
911         final HttpGet second = new HttpGet("/lille");
912         second.setConfig(config);
913 
914         final Future<HttpResponse> future2 = this.httpclient.execute(target, second, context, null);
915         final HttpResponse response2 = future2.get();
916         Assert.assertNotNull(response2);
917 
918         final HttpRequest reqWrapper = context.getRequest();
919         final HttpHost host = context.getTargetHost();
920 
921         Assert.assertEquals(HttpStatus.SC_OK, response2.getStatusLine().getStatusCode());
922         Assert.assertEquals("/rome", reqWrapper.getRequestLine().getUri());
923         Assert.assertEquals(host, target);
924     }
925 
926 }