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.hc.client5.testing.async;
28  
29  import java.net.InetSocketAddress;
30  import java.net.URI;
31  import java.net.URISyntaxException;
32  import java.util.concurrent.ExecutionException;
33  import java.util.concurrent.Future;
34  
35  import org.apache.hc.client5.http.CircularRedirectException;
36  import org.apache.hc.client5.http.RedirectException;
37  import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
38  import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
39  import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
40  import org.apache.hc.client5.http.config.RequestConfig;
41  import org.apache.hc.client5.http.cookie.BasicCookieStore;
42  import org.apache.hc.client5.http.cookie.CookieStore;
43  import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
44  import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
45  import org.apache.hc.client5.http.protocol.HttpClientContext;
46  import org.apache.hc.client5.testing.OldPathRedirectResolver;
47  import org.apache.hc.client5.testing.SSLTestContexts;
48  import org.apache.hc.client5.testing.redirect.Redirect;
49  import org.apache.hc.client5.testing.redirect.RedirectResolver;
50  import org.apache.hc.core5.function.Decorator;
51  import org.apache.hc.core5.function.Supplier;
52  import org.apache.hc.core5.http.ContentType;
53  import org.apache.hc.core5.http.Header;
54  import org.apache.hc.core5.http.HttpException;
55  import org.apache.hc.core5.http.HttpHost;
56  import org.apache.hc.core5.http.HttpRequest;
57  import org.apache.hc.core5.http.HttpResponse;
58  import org.apache.hc.core5.http.HttpStatus;
59  import org.apache.hc.core5.http.HttpVersion;
60  import org.apache.hc.core5.http.ProtocolException;
61  import org.apache.hc.core5.http.URIScheme;
62  import org.apache.hc.core5.http.config.Http1Config;
63  import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
64  import org.apache.hc.core5.http.protocol.HttpCoreContext;
65  import org.apache.hc.core5.http2.config.H2Config;
66  import org.apache.hc.core5.net.URIBuilder;
67  import org.apache.hc.core5.reactive.ReactiveServerExchangeHandler;
68  import org.apache.hc.core5.reactor.IOReactorConfig;
69  import org.apache.hc.core5.testing.nio.H2TestServer;
70  import org.apache.hc.core5.testing.reactive.ReactiveRandomProcessor;
71  import org.apache.hc.core5.util.TimeValue;
72  import org.junit.Assert;
73  import org.junit.Test;
74  
75  public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsyncClient> extends AbstractIntegrationTestBase<T> {
76  
77      protected final HttpVersion version;
78  
79      public AbstractHttpAsyncRedirectsTest(final HttpVersion version, final URIScheme scheme) {
80          super(scheme);
81          this.version = version;
82      }
83  
84      @Override
85      public final HttpHost start() throws Exception {
86          if (version.greaterEquals(HttpVersion.HTTP_2)) {
87              return super.start(null, H2Config.DEFAULT);
88          } else {
89              return super.start(null, Http1Config.DEFAULT);
90          }
91      }
92  
93      public final HttpHost start(final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator) throws Exception {
94          if (version.greaterEquals(HttpVersion.HTTP_2)) {
95              return super.start(null, exchangeHandlerDecorator, H2Config.DEFAULT);
96          } else {
97              return super.start(null, exchangeHandlerDecorator, Http1Config.DEFAULT);
98          }
99      }
100 
101     @Test
102     public void testBasicRedirect300() throws Exception {
103         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
104 
105             @Override
106             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
107                 return new RedirectingAsyncDecorator(
108                         exchangeHandler,
109                         new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MULTIPLE_CHOICES));
110             }
111 
112         });
113 
114         final HttpClientContext context = HttpClientContext.create();
115         final Future<SimpleHttpResponse> future = httpclient.execute(
116                 SimpleHttpRequests.get(target, "/oldlocation/"), context, null);
117         final HttpResponse response = future.get();
118         Assert.assertNotNull(response);
119 
120         final HttpRequest request = context.getRequest();
121 
122         Assert.assertEquals(HttpStatus.SC_MULTIPLE_CHOICES, response.getCode());
123         Assert.assertEquals("/oldlocation/", request.getRequestUri());
124     }
125 
126     @Test
127     public void testBasicRedirect301() throws Exception {
128         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
129 
130             @Override
131             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
132                 return new RedirectingAsyncDecorator(
133                         exchangeHandler,
134                         new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_PERMANENTLY));
135             }
136 
137         });
138         final HttpClientContext context = HttpClientContext.create();
139         final Future<SimpleHttpResponse> future = httpclient.execute(
140                 SimpleHttpRequests.get(target, "/oldlocation/100"), context, null);
141         final HttpResponse response = future.get();
142         Assert.assertNotNull(response);
143 
144         final HttpRequest request = context.getRequest();
145 
146         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
147         Assert.assertEquals("/random/100", request.getRequestUri());
148         Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
149     }
150 
151     @Test
152     public void testBasicRedirect302() throws Exception {
153         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
154 
155             @Override
156             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
157                 return new RedirectingAsyncDecorator(
158                         exchangeHandler,
159                         new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_TEMPORARILY));
160             }
161 
162         });
163         final HttpClientContext context = HttpClientContext.create();
164         final Future<SimpleHttpResponse> future = httpclient.execute(
165                 SimpleHttpRequests.get(target, "/oldlocation/123"), context, null);
166         final HttpResponse response = future.get();
167         Assert.assertNotNull(response);
168 
169         final HttpRequest request = context.getRequest();
170 
171         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
172         Assert.assertEquals("/random/123", request.getRequestUri());
173         Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
174     }
175 
176     @Test
177     public void testBasicRedirect302NoLocation() throws Exception {
178         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
179 
180             @Override
181             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
182                 return new RedirectingAsyncDecorator(
183                         exchangeHandler,
184                         new RedirectResolver() {
185 
186                             @Override
187                             public Redirect resolve(final URI requestUri) throws URISyntaxException {
188                                 final String path = requestUri.getPath();
189                                 if (path.startsWith("/oldlocation")) {
190                                     return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, null);
191                                 }
192                                 return null;
193                             }
194 
195                         });
196             }
197 
198         });
199         final HttpClientContext context = HttpClientContext.create();
200         final Future<SimpleHttpResponse> future = httpclient.execute(
201                 SimpleHttpRequests.get(target, "/oldlocation/100"), context, null);
202         final HttpResponse response = future.get();
203         Assert.assertNotNull(response);
204 
205         final HttpRequest request = context.getRequest();
206         Assert.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getCode());
207         Assert.assertEquals("/oldlocation/100", request.getRequestUri());
208         Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
209     }
210 
211     @Test
212     public void testBasicRedirect303() throws Exception {
213         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
214 
215             @Override
216             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
217                 return new RedirectingAsyncDecorator(
218                         exchangeHandler,
219                         new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_SEE_OTHER));
220             }
221 
222         });
223         final HttpClientContext context = HttpClientContext.create();
224         final Future<SimpleHttpResponse> future = httpclient.execute(
225                 SimpleHttpRequests.get(target, "/oldlocation/123"), context, null);
226         final HttpResponse response = future.get();
227         Assert.assertNotNull(response);
228 
229         final HttpRequest request = context.getRequest();
230 
231         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
232         Assert.assertEquals("/random/123", request.getRequestUri());
233         Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
234     }
235 
236     @Test
237     public void testBasicRedirect304() throws Exception {
238         server.register("/oldlocation/*", new Supplier<AsyncServerExchangeHandler>() {
239 
240             @Override
241             public AsyncServerExchangeHandler get() {
242 
243                 return new AbstractSimpleServerExchangeHandler() {
244 
245                     @Override
246                     protected SimpleHttpResponse handle(final SimpleHttpRequest request,
247                                                         final HttpCoreContext context) throws HttpException {
248                         return SimpleHttpResponse.create(HttpStatus.SC_NOT_MODIFIED, (String) null);
249                     }
250                 };
251 
252             }
253         });
254         final HttpHost target = start();
255         final HttpClientContext context = HttpClientContext.create();
256         final Future<SimpleHttpResponse> future = httpclient.execute(
257                 SimpleHttpRequests.get(target, "/oldlocation/"), context, null);
258         final HttpResponse response = future.get();
259         Assert.assertNotNull(response);
260 
261         final HttpRequest request = context.getRequest();
262 
263         Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, response.getCode());
264         Assert.assertEquals("/oldlocation/", request.getRequestUri());
265     }
266 
267     @Test
268     public void testBasicRedirect305() throws Exception {
269         server.register("/oldlocation/*", new Supplier<AsyncServerExchangeHandler>() {
270 
271             @Override
272             public AsyncServerExchangeHandler get() {
273 
274                 return new AbstractSimpleServerExchangeHandler() {
275 
276                     @Override
277                     protected SimpleHttpResponse handle(final SimpleHttpRequest request,
278                                                         final HttpCoreContext context) throws HttpException {
279                         return SimpleHttpResponse.create(HttpStatus.SC_USE_PROXY, (String) null);
280                     }
281                 };
282 
283             }
284         });
285         final HttpHost target = start();
286         final HttpClientContext context = HttpClientContext.create();
287         final Future<SimpleHttpResponse> future = httpclient.execute(
288                 SimpleHttpRequests.get(target, "/oldlocation/"), context, null);
289         final HttpResponse response = future.get();
290         Assert.assertNotNull(response);
291 
292         final HttpRequest request = context.getRequest();
293 
294         Assert.assertEquals(HttpStatus.SC_USE_PROXY, response.getCode());
295         Assert.assertEquals("/oldlocation/", request.getRequestUri());
296     }
297 
298     @Test
299     public void testBasicRedirect307() throws Exception {
300         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
301 
302             @Override
303             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
304                 return new RedirectingAsyncDecorator(
305                         exchangeHandler,
306                         new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_TEMPORARY_REDIRECT));
307             }
308 
309         });
310         final HttpClientContext context = HttpClientContext.create();
311         final Future<SimpleHttpResponse> future = httpclient.execute(
312                 SimpleHttpRequests.get(target, "/oldlocation/123"), context, null);
313         final HttpResponse response = future.get();
314         Assert.assertNotNull(response);
315 
316         final HttpRequest request = context.getRequest();
317 
318         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
319         Assert.assertEquals("/random/123", request.getRequestUri());
320         Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
321     }
322 
323     @Test(expected=ExecutionException.class)
324     public void testMaxRedirectCheck() throws Exception {
325         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
326 
327             @Override
328             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
329                 return new RedirectingAsyncDecorator(
330                         exchangeHandler,
331                         new OldPathRedirectResolver("/circular-oldlocation/", "/circular-oldlocation/",
332                                 HttpStatus.SC_MOVED_TEMPORARILY));
333             }
334 
335         });
336 
337         final RequestConfig config = RequestConfig.custom()
338                 .setCircularRedirectsAllowed(true)
339                 .setMaxRedirects(5).build();
340         try {
341             final SimpleHttpRequest request = SimpleHttpRequests.get(target, "/circular-oldlocation/");
342             request.setConfig(config);
343             final Future<SimpleHttpResponse> future = httpclient.execute(request, null);
344             future.get();
345         } catch (final ExecutionException e) {
346             Assert.assertTrue(e.getCause() instanceof RedirectException);
347             throw e;
348         }
349     }
350 
351     @Test(expected=ExecutionException.class)
352     public void testCircularRedirect() throws Exception {
353         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
354 
355             @Override
356             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
357                 return new RedirectingAsyncDecorator(
358                         exchangeHandler,
359                         new OldPathRedirectResolver("/circular-oldlocation/", "/circular-oldlocation/",
360                                 HttpStatus.SC_MOVED_TEMPORARILY));
361             }
362 
363         });
364 
365         final RequestConfig config = RequestConfig.custom()
366                 .setCircularRedirectsAllowed(false)
367                 .build();
368         try {
369             final SimpleHttpRequest request = SimpleHttpRequests.get(target, "/circular-oldlocation/");
370             request.setConfig(config);
371             final Future<SimpleHttpResponse> future = httpclient.execute(request, null);
372             future.get();
373         } catch (final ExecutionException e) {
374             Assert.assertTrue(e.getCause() instanceof CircularRedirectException);
375             throw e;
376         }
377     }
378 
379     @Test
380     public void testPostRedirect() throws Exception {
381         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
382 
383             @Override
384             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
385                 return new RedirectingAsyncDecorator(
386                         exchangeHandler,
387                         new OldPathRedirectResolver("/oldlocation", "/echo", HttpStatus.SC_TEMPORARY_REDIRECT));
388             }
389 
390         });
391 
392         final HttpClientContext context = HttpClientContext.create();
393 
394         final SimpleHttpRequest post = SimpleHttpRequests.post(target, "/oldlocation/stuff");
395         post.setBody("stuff", ContentType.TEXT_PLAIN);
396         final Future<SimpleHttpResponse> future = httpclient.execute(post, context, null);
397         final HttpResponse response = future.get();
398         Assert.assertNotNull(response);
399 
400         final HttpRequest request = context.getRequest();
401 
402         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
403         Assert.assertEquals("/echo/stuff", request.getRequestUri());
404         Assert.assertEquals("POST", request.getMethod());
405     }
406 
407     @Test
408     public void testPostRedirectSeeOther() throws Exception {
409         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
410 
411             @Override
412             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
413                 return new RedirectingAsyncDecorator(
414                         exchangeHandler,
415                         new OldPathRedirectResolver("/oldlocation", "/echo", HttpStatus.SC_SEE_OTHER));
416             }
417 
418         });
419 
420         final HttpClientContext context = HttpClientContext.create();
421 
422         final SimpleHttpRequest post = SimpleHttpRequests.post(target, "/oldlocation/stuff");
423         post.setBody("stuff", ContentType.TEXT_PLAIN);
424         final Future<SimpleHttpResponse> future = httpclient.execute(post, context, null);
425         final HttpResponse response = future.get();
426         Assert.assertNotNull(response);
427 
428         final HttpRequest request = context.getRequest();
429 
430         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
431         Assert.assertEquals("/echo/stuff", request.getRequestUri());
432         Assert.assertEquals("GET", request.getMethod());
433     }
434 
435     @Test
436     public void testRelativeRedirect() throws Exception {
437         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
438 
439             @Override
440             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
441                 return new RedirectingAsyncDecorator(
442                         exchangeHandler,
443                         new RedirectResolver() {
444 
445                             @Override
446                             public Redirect resolve(final URI requestUri) throws URISyntaxException {
447                                 final String path = requestUri.getPath();
448                                 if (path.startsWith("/oldlocation")) {
449                                     return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "/random/100");
450 
451                                 }
452                                 return null;
453                             }
454 
455                         });
456             }
457 
458         });
459 
460         final HttpClientContext context = HttpClientContext.create();
461 
462         final Future<SimpleHttpResponse> future = httpclient.execute(
463                 SimpleHttpRequests.get(target, "/oldlocation/stuff"), context, null);
464         final HttpResponse response = future.get();
465         Assert.assertNotNull(response);
466 
467         final HttpRequest request = context.getRequest();
468 
469         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
470         Assert.assertEquals("/random/100", request.getRequestUri());
471         Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
472     }
473 
474     @Test
475     public void testRelativeRedirect2() throws Exception {
476         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
477 
478             @Override
479             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
480                 return new RedirectingAsyncDecorator(
481                         exchangeHandler,
482                         new RedirectResolver() {
483 
484                             @Override
485                             public Redirect resolve(final URI requestUri) throws URISyntaxException {
486                                 final String path = requestUri.getPath();
487                                 if (path.equals("/random/oldlocation")) {
488                                     return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "100");
489 
490                                 }
491                                 return null;
492                             }
493 
494                         });
495             }
496 
497         });
498 
499         final HttpClientContext context = HttpClientContext.create();
500 
501         final Future<SimpleHttpResponse> future = httpclient.execute(
502                 SimpleHttpRequests.get(target, "/random/oldlocation"), context, null);
503         final HttpResponse response = future.get();
504         Assert.assertNotNull(response);
505 
506         final HttpRequest request = context.getRequest();
507 
508         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
509         Assert.assertEquals("/random/100", request.getRequestUri());
510         Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
511     }
512 
513     @Test(expected=ExecutionException.class)
514     public void testRejectBogusRedirectLocation() throws Exception {
515         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
516 
517             @Override
518             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
519                 return new RedirectingAsyncDecorator(
520                         exchangeHandler,
521                         new RedirectResolver() {
522 
523                             @Override
524                             public Redirect resolve(final URI requestUri) throws URISyntaxException {
525                                 final String path = requestUri.getPath();
526                                 if (path.equals("/oldlocation/")) {
527                                     return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "xxx://bogus");
528 
529                                 }
530                                 return null;
531                             }
532 
533                         });
534             }
535 
536         });
537 
538         try {
539             final Future<SimpleHttpResponse> future = httpclient.execute(
540                     SimpleHttpRequests.get(target, "/oldlocation/"), null);
541             future.get();
542         } catch (final ExecutionException ex) {
543             Assert.assertTrue(ex.getCause() instanceof HttpException);
544             throw ex;
545         }
546     }
547 
548     @Test(expected=ExecutionException.class)
549     public void testRejectInvalidRedirectLocation() throws Exception {
550         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
551 
552             @Override
553             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
554                 return new RedirectingAsyncDecorator(
555                         exchangeHandler,
556                         new RedirectResolver() {
557 
558                             @Override
559                             public Redirect resolve(final URI requestUri) throws URISyntaxException {
560                                 final String path = requestUri.getPath();
561                                 if (path.equals("/oldlocation/")) {
562                                     return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "/newlocation/?p=I have spaces");
563 
564                                 }
565                                 return null;
566                             }
567 
568                         });
569             }
570 
571         });
572 
573         try {
574             final Future<SimpleHttpResponse> future = httpclient.execute(
575                     SimpleHttpRequests.get(target, "/oldlocation/"), null);
576             future.get();
577         } catch (final ExecutionException e) {
578             Assert.assertTrue(e.getCause() instanceof ProtocolException);
579             throw e;
580         }
581     }
582 
583     @Test
584     public void testRedirectWithCookie() throws Exception {
585         final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
586 
587             @Override
588             public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
589                 return new RedirectingAsyncDecorator(
590                         exchangeHandler,
591                         new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_TEMPORARILY));
592             }
593 
594         });
595 
596         final CookieStore cookieStore = new BasicCookieStore();
597         final HttpClientContext context = HttpClientContext.create();
598         context.setCookieStore(cookieStore);
599 
600         final BasicClientCookie cookie = new BasicClientCookie("name", "value");
601         cookie.setDomain(target.getHostName());
602         cookie.setPath("/");
603 
604         cookieStore.addCookie(cookie);
605 
606         final Future<SimpleHttpResponse> future = httpclient.execute(
607                 SimpleHttpRequests.get(target, "/oldlocation/100"), context, null);
608         final HttpResponse response = future.get();
609         Assert.assertNotNull(response);
610 
611         final HttpRequest request = context.getRequest();
612 
613         Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
614         Assert.assertEquals("/random/100", request.getRequestUri());
615 
616         final Header[] headers = request.getHeaders("Cookie");
617         Assert.assertEquals("There can only be one (cookie)", 1, headers.length);
618     }
619 
620     @Test
621     public void testCrossSiteRedirect() throws Exception {
622         final H2TestServer secondServer = new H2TestServer(IOReactorConfig.DEFAULT,
623                 scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null, null, null);
624         try {
625             secondServer.register("/random/*", new Supplier<AsyncServerExchangeHandler>() {
626 
627                 @Override
628                 public AsyncServerExchangeHandler get() {
629                     if (isReactive()) {
630                         return new ReactiveServerExchangeHandler(new ReactiveRandomProcessor());
631                     } else {
632                         return new AsyncRandomHandler();
633                     }
634                 }
635 
636             });
637             final InetSocketAddress address2;
638             if (version.greaterEquals(HttpVersion.HTTP_2)) {
639                 address2 = secondServer.start(H2Config.DEFAULT);
640             } else {
641                 address2 = secondServer.start(Http1Config.DEFAULT);
642             }
643             final HttpHost redirectTarget = new HttpHost(scheme.name(), "localhost", address2.getPort());
644 
645             final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
646 
647                 @Override
648                 public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
649                     return new RedirectingAsyncDecorator(
650                             exchangeHandler,
651                             new RedirectResolver() {
652 
653                                 @Override
654                                 public Redirect resolve(final URI requestUri) throws URISyntaxException {
655                                     final String path = requestUri.getPath();
656                                     if (path.equals("/oldlocation")) {
657                                         final URI location = new URIBuilder(requestUri)
658                                                 .setHttpHost(redirectTarget)
659                                                 .setPath("/random/100")
660                                                 .build();
661                                         return new Redirect(HttpStatus.SC_MOVED_PERMANENTLY, location.toString());
662                                     }
663                                     return null;
664                                 }
665 
666                             });
667                 }
668 
669             });
670 
671             final HttpClientContext context = HttpClientContext.create();
672             final Future<SimpleHttpResponse> future = httpclient.execute(
673                     SimpleHttpRequests.get(target, "/oldlocation"), context, null);
674             final HttpResponse response = future.get();
675             Assert.assertNotNull(response);
676 
677             final HttpRequest request = context.getRequest();
678 
679             Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
680             Assert.assertEquals("/random/100", request.getRequestUri());
681             Assert.assertEquals(redirectTarget, new HttpHost(request.getScheme(), request.getAuthority()));
682         } finally {
683             server.shutdown(TimeValue.ofSeconds(5));
684         }
685     }
686 
687 }