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