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