Rewrite of redirect integration test cases

This commit is contained in:
Oleg Kalnichevski 2020-03-16 19:24:35 +01:00
parent ffa0530bb2
commit 1928f8db40
8 changed files with 1038 additions and 916 deletions

View File

@ -0,0 +1,158 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.async;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hc.client5.testing.redirect.Redirect;
import org.apache.hc.client5.testing.redirect.RedirectResolver;
import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.ProtocolException;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicHttpResponse;
import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
import org.apache.hc.core5.http.nio.CapacityChannel;
import org.apache.hc.core5.http.nio.DataStreamChannel;
import org.apache.hc.core5.http.nio.ResponseChannel;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.Args;
public class RedirectingAsyncDecorator implements AsyncServerExchangeHandler {
private final AsyncServerExchangeHandler exchangeHandler;
private final RedirectResolver redirectResolver;
private final AtomicBoolean redirecting;
public RedirectingAsyncDecorator(final AsyncServerExchangeHandler exchangeHandler,
final RedirectResolver redirectResolver) {
this.exchangeHandler = Args.notNull(exchangeHandler, "Exchange handler");
this.redirectResolver = redirectResolver;
this.redirecting = new AtomicBoolean();
}
private Redirect resolveRedirect(final HttpRequest request) throws HttpException {
try {
final URI requestURI = request.getUri();
return redirectResolver != null ? redirectResolver.resolve(requestURI) : null;
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
}
private HttpResponse createRedirectResponse(final Redirect redirect) {
final HttpResponse response = new BasicHttpResponse(redirect.status);
if (redirect.location != null) {
response.addHeader(new BasicHeader(HttpHeaders.LOCATION, redirect.location));
}
switch (redirect.connControl) {
case KEEP_ALIVE:
response.addHeader(new BasicHeader(HttpHeaders.CONNECTION, HeaderElements.KEEP_ALIVE));
break;
case CLOSE:
response.addHeader(new BasicHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE));
}
return response;
}
@Override
public void handleRequest(final HttpRequest request,
final EntityDetails entityDetails,
final ResponseChannel responseChannel,
final HttpContext context) throws HttpException, IOException {
final Redirect redirect = resolveRedirect(request);
if (redirect != null) {
responseChannel.sendResponse(createRedirectResponse(redirect), null, context);
redirecting.set(true);
} else {
exchangeHandler.handleRequest(request, entityDetails, responseChannel, context);
}
}
@Override
public final void updateCapacity(final CapacityChannel capacityChannel) throws IOException {
if (!redirecting.get()) {
exchangeHandler.updateCapacity(capacityChannel);
} else {
capacityChannel.update(Integer.MAX_VALUE);
}
}
@Override
public final void consume(final ByteBuffer src) throws IOException {
if (!redirecting.get()) {
exchangeHandler.consume(src);
}
}
@Override
public final void streamEnd(final List<? extends Header> trailers) throws HttpException, IOException {
if (!redirecting.get()) {
exchangeHandler.streamEnd(trailers);
}
}
@Override
public int available() {
if (!redirecting.get()) {
return exchangeHandler.available();
} else {
return 0;
}
}
@Override
public void produce(final DataStreamChannel channel) throws IOException {
if (!redirecting.get()) {
exchangeHandler.produce(channel);
}
}
@Override
public void failed(final Exception cause) {
if (!redirecting.get()) {
exchangeHandler.failed(cause);
}
}
@Override
public void releaseResources() {
exchangeHandler.releaseResources();
}
}

View File

@ -0,0 +1,86 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.classic;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.hc.client5.testing.redirect.Redirect;
import org.apache.hc.client5.testing.redirect.RedirectResolver;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.ProtocolException;
import org.apache.hc.core5.http.io.HttpServerRequestHandler;
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.Args;
public class RedirectingDecorator implements HttpServerRequestHandler {
private final HttpServerRequestHandler requestHandler;
private final RedirectResolver redirectResolver;
public RedirectingDecorator(final HttpServerRequestHandler requestHandler,
final RedirectResolver redirectResolver) {
this.requestHandler = Args.notNull(requestHandler, "Request handler");
this.redirectResolver = redirectResolver;
}
@Override
public void handle(final ClassicHttpRequest request,
final ResponseTrigger responseTrigger,
final HttpContext context) throws HttpException, IOException {
try {
final URI requestURI = request.getUri();
final Redirect redirect = redirectResolver != null ? redirectResolver.resolve(requestURI) : null;
if (redirect != null) {
final ClassicHttpResponse response = new BasicClassicHttpResponse(redirect.status);
if (redirect.location != null) {
response.addHeader(new BasicHeader(HttpHeaders.LOCATION, redirect.location));
}
switch (redirect.connControl) {
case KEEP_ALIVE:
response.addHeader(new BasicHeader(HttpHeaders.CONNECTION, HeaderElements.KEEP_ALIVE));
break;
case CLOSE:
response.addHeader(new BasicHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE));
}
responseTrigger.submitResponse(response);
} else {
requestHandler.handle(request, responseTrigger, context);
}
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
}
}

