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