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