View File

@ -0,0 +1,48 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.redirect;
public class Redirect {
public enum ConnControl { PROTOCOL_DEFAULT, KEEP_ALIVE, CLOSE }
public final int status;
public final String location;
public final ConnControl connControl;
public Redirect(final int status, final String location, final ConnControl connControl) {
this.status = status;
this.location = location;
this.connControl = connControl;
}
public Redirect(final int status, final String location) {
this(status , location, ConnControl.PROTOCOL_DEFAULT);
}
}

View File

@ -0,0 +1,37 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing.redirect;
import java.net.URI;
import java.net.URISyntaxException;
public interface RedirectResolver {
Redirect resolve(URI requestUri) throws URISyntaxException;
}

View File

@ -0,0 +1,68 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.testing;
import java.net.URI;
import java.net.URISyntaxException;
import org.apache.hc.client5.testing.redirect.Redirect;
import org.apache.hc.client5.testing.redirect.RedirectResolver;
import org.apache.hc.core5.net.URIBuilder;
public class OldPathRedirectResolver implements RedirectResolver {
private final String oldPath;
private final String newPath;
private final int status;
private final Redirect.ConnControl connControl;
public OldPathRedirectResolver(
final String oldPath, final String newPath, final int status, final Redirect.ConnControl connControl) {
this.oldPath = oldPath;
this.newPath = newPath;
this.status = status;
this.connControl = connControl;
}
public OldPathRedirectResolver(final String oldPath, final String newPath, final int status) {
this(oldPath, newPath, status, Redirect.ConnControl.PROTOCOL_DEFAULT);
}
@Override
public Redirect resolve(final URI requestUri) throws URISyntaxException {
final String path = requestUri.getPath();
if (path.startsWith(oldPath)) {
final URI location = new URIBuilder(requestUri)
.setPath(newPath + path.substring(oldPath.length()))
.build();
return new Redirect(status, location.toString(), connControl);
}
return null;
}
}

View File

@ -29,8 +29,6 @@ package org.apache.hc.client5.testing.async;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -45,7 +43,11 @@ import org.apache.hc.client5.http.cookie.CookieStore;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.cookie.BasicClientCookie; import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.testing.OldPathRedirectResolver;
import org.apache.hc.client5.testing.SSLTestContexts; import org.apache.hc.client5.testing.SSLTestContexts;
import org.apache.hc.client5.testing.redirect.Redirect;
import org.apache.hc.client5.testing.redirect.RedirectResolver;
import org.apache.hc.core5.function.Decorator;
import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.function.Supplier;
import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.Header;
@ -58,14 +60,14 @@ import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http.ProtocolException; import org.apache.hc.core5.http.ProtocolException;
import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http.config.Http1Config; import org.apache.hc.core5.http.config.Http1Config;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
import org.apache.hc.core5.http.protocol.HttpCoreContext; import org.apache.hc.core5.http.protocol.HttpCoreContext;
import org.apache.hc.core5.http2.config.H2Config; import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.net.URIBuilder; import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.reactive.ReactiveServerExchangeHandler;
import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.reactor.ListenerEndpoint;
import org.apache.hc.core5.testing.nio.H2TestServer; import org.apache.hc.core5.testing.nio.H2TestServer;
import org.apache.hc.core5.testing.reactive.ReactiveRandomProcessor;
import org.apache.hc.core5.util.TimeValue; import org.apache.hc.core5.util.TimeValue;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -88,132 +90,26 @@ public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsy
} }
} }
static class BasicRedirectService extends AbstractSimpleServerExchangeHandler { public final HttpHost start(final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator) throws Exception {
if (version.greaterEquals(HttpVersion.HTTP_2)) {
private final int statuscode; return super.start(null, exchangeHandlerDecorator, H2Config.DEFAULT);
} else {
public BasicRedirectService(final int statuscode) { return super.start(null, exchangeHandlerDecorator, Http1Config.DEFAULT);
super();
this.statuscode = statuscode;
} }
@Override
protected SimpleHttpResponse handle(
final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
try {
final URI requestURI = request.getUri();
final String path = requestURI.getPath();
if (path.equals("/oldlocation/")) {
final SimpleHttpResponse response = new SimpleHttpResponse(statuscode);
response.addHeader(new BasicHeader("Location",
new URIBuilder(requestURI).setPath("/newlocation/").build()));
return response;
} else if (path.equals("/newlocation/")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_OK);
response.setBody("Successful redirect", ContentType.TEXT_PLAIN);
return response;
} else {
return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND);
}
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
}
}
static class CircularRedirectService extends AbstractSimpleServerExchangeHandler {
public CircularRedirectService() {
super();
}
@Override
protected SimpleHttpResponse handle(
final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
try {
final URI requestURI = request.getUri();
final String path = requestURI.getPath();
if (path.startsWith("/circular-oldlocation")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
response.addHeader(new BasicHeader("Location", "/circular-location2"));
return response;
} else if (path.startsWith("/circular-location2")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
response.addHeader(new BasicHeader("Location", "/circular-oldlocation"));
return response;
} else {
return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND);
}
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
}
}
static class RelativeRedirectService extends AbstractSimpleServerExchangeHandler {
@Override
protected SimpleHttpResponse handle(
final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
try {
final URI requestURI = request.getUri();
final String path = requestURI.getPath();
if (path.equals("/oldlocation/")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
response.addHeader(new BasicHeader("Location", "/relativelocation/"));
return response;
} else if (path.equals("/relativelocation/")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_OK);
response.setBody("Successful redirect", ContentType.TEXT_PLAIN);
return response;
} else {
return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND);
}
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
}
}
static class RelativeRedirectService2 extends AbstractSimpleServerExchangeHandler {
@Override
protected SimpleHttpResponse handle(
final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
try {
final URI requestURI = request.getUri();
final String path = requestURI.getPath();
if (path.equals("/test/oldlocation")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
response.addHeader(new BasicHeader("Location", "relativelocation"));
return response;
} else if (path.equals("/test/relativelocation")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_OK);
response.setBody("Successful redirect", ContentType.TEXT_PLAIN);
return response;
} else {
return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND);
}
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
}
} }
@Test @Test
public void testBasicRedirect300() throws Exception { public void testBasicRedirect300() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new BasicRedirectService(HttpStatus.SC_MULTIPLE_CHOICES); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MULTIPLE_CHOICES));
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
@ -229,117 +125,131 @@ public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsy
@Test @Test
public void testBasicRedirect301() throws Exception { public void testBasicRedirect301() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new BasicRedirectService(HttpStatus.SC_MOVED_PERMANENTLY); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_PERMANENTLY));
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/oldlocation/"), context, null); SimpleHttpRequests.get(target, "/oldlocation/100"), context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
Assert.assertNotNull(response); Assert.assertNotNull(response);
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode()); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertEquals("/newlocation/", request.getRequestUri()); Assert.assertEquals("/random/100", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
} }
@Test @Test
public void testBasicRedirect302() throws Exception { public void testBasicRedirect302() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new BasicRedirectService(HttpStatus.SC_MOVED_TEMPORARILY); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_TEMPORARILY));
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/oldlocation/"), context, null); SimpleHttpRequests.get(target, "/oldlocation/123"), context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
Assert.assertNotNull(response); Assert.assertNotNull(response);
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode()); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertEquals("/newlocation/", request.getRequestUri()); Assert.assertEquals("/random/123", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
} }
@Test @Test
public void testBasicRedirect302NoLocation() throws Exception { public void testBasicRedirect302NoLocation() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new AbstractSimpleServerExchangeHandler() { return new RedirectingAsyncDecorator(
exchangeHandler,
new RedirectResolver() {
@Override @Override
protected SimpleHttpResponse handle( public Redirect resolve(final URI requestUri) throws URISyntaxException {
final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException { final String path = requestUri.getPath();
return new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY); if (path.startsWith("/oldlocation")) {
} return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, null);
}
return null;
}
}; });
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/oldlocation/"), context, null); SimpleHttpRequests.get(target, "/oldlocation/100"), context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
Assert.assertNotNull(response); Assert.assertNotNull(response);
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getCode()); Assert.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getCode());
Assert.assertEquals("/oldlocation/", request.getRequestUri()); Assert.assertEquals("/oldlocation/100", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
} }
@Test @Test
public void testBasicRedirect303() throws Exception { public void testBasicRedirect303() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new BasicRedirectService(HttpStatus.SC_SEE_OTHER); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_SEE_OTHER));
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/oldlocation/"), context, null); SimpleHttpRequests.get(target, "/oldlocation/123"), context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
Assert.assertNotNull(response); Assert.assertNotNull(response);
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode()); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertEquals("/newlocation/", request.getRequestUri()); Assert.assertEquals("/random/123", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
} }
@Test @Test
public void testBasicRedirect304() throws Exception { public void testBasicRedirect304() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { server.register("/oldlocation/*", new Supplier<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler get() {
return new BasicRedirectService(HttpStatus.SC_NOT_MODIFIED);
}
return new AbstractSimpleServerExchangeHandler() {
@Override
protected SimpleHttpResponse handle(final SimpleHttpRequest request,
final HttpCoreContext context) throws HttpException {
return SimpleHttpResponse.create(HttpStatus.SC_NOT_MODIFIED, (String) null);
}
};
}
}); });
final HttpHost target = start(); final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
@ -356,13 +266,21 @@ public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsy
@Test @Test
public void testBasicRedirect305() throws Exception { public void testBasicRedirect305() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { server.register("/oldlocation/*", new Supplier<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler get() {
return new BasicRedirectService(HttpStatus.SC_USE_PROXY);
}
return new AbstractSimpleServerExchangeHandler() {
@Override
protected SimpleHttpResponse handle(final SimpleHttpRequest request,
final HttpCoreContext context) throws HttpException {
return SimpleHttpResponse.create(HttpStatus.SC_USE_PROXY, (String) null);
}
};
}
}); });
final HttpHost target = start(); final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
@ -379,39 +297,42 @@ public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsy
@Test @Test
public void testBasicRedirect307() throws Exception { public void testBasicRedirect307() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new BasicRedirectService(HttpStatus.SC_TEMPORARY_REDIRECT); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_TEMPORARY_REDIRECT));
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/oldlocation/"), context, null); SimpleHttpRequests.get(target, "/oldlocation/123"), context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
Assert.assertNotNull(response); Assert.assertNotNull(response);
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode()); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertEquals("/newlocation/", request.getRequestUri()); Assert.assertEquals("/random/123", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
} }
@Test(expected=ExecutionException.class) @Test(expected=ExecutionException.class)
public void testMaxRedirectCheck() throws Exception { public void testMaxRedirectCheck() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new CircularRedirectService(); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/circular-oldlocation/", "/circular-oldlocation/",
HttpStatus.SC_MOVED_TEMPORARILY));
} }
}); });
final HttpHost target = start();
final RequestConfig config = RequestConfig.custom() final RequestConfig config = RequestConfig.custom()
.setCircularRedirectsAllowed(true) .setCircularRedirectsAllowed(true)
@ -429,15 +350,17 @@ public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsy
@Test(expected=ExecutionException.class) @Test(expected=ExecutionException.class)
public void testCircularRedirect() throws Exception { public void testCircularRedirect() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new CircularRedirectService(); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/circular-oldlocation/", "/circular-oldlocation/",
HttpStatus.SC_MOVED_TEMPORARILY));
} }
}); });
final HttpHost target = start();
final RequestConfig config = RequestConfig.custom() final RequestConfig config = RequestConfig.custom()
.setCircularRedirectsAllowed(false) .setCircularRedirectsAllowed(false)
@ -455,19 +378,20 @@ public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsy
@Test @Test
public void testPostRedirectSeeOther() throws Exception { public void testPostRedirectSeeOther() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new BasicRedirectService(HttpStatus.SC_SEE_OTHER); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/oldlocation", "/echo", HttpStatus.SC_SEE_OTHER));
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final SimpleHttpRequest post = SimpleHttpRequests.post(target, "/oldlocation/"); final SimpleHttpRequest post = SimpleHttpRequests.post(target, "/oldlocation/stuff");
post.setBody("stuff", ContentType.TEXT_PLAIN); post.setBody("stuff", ContentType.TEXT_PLAIN);
final Future<SimpleHttpResponse> future = httpclient.execute(post, context, null); final Future<SimpleHttpResponse> future = httpclient.execute(post, context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
@ -476,106 +400,112 @@ public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsy
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode()); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertEquals("/newlocation/", request.getRequestUri()); Assert.assertEquals("/echo/stuff", request.getRequestUri());
Assert.assertEquals("GET", request.getMethod()); Assert.assertEquals("GET", request.getMethod());
} }
@Test @Test
public void testRelativeRedirect() throws Exception { public void testRelativeRedirect() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new RelativeRedirectService(); return new RedirectingAsyncDecorator(
exchangeHandler,
new RedirectResolver() {
@Override
public Redirect resolve(final URI requestUri) throws URISyntaxException {
final String path = requestUri.getPath();
if (path.startsWith("/oldlocation")) {
return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "/random/100");
}
return null;
}
});
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/oldlocation/"), context, null); SimpleHttpRequests.get(target, "/oldlocation/stuff"), context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
Assert.assertNotNull(response); Assert.assertNotNull(response);
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode()); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertEquals("/relativelocation/", request.getRequestUri()); Assert.assertEquals("/random/100", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
} }
@Test @Test
public void testRelativeRedirect2() throws Exception { public void testRelativeRedirect2() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new RelativeRedirectService2(); return new RedirectingAsyncDecorator(
exchangeHandler,
new RedirectResolver() {
@Override
public Redirect resolve(final URI requestUri) throws URISyntaxException {
final String path = requestUri.getPath();
if (path.equals("/random/oldlocation")) {
return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "100");
}
return null;
}
});
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/test/oldlocation"), context, null); SimpleHttpRequests.get(target, "/random/oldlocation"), context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
Assert.assertNotNull(response); Assert.assertNotNull(response);
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode()); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertEquals("/test/relativelocation", request.getRequestUri()); Assert.assertEquals("/random/100", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
} }
static class BogusRedirectService extends AbstractSimpleServerExchangeHandler {
private final String url;
public BogusRedirectService(final String url) {
super();
this.url = url;
}
@Override
protected SimpleHttpResponse handle(
final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
try {
final URI requestURI = request.getUri();
final String path = requestURI.getPath();
if (path.equals("/oldlocation/")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
response.addHeader(new BasicHeader("Location", url));
return response;
} else if (path.equals("/relativelocation/")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_OK);
response.setBody("Successful redirect", ContentType.TEXT_PLAIN);
return response;
} else {
return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND);
}
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
}
}
@Test(expected=ExecutionException.class) @Test(expected=ExecutionException.class)
public void testRejectBogusRedirectLocation() throws Exception { public void testRejectBogusRedirectLocation() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new BogusRedirectService("xxx://bogus"); return new RedirectingAsyncDecorator(
exchangeHandler,
new RedirectResolver() {
@Override
public Redirect resolve(final URI requestUri) throws URISyntaxException {
final String path = requestUri.getPath();
if (path.equals("/oldlocation/")) {
return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "xxx://bogus");
}
return null;
}
});
} }
}); });
final HttpHost target = start();
try { try {
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
@ -589,15 +519,28 @@ public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsy
@Test(expected=ExecutionException.class) @Test(expected=ExecutionException.class)
public void testRejectInvalidRedirectLocation() throws Exception { public void testRejectInvalidRedirectLocation() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new BogusRedirectService("/newlocation/?p=I have spaces"); return new RedirectingAsyncDecorator(
exchangeHandler,
new RedirectResolver() {
@Override
public Redirect resolve(final URI requestUri) throws URISyntaxException {
final String path = requestUri.getPath();
if (path.equals("/oldlocation/")) {
return new Redirect(HttpStatus.SC_MOVED_TEMPORARILY, "/newlocation/?p=I have spaces");
}
return null;
}
});
} }
}); });
final HttpHost target = start();
try { try {
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
@ -611,15 +554,16 @@ public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsy
@Test @Test
public void testRedirectWithCookie() throws Exception { public void testRedirectWithCookie() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new BasicRedirectService(HttpStatus.SC_MOVED_TEMPORARILY); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_TEMPORARILY));
} }
}); });
final HttpHost target = start();
final CookieStore cookieStore = new BasicCookieStore(); final CookieStore cookieStore = new BasicCookieStore();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
@ -632,214 +576,84 @@ public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsy
cookieStore.addCookie(cookie); cookieStore.addCookie(cookie);
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/oldlocation/"), context, null); SimpleHttpRequests.get(target, "/oldlocation/100"), context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
Assert.assertNotNull(response); Assert.assertNotNull(response);
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode()); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertEquals("/newlocation/", request.getRequestUri()); Assert.assertEquals("/random/100", request.getRequestUri());
final Header[] headers = request.getHeaders("Cookie"); final Header[] headers = request.getHeaders("Cookie");
Assert.assertEquals("There can only be one (cookie)", 1, headers.length); Assert.assertEquals("There can only be one (cookie)", 1, headers.length);
} }
static class CrossSiteRedirectService extends AbstractSimpleServerExchangeHandler {
private final HttpHost host;
public CrossSiteRedirectService(final HttpHost host) {
super();
this.host = host;
}
@Override
protected SimpleHttpResponse handle(
final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
final String location;
try {
final URIBuilder uribuilder = new URIBuilder(request.getUri());
uribuilder.setScheme(host.getSchemeName());
uribuilder.setHost(host.getHostName());
uribuilder.setPort(host.getPort());
uribuilder.setPath("/random/1024");
location = uribuilder.build().toASCIIString();
} catch (final URISyntaxException ex) {
throw new ProtocolException("Invalid request URI", ex);
}
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_TEMPORARY_REDIRECT);
response.addHeader(new BasicHeader("Location", location));
return response;
}
}
@Test @Test
public void testCrossSiteRedirect() throws Exception { public void testCrossSiteRedirect() throws Exception {
server.register("/random/*", new Supplier<AsyncServerExchangeHandler>() {
@Override
public AsyncServerExchangeHandler get() {
return new AsyncRandomHandler();
}
});
final HttpHost redirectTarget = start();
final H2TestServer secondServer = new H2TestServer(IOReactorConfig.DEFAULT, final H2TestServer secondServer = new H2TestServer(IOReactorConfig.DEFAULT,
scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null, null, null); scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null, null, null);
try { try {
secondServer.register("/redirect/*", new Supplier<AsyncServerExchangeHandler>() { secondServer.register("/random/*", new Supplier<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler get() {
return new CrossSiteRedirectService(redirectTarget); if (isReactive()) {
return new ReactiveServerExchangeHandler(new ReactiveRandomProcessor());
} else {
return new AsyncRandomHandler();
}
}
});
final InetSocketAddress address2;
if (version.greaterEquals(HttpVersion.HTTP_2)) {
address2 = secondServer.start(H2Config.DEFAULT);
} else {
address2 = secondServer.start(Http1Config.DEFAULT);
}
final HttpHost redirectTarget = new HttpHost(scheme.name(), "localhost", address2.getPort());
final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override
public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new RedirectingAsyncDecorator(
exchangeHandler,
new RedirectResolver() {
@Override
public Redirect resolve(final URI requestUri) throws URISyntaxException {
final String path = requestUri.getPath();
if (path.equals("/oldlocation")) {
final URI location = new URIBuilder(requestUri)
.setHttpHost(redirectTarget)
.setPath("/random/100")
.build();
return new Redirect(HttpStatus.SC_MOVED_PERMANENTLY, location.toString());
}
return null;
}
});
} }
}); });
if (version.greaterEquals(HttpVersion.HTTP_2)) { final HttpClientContext context = HttpClientContext.create();
secondServer.start(H2Config.DEFAULT); final Future<SimpleHttpResponse> future = httpclient.execute(
} else { SimpleHttpRequests.get(target, "/oldlocation"), context, null);
secondServer.start(Http1Config.DEFAULT); final HttpResponse response = future.get();
} Assert.assertNotNull(response);
final Future<ListenerEndpoint> endpointFuture = secondServer.listen(new InetSocketAddress(0));
final ListenerEndpoint endpoint2 = endpointFuture.get();
final InetSocketAddress address2 = (InetSocketAddress) endpoint2.getAddress(); final HttpRequest request = context.getRequest();
final HttpHost initialTarget = new HttpHost(scheme.name(), "localhost", address2.getPort());
final Queue<Future<SimpleHttpResponse>> queue = new ConcurrentLinkedQueue<>(); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
for (int i = 0; i < 1; i++) { Assert.assertEquals("/random/100", request.getRequestUri());
queue.add(httpclient.execute(SimpleHttpRequests.get(initialTarget, "/redirect/anywhere"), null)); Assert.assertEquals(redirectTarget, new HttpHost(request.getScheme(), request.getAuthority()));
}
while (!queue.isEmpty()) {
final Future<SimpleHttpResponse> future = queue.remove();
final HttpResponse response = future.get();
Assert.assertNotNull(response);
Assert.assertEquals(200, response.getCode());
}
} finally { } finally {
server.shutdown(TimeValue.ofSeconds(5)); server.shutdown(TimeValue.ofSeconds(5));
} }
} }
private static class RomeRedirectService extends AbstractSimpleServerExchangeHandler {
@Override
protected SimpleHttpResponse handle(
final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
try {
final URI requestURI = request.getUri();
final String path = requestURI.getPath();
if (path.equals("/rome")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_OK);
response.setBody("Successful redirect", ContentType.TEXT_PLAIN);
return response;
} else {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
response.addHeader(new BasicHeader("Location", "/rome"));
return response;
}
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
}
}
@Test
public void testRepeatRequest() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() {
@Override
public AsyncServerExchangeHandler get() {
return new RomeRedirectService();
}
});
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future1 = httpclient.execute(
SimpleHttpRequests.get(target, "/rome"), context, null);
final HttpResponse response1 = future1.get();
Assert.assertNotNull(response1);
final Future<SimpleHttpResponse> future2 = httpclient.execute(
SimpleHttpRequests.get(target, "/rome"), context, null);
final HttpResponse response2 = future2.get();
Assert.assertNotNull(response2);
final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
Assert.assertEquals("/rome", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
}
@Test
public void testRepeatRequestRedirect() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() {
@Override
public AsyncServerExchangeHandler get() {
return new RomeRedirectService();
}
});
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future1 = httpclient.execute(
SimpleHttpRequests.get(target, "/lille"), context, null);
final HttpResponse response1 = future1.get();
Assert.assertNotNull(response1);
final Future<SimpleHttpResponse> future2 = httpclient.execute(
SimpleHttpRequests.get(target, "/lille"), context, null);
final HttpResponse response2 = future2.get();
Assert.assertNotNull(response2);
final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
Assert.assertEquals("/rome", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
}
@Test
public void testDifferentRequestSameRedirect() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() {
@Override
public AsyncServerExchangeHandler get() {
return new RomeRedirectService();
}
});
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future1 = httpclient.execute(
SimpleHttpRequests.get(target, "/alian"), context, null);
final HttpResponse response1 = future1.get();
Assert.assertNotNull(response1);
final Future<SimpleHttpResponse> future2 = httpclient.execute(
SimpleHttpRequests.get(target, "/lille"), context, null);
final HttpResponse response2 = future2.get();
Assert.assertNotNull(response2);
final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
Assert.assertEquals("/rome", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
}
} }

View File

@ -26,15 +26,12 @@
*/ */
package org.apache.hc.client5.testing.async; package org.apache.hc.client5.testing.async;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequests; import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.config.RequestConfig;
@ -44,23 +41,20 @@ import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.client5.testing.OldPathRedirectResolver;
import org.apache.hc.client5.testing.SSLTestContexts; import org.apache.hc.client5.testing.SSLTestContexts;
import org.apache.hc.core5.function.Supplier; import org.apache.hc.client5.testing.redirect.Redirect;
import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.function.Decorator;
import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHeaders; import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest; import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse; import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus; import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.HttpVersion; import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http.ProtocolException;
import org.apache.hc.core5.http.URIScheme; import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http.message.BasicHeader; import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler; import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
import org.apache.hc.core5.http.protocol.HttpCoreContext;
import org.apache.hc.core5.net.URIBuilder;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -129,54 +123,19 @@ public class TestHttp1AsyncRedirects extends AbstractHttpAsyncRedirectsTest<Clos
return clientBuilder.build(); return clientBuilder.build();
} }
static class NoKeepAliveRedirectService extends AbstractSimpleServerExchangeHandler {
private final int statuscode;
public NoKeepAliveRedirectService(final int statuscode) {
super();
this.statuscode = statuscode;
}
@Override
protected SimpleHttpResponse handle(
final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
try {
final URI requestURI = request.getUri();
final String path = requestURI.getPath();
if (path.equals("/oldlocation/")) {
final SimpleHttpResponse response = new SimpleHttpResponse(statuscode);
response.addHeader(new BasicHeader("Location",
new URIBuilder(requestURI).setPath("/newlocation/").build()));
response.addHeader(new BasicHeader("Connection", "close"));
return response;
} else if (path.equals("/newlocation/")) {
final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_OK);
response.setBody("Successful redirect", ContentType.TEXT_PLAIN);
return response;
} else {
return new SimpleHttpResponse(HttpStatus.SC_NOT_FOUND);
}
} catch (final URISyntaxException ex) {
throw new ProtocolException(ex.getMessage(), ex);
}
}
}
@Override
@Test @Test
public void testBasicRedirect300() throws Exception { public void testBasicRedirect300NoKeepAlive() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new NoKeepAliveRedirectService(HttpStatus.SC_MULTIPLE_CHOICES); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MULTIPLE_CHOICES,
Redirect.ConnControl.CLOSE));
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/oldlocation/"), context, null); SimpleHttpRequests.get(target, "/oldlocation/"), context, null);
@ -191,57 +150,59 @@ public class TestHttp1AsyncRedirects extends AbstractHttpAsyncRedirectsTest<Clos
@Test @Test
public void testBasicRedirect301NoKeepAlive() throws Exception { public void testBasicRedirect301NoKeepAlive() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() { final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override @Override
public AsyncServerExchangeHandler get() { public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new NoKeepAliveRedirectService(HttpStatus.SC_MOVED_PERMANENTLY); return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_PERMANENTLY,
Redirect.ConnControl.CLOSE));
} }
}); });
final HttpHost target = start();
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/oldlocation/"), context, null); SimpleHttpRequests.get(target, "/oldlocation/100"), context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
Assert.assertNotNull(response); Assert.assertNotNull(response);
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode()); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertEquals("/newlocation/", request.getRequestUri()); Assert.assertEquals("/random/100", request.getRequestUri());
Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority())); Assert.assertEquals(target, new HttpHost(request.getScheme(), request.getAuthority()));
} }
@Test @Test
public void testDefaultHeadersRedirect() throws Exception { public void testDefaultHeadersRedirect() throws Exception {
server.register("*", new Supplier<AsyncServerExchangeHandler>() {
@Override
public AsyncServerExchangeHandler get() {
return new NoKeepAliveRedirectService(HttpStatus.SC_MOVED_TEMPORARILY);
}
});
final List<Header> defaultHeaders = new ArrayList<>(1); final List<Header> defaultHeaders = new ArrayList<>(1);
defaultHeaders.add(new BasicHeader(HttpHeaders.USER_AGENT, "my-test-client")); defaultHeaders.add(new BasicHeader(HttpHeaders.USER_AGENT, "my-test-client"));
clientBuilder.setDefaultHeaders(defaultHeaders); clientBuilder.setDefaultHeaders(defaultHeaders);
final HttpHost target = start(); final HttpHost target = start(new Decorator<AsyncServerExchangeHandler>() {
@Override
public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
return new RedirectingAsyncDecorator(
exchangeHandler,
new OldPathRedirectResolver("/oldlocation", "/random", HttpStatus.SC_MOVED_PERMANENTLY,
Redirect.ConnControl.CLOSE));
}
});
final HttpClientContext context = HttpClientContext.create(); final HttpClientContext context = HttpClientContext.create();
final Future<SimpleHttpResponse> future = httpclient.execute( final Future<SimpleHttpResponse> future = httpclient.execute(
SimpleHttpRequests.get(target, "/oldlocation/"), context, null); SimpleHttpRequests.get(target, "/oldlocation/123"), context, null);
final HttpResponse response = future.get(); final HttpResponse response = future.get();
Assert.assertNotNull(response); Assert.assertNotNull(response);
final HttpRequest request = context.getRequest(); final HttpRequest request = context.getRequest();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode()); Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertEquals("/newlocation/", request.getRequestUri()); Assert.assertEquals("/random/123", request.getRequestUri());
final Header header = request.getFirstHeader(HttpHeaders.USER_AGENT); final Header header = request.getFirstHeader(HttpHeaders.USER_AGENT);
Assert.assertEquals("my-test-client", header.getValue()); Assert.assertEquals("my-test-client", header.getValue());