hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject [6/6] httpcomponents-client git commit: * HTTP/2 multiplexing HttpAsyncClient implementation * Restructured integration tests to reduce test duplication
Date Mon, 13 Nov 2017 21:46:01 GMT
* HTTP/2 multiplexing HttpAsyncClient implementation
* Restructured integration tests to reduce test duplication


Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/commit/6228a736
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/tree/6228a736
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-client/diff/6228a736

Branch: refs/heads/master
Commit: 6228a7361350df0c1ec99ebfd36034438512f068
Parents: 703b796
Author: Oleg Kalnichevski <olegk@apache.org>
Authored: Mon Nov 13 10:50:27 2017 +0100
Committer: Oleg Kalnichevski <olegk@apache.org>
Committed: Mon Nov 13 22:30:12 2017 +0100

----------------------------------------------------------------------
 .../async/AbstractHttp1IntegrationTestBase.java | 133 +++
 .../AbstractHttpAsyncClientAuthentication.java  | 565 ++++++++++++
 .../AbstractHttpAsyncFundamentalsTest.java      | 136 +++
 .../async/AbstractHttpAsyncRedirectsTest.java   | 825 +++++++++++++++++
 .../async/AbstractIntegrationTestBase.java      | 114 +++
 .../testing/async/AbstractServerTestBase.java   |  96 ++
 .../testing/async/IntegrationTestBase.java      | 108 ---
 .../testing/async/LocalAsyncServerTestBase.java | 120 ---
 .../testing/async/TestAsyncRedirects.java       | 910 -------------------
 .../async/TestAsyncStatefulConnManagement.java  | 261 ------
 .../testing/async/TestClientAuthentication.java | 598 ------------
 .../client5/testing/async/TestHttp1Async.java   | 198 ++++
 .../testing/async/TestHttp1AsyncRedirects.java  | 254 ++++++
 .../TestHttp1AsyncStatefulConnManagement.java   | 319 +++++++
 .../async/TestHttp1ClientAuthentication.java    | 180 ++++
 .../client5/testing/async/TestHttp2Async.java   |  88 ++
 .../testing/async/TestHttp2AsyncMinimal.java    | 171 +---
 .../testing/async/TestHttp2AsyncRedirect.java   |  88 ++
 .../async/TestHttp2ClientAuthentication.java    |  97 ++
 .../hc/client5/testing/async/TestHttpAsync.java | 252 -----
 .../testing/async/TestHttpAsyncMinimal.java     | 176 +---
 .../http/impl/async/AsyncExecRuntimeImpl.java   | 276 ------
 .../http/impl/async/AsyncMainClientExec.java    | 229 -----
 .../impl/async/Http2AsyncClientBuilder.java     | 885 ++++++++++++++++++
 .../Http2AsyncClientEventHandlerFactory.java    | 194 ++++
 .../impl/async/Http2AsyncMainClientExec.java    | 170 ++++
 .../http/impl/async/HttpAsyncClientBuilder.java |   2 +-
 .../HttpAsyncClientEventHandlerFactory.java     |   2 +-
 .../http/impl/async/HttpAsyncClients.java       |  32 +-
 .../impl/async/HttpAsyncMainClientExec.java     | 229 +++++
 .../async/InternalAbstractHttpAsyncClient.java  | 308 +++++++
 .../impl/async/InternalHttp2AsyncClient.java    |  88 ++
 .../async/InternalHttp2AsyncExecRuntime.java    | 245 +++++
 .../impl/async/InternalHttpAsyncClient.java     | 254 +-----
 .../async/InternalHttpAsyncExecRuntime.java     | 276 ++++++
 35 files changed, 5563 insertions(+), 3316 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6228a736/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttp1IntegrationTestBase.java
----------------------------------------------------------------------
diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttp1IntegrationTestBase.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttp1IntegrationTestBase.java
new file mode 100644
index 0000000..18dd89e
--- /dev/null
+++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttp1IntegrationTestBase.java
@@ -0,0 +1,133 @@
+/*
+ * ====================================================================
+ * 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.net.InetSocketAddress;
+import java.util.concurrent.Future;
+
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.ssl.H2TlsStrategy;
+import org.apache.hc.client5.testing.SSLTestContexts;
+import org.apache.hc.core5.function.Decorator;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.config.H1Config;
+import org.apache.hc.core5.http.impl.HttpProcessors;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.io.ShutdownType;
+import org.apache.hc.core5.reactor.ListenerEndpoint;
+import org.junit.Rule;
+import org.junit.rules.ExternalResource;
+
+public abstract class AbstractHttp1IntegrationTestBase extends AbstractServerTestBase {
+
+    public AbstractHttp1IntegrationTestBase(final URIScheme scheme) {
+        super(scheme);
+    }
+
+    public AbstractHttp1IntegrationTestBase() {
+        super(URIScheme.HTTP);
+    }
+
+    protected HttpAsyncClientBuilder clientBuilder;
+    protected PoolingAsyncClientConnectionManager connManager;
+    protected CloseableHttpAsyncClient httpclient;
+
+    @Rule
+    public ExternalResource connManagerResource = new ExternalResource() {
+
+        @Override
+        protected void before() throws Throwable {
+            connManager = PoolingAsyncClientConnectionManagerBuilder.create()
+                    .setTlsStrategy(new H2TlsStrategy(SSLTestContexts.createClientSSLContext()))
+                    .build();
+        }
+
+        @Override
+        protected void after() {
+            if (connManager != null) {
+                connManager.close();
+                connManager = null;
+            }
+        }
+
+    };
+
+    @Rule
+    public ExternalResource clientResource = new ExternalResource() {
+
+        @Override
+        protected void before() throws Throwable {
+            clientBuilder = HttpAsyncClientBuilder.create()
+                    .setDefaultRequestConfig(RequestConfig.custom()
+                            .setSocketTimeout(TIMEOUT)
+                            .setConnectTimeout(TIMEOUT)
+                            .setConnectionRequestTimeout(TIMEOUT)
+                            .build())
+                    .setConnectionManager(connManager);
+        }
+
+        @Override
+        protected void after() {
+            if (httpclient != null) {
+                httpclient.shutdown(ShutdownType.GRACEFUL);
+                httpclient = null;
+            }
+        }
+
+    };
+
+    public HttpHost start(
+            final HttpProcessor httpProcessor,
+            final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator,
+            final H1Config h1Config) throws Exception {
+        server.start(httpProcessor, exchangeHandlerDecorator, h1Config);
+        final Future<ListenerEndpoint> endpointFuture = server.listen(new InetSocketAddress(0));
+        httpclient = clientBuilder.build();
+        httpclient.start();
+        final ListenerEndpoint endpoint = endpointFuture.get();
+        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
+        return new HttpHost("localhost", address.getPort(), scheme.name());
+    }
+
+    public HttpHost start(
+            final HttpProcessor httpProcessor,
+            final H1Config h1Config) throws Exception {
+        return start(httpProcessor, null, h1Config);
+    }
+
+    public HttpHost start() throws Exception {
+        return start(HttpProcessors.server(), H1Config.DEFAULT);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6228a736/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncClientAuthentication.java
----------------------------------------------------------------------
diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncClientAuthentication.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncClientAuthentication.java
new file mode 100644
index 0000000..7dc8098
--- /dev/null
+++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncClientAuthentication.java
@@ -0,0 +1,565 @@
+/*
+ * ====================================================================
+ * 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.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.hc.client5.http.AuthenticationStrategy;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.auth.AuthChallenge;
+import org.apache.hc.client5.http.auth.AuthScheme;
+import org.apache.hc.client5.http.auth.AuthSchemeProvider;
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.ChallengeType;
+import org.apache.hc.client5.http.auth.Credentials;
+import org.apache.hc.client5.http.auth.CredentialsStore;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
+import org.apache.hc.client5.http.impl.auth.BasicScheme;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.client5.testing.BasicTestAuthenticator;
+import org.apache.hc.client5.testing.auth.Authenticator;
+import org.apache.hc.core5.function.Decorator;
+import org.apache.hc.core5.function.Supplier;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.config.H1Config;
+import org.apache.hc.core5.http.config.Lookup;
+import org.apache.hc.core5.http.config.Registry;
+import org.apache.hc.core5.http.config.RegistryBuilder;
+import org.apache.hc.core5.http.impl.HttpProcessors;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.apache.hc.core5.http2.config.H2Config;
+import org.apache.hc.core5.http2.impl.Http2Processors;
+import org.apache.hc.core5.net.URIAuthority;
+import org.junit.Assert;
+import org.junit.Test;
+
+public abstract class AbstractHttpAsyncClientAuthentication<T extends CloseableHttpAsyncClient> extends AbstractIntegrationTestBase<T> {
+
+    protected final HttpVersion protocolVersion;
+
+    public AbstractHttpAsyncClientAuthentication(final URIScheme scheme, final HttpVersion protocolVersion) {
+        super(scheme);
+        this.protocolVersion = protocolVersion;
+    }
+
+    @Override
+    public final HttpHost start() throws Exception {
+        return start(new Decorator<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler requestHandler) {
+                return new AuthenticatingAsyncDecorator(requestHandler, new BasicTestAuthenticator("test:test", "test realm"));
+            }
+
+        });
+    }
+
+    public final HttpHost start(
+            final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator) throws Exception {
+        if (protocolVersion.greaterEquals(HttpVersion.HTTP_2_0)) {
+            return super.start(
+                    Http2Processors.server(),
+                    exchangeHandlerDecorator,
+                    H2Config.DEFAULT);
+        } else {
+            return super.start(
+                    HttpProcessors.server(),
+                    exchangeHandlerDecorator,
+                    H1Config.DEFAULT);
+        }
+    }
+
+    abstract void setDefaultAuthSchemeRegistry(Lookup<AuthSchemeProvider> authSchemeRegistry);
+
+    abstract void setTargetAuthenticationStrategy(AuthenticationStrategy targetAuthStrategy);
+
+    static class TestCredentialsProvider implements CredentialsStore {
+
+        private final Credentials creds;
+        private AuthScope authscope;
+
+        TestCredentialsProvider(final Credentials creds) {
+            super();
+            this.creds = creds;
+        }
+
+        @Override
+        public void clear() {
+        }
+
+        @Override
+        public Credentials getCredentials(final AuthScope authscope, final HttpContext context) {
+            this.authscope = authscope;
+            return this.creds;
+        }
+
+        @Override
+        public void setCredentials(final AuthScope authscope, final Credentials credentials) {
+        }
+
+        public AuthScope getAuthScope() {
+            return this.authscope;
+        }
+
+    }
+
+    @Test
+    public void testBasicAuthenticationNoCreds() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(null);
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationFailure() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccess() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationWithEntitySuccess() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final SimpleHttpRequest put = SimpleHttpRequest.put(target, "/");
+        put.setBodyText("Some important stuff", ContentType.TEXT_PLAIN);
+        final Future<SimpleHttpResponse> future = httpclient.execute(put, context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationExpectationFailure() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+        context.setRequestConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
+
+        final SimpleHttpRequest put = SimpleHttpRequest.put(target, "/");
+        put.setBodyText("Some important stuff", ContentType.TEXT_PLAIN);
+        final Future<SimpleHttpResponse> future = httpclient.execute(put, context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
+    }
+
+    @Test
+    public void testBasicAuthenticationExpectationSuccess() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+        context.setRequestConfig(RequestConfig.custom().setExpectContinueEnabled(true).build());
+
+        final SimpleHttpRequest put = SimpleHttpRequest.put(target, "/");
+        put.setBodyText("Some important stuff", ContentType.TEXT_PLAIN);
+        final Future<SimpleHttpResponse> future = httpclient.execute(put, context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationCredentialsCaching() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+
+        final AtomicLong count = new AtomicLong(0);
+        setTargetAuthenticationStrategy(new DefaultAuthenticationStrategy() {
+
+            @Override
+            public List<AuthScheme> select(
+                    final ChallengeType challengeType,
+                    final Map<String, AuthChallenge> challenges,
+                    final HttpContext context) {
+                count.incrementAndGet();
+                return super.select(challengeType, challenges, context);
+            }
+        });
+        final HttpHost target = start();
+
+        final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
+        credsProvider.setCredentials(AuthScope.ANY,
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future1 = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response1 = future1.get();
+        Assert.assertNotNull(response1);
+        Assert.assertEquals(HttpStatus.SC_OK, response1.getCode());
+
+        final Future<SimpleHttpResponse> future2 = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response2 = future2.get();
+        Assert.assertNotNull(response2);
+        Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
+
+        Assert.assertEquals(1, count.get());
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRequestSuccess() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target.getSchemeName() + "://test:test@" +  target.toHostString() + "/"), context, null);
+        final SimpleHttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRequestFailure() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target.getSchemeName() + "://test:all-worng@" +  target.toHostString() + "/"), context, null);
+        final SimpleHttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRedirectSuccess() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final HttpHost target = start();
+        server.register("/thatway", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AbstractSimpleServerExchangeHandler() {
+
+                    @Override
+                    protected SimpleHttpResponse handle(
+                            final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+                        final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_PERMANENTLY);
+                        response.addHeader(new BasicHeader("Location", target.getSchemeName() + "://test:test@" + target.toHostString() + "/"));
+                        return response;
+                    }
+                };
+            }
+
+        });
+
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target.getSchemeName() + "://test:test@" +  target.toHostString() + "/thatway"), context, null);
+        final SimpleHttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+    }
+
+    @Test
+    public void testReauthentication() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+
+        final Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
+                .register("MyBasic", new AuthSchemeProvider() {
+
+                    @Override
+                    public AuthScheme create(final HttpContext context) {
+                        return new BasicScheme() {
+
+                            @Override
+                            public String getName() {
+                                return "MyBasic";
+                            }
+
+                        };
+                    }
+
+                })
+                .build();
+        setDefaultAuthSchemeRegistry(authSchemeRegistry);
+
+        final Authenticator authenticator = new BasicTestAuthenticator("test:test", "test realm") {
+
+            private final AtomicLong count = new AtomicLong(0);
+
+            @Override
+            public boolean authenticate(final URIAuthority authority, final String requestUri, final String credentials) {
+                final boolean authenticated = super.authenticate(authority, requestUri, credentials);
+                if (authenticated) {
+                    if (this.count.incrementAndGet() % 4 != 0) {
+                        return true;
+                    } else {
+                        return false;
+                    }
+                } else {
+                    return false;
+                }
+            }
+        };
+
+        final HttpHost target = start(
+                new Decorator<AsyncServerExchangeHandler>() {
+
+                    @Override
+                    public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
+                        return new AuthenticatingAsyncDecorator(exchangeHandler, authenticator) {
+
+                            @Override
+                            protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) {
+                                unauthorized.removeHeaders(HttpHeaders.WWW_AUTHENTICATE);
+                                unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, "MyBasic realm=\"test realm\"");
+                            }
+
+                        };
+                    }
+
+                });
+
+        final RequestConfig config = RequestConfig.custom()
+                .setTargetPreferredAuthSchemes(Arrays.asList("MyBasic"))
+                .build();
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        for (int i = 0; i < 10; i++) {
+            final SimpleHttpRequest request = SimpleHttpRequest.get(target, "/");
+            request.setConfig(config);
+            final Future<SimpleHttpResponse> future = httpclient.execute(request, context, null);
+            final SimpleHttpResponse response = future.get();
+            Assert.assertNotNull(response);
+            Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        }
+    }
+
+    @Test
+    public void testAuthenticationFallback() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncEchoHandler();
+            }
+
+        });
+        final HttpHost target = start(
+                new Decorator<AsyncServerExchangeHandler>() {
+
+                    @Override
+                    public AsyncServerExchangeHandler decorate(final AsyncServerExchangeHandler exchangeHandler) {
+                        return new AuthenticatingAsyncDecorator(exchangeHandler, new BasicTestAuthenticator("test:test", "test realm")) {
+
+                            @Override
+                            protected void customizeUnauthorizedResponse(final HttpResponse unauthorized) {
+                                unauthorized.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"test realm\" invalid");
+                            }
+
+                        };
+                    }
+
+                });
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final SimpleHttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6228a736/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncFundamentalsTest.java
----------------------------------------------------------------------
diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncFundamentalsTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncFundamentalsTest.java
new file mode 100644
index 0000000..73c7409
--- /dev/null
+++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncFundamentalsTest.java
@@ -0,0 +1,136 @@
+/*
+ * ====================================================================
+ * 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.util.LinkedList;
+import java.util.Queue;
+import java.util.Random;
+import java.util.concurrent.Future;
+
+import org.apache.hc.client5.http.async.methods.AsyncRequestBuilder;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.Message;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.nio.BasicResponseConsumer;
+import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityConsumer;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+public abstract class AbstractHttpAsyncFundamentalsTest<T extends CloseableHttpAsyncClient> extends AbstractIntegrationTestBase<T> {
+
+    public AbstractHttpAsyncFundamentalsTest(final URIScheme scheme) {
+        super(scheme);
+    }
+
+    @Test
+    public void testSequenctialGetRequests() throws Exception {
+        final HttpHost target = start();
+        for (int i = 0; i < 3; i++) {
+            final Future<SimpleHttpResponse> future = httpclient.execute(
+                    SimpleHttpRequest.get(target, "/random/2048"), null);
+            final SimpleHttpResponse response = future.get();
+            Assert.assertThat(response, CoreMatchers.notNullValue());
+            Assert.assertThat(response.getCode(), CoreMatchers.equalTo(200));
+            final String body = response.getBodyText();
+            Assert.assertThat(body, CoreMatchers.notNullValue());
+            Assert.assertThat(body.length(), CoreMatchers.equalTo(2048));
+        }
+    }
+
+    @Test
+    public void testSequenctialHeadRequests() throws Exception {
+        final HttpHost target = start();
+        for (int i = 0; i < 3; i++) {
+            final Future<SimpleHttpResponse> future = httpclient.execute(
+                    SimpleHttpRequest.head(target, "/random/2048"), null);
+            final SimpleHttpResponse response = future.get();
+            Assert.assertThat(response, CoreMatchers.notNullValue());
+            Assert.assertThat(response.getCode(), CoreMatchers.equalTo(200));
+            final String body = response.getBodyText();
+            Assert.assertThat(body, CoreMatchers.nullValue());
+        }
+    }
+
+    @Test
+    public void testSequenctialPostRequests() throws Exception {
+        final HttpHost target = start();
+        for (int i = 0; i < 3; i++) {
+            final byte[] b1 = new byte[1024];
+            final Random rnd = new Random(System.currentTimeMillis());
+            rnd.nextBytes(b1);
+            final Future<Message<HttpResponse, byte[]>> future = httpclient.execute(
+                    AsyncRequestBuilder.post(target, "/echo/")
+                        .setEntity(b1, ContentType.APPLICATION_OCTET_STREAM)
+                        .build(),
+                    new BasicResponseConsumer<>(new BasicAsyncEntityConsumer()), HttpClientContext.create(), null);
+            final Message<HttpResponse, byte[]> responseMessage = future.get();
+            Assert.assertThat(responseMessage, CoreMatchers.notNullValue());
+            final HttpResponse response = responseMessage.getHead();
+            Assert.assertThat(response.getCode(), CoreMatchers.equalTo(200));
+            final byte[] b2 = responseMessage.getBody();
+            Assert.assertThat(b1, CoreMatchers.equalTo(b2));
+        }
+    }
+
+    @Test
+    public void testConcurrentPostRequests() throws Exception {
+        final HttpHost target = start();
+        final byte[] b1 = new byte[1024];
+        final Random rnd = new Random(System.currentTimeMillis());
+        rnd.nextBytes(b1);
+
+        final int reqCount = 20;
+
+        final Queue<Future<Message<HttpResponse, byte[]>>> queue = new LinkedList<>();
+        for (int i = 0; i < reqCount; i++) {
+            final Future<Message<HttpResponse, byte[]>> future = httpclient.execute(
+                    AsyncRequestBuilder.post(target, "/echo/")
+                            .setEntity(b1, ContentType.APPLICATION_OCTET_STREAM)
+                            .build(),
+                    new BasicResponseConsumer<>(new BasicAsyncEntityConsumer()), HttpClientContext.create(), null);
+            queue.add(future);
+        }
+
+        while (!queue.isEmpty()) {
+            final Future<Message<HttpResponse, byte[]>> future = queue.remove();
+            final Message<HttpResponse, byte[]> responseMessage = future.get();
+            Assert.assertThat(responseMessage, CoreMatchers.notNullValue());
+            final HttpResponse response = responseMessage.getHead();
+            Assert.assertThat(response.getCode(), CoreMatchers.equalTo(200));
+            final byte[] b2 = responseMessage.getBody();
+            Assert.assertThat(b1, CoreMatchers.equalTo(b2));
+        }
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6228a736/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncRedirectsTest.java
----------------------------------------------------------------------
diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncRedirectsTest.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncRedirectsTest.java
new file mode 100644
index 0000000..4649b50
--- /dev/null
+++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractHttpAsyncRedirectsTest.java
@@ -0,0 +1,825 @@
+/*
+ * ====================================================================
+ * 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.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import org.apache.hc.client5.http.CircularRedirectException;
+import org.apache.hc.client5.http.RedirectException;
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.cookie.BasicCookieStore;
+import org.apache.hc.client5.http.cookie.CookieStore;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.client5.testing.SSLTestContexts;
+import org.apache.hc.core5.function.Supplier;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.message.BasicHeader;
+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.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.reactor.ListenerEndpoint;
+import org.apache.hc.core5.testing.nio.Http2TestServer;
+import org.apache.hc.core5.util.TimeValue;
+import org.junit.Assert;
+import org.junit.Test;
+
+public abstract class AbstractHttpAsyncRedirectsTest <T extends CloseableHttpAsyncClient> extends AbstractIntegrationTestBase<T> {
+
+    public AbstractHttpAsyncRedirectsTest(final URIScheme scheme) {
+        super(scheme);
+    }
+
+    static class BasicRedirectService extends AbstractSimpleServerExchangeHandler {
+
+        private final int statuscode;
+
+        public BasicRedirectService(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()));
+                    return response;
+                } else if (path.equals("/newlocation/")) {
+                    final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_OK);
+                    response.setBodyText("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.setBodyText("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.setBodyText("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
+    public void testBasicRedirect300() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BasicRedirectService(HttpStatus.SC_MULTIPLE_CHOICES);
+            }
+
+        });
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_MULTIPLE_CHOICES, response.getCode());
+        Assert.assertEquals("/oldlocation/", request.getRequestUri());
+    }
+
+    @Test
+    public void testBasicRedirect301() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BasicRedirectService(HttpStatus.SC_MOVED_PERMANENTLY);
+            }
+
+        });
+
+        final HttpHost target = start();
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("/newlocation/", request.getRequestUri());
+        Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+    }
+
+    @Test
+    public void testBasicRedirect302() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BasicRedirectService(HttpStatus.SC_MOVED_TEMPORARILY);
+            }
+
+        });
+        final HttpHost target = start();
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("/newlocation/", request.getRequestUri());
+        Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+    }
+
+    @Test
+    public void testBasicRedirect302NoLocation() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AbstractSimpleServerExchangeHandler() {
+
+                    @Override
+                    protected SimpleHttpResponse handle(
+                            final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+                        return new SimpleHttpResponse(HttpStatus.SC_MOVED_TEMPORARILY);
+                    }
+
+                };
+            }
+
+        });
+        final HttpHost target = start();
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+        Assert.assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getCode());
+        Assert.assertEquals("/oldlocation/", request.getRequestUri());
+        Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+    }
+
+    @Test
+    public void testBasicRedirect303() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BasicRedirectService(HttpStatus.SC_SEE_OTHER);
+            }
+
+        });
+        final HttpHost target = start();
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("/newlocation/", request.getRequestUri());
+        Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+    }
+
+    @Test
+    public void testBasicRedirect304() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BasicRedirectService(HttpStatus.SC_NOT_MODIFIED);
+            }
+
+        });
+        final HttpHost target = start();
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_NOT_MODIFIED, response.getCode());
+        Assert.assertEquals("/oldlocation/", request.getRequestUri());
+    }
+
+    @Test
+    public void testBasicRedirect305() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BasicRedirectService(HttpStatus.SC_USE_PROXY);
+            }
+
+        });
+        final HttpHost target = start();
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_USE_PROXY, response.getCode());
+        Assert.assertEquals("/oldlocation/", request.getRequestUri());
+    }
+
+    @Test
+    public void testBasicRedirect307() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BasicRedirectService(HttpStatus.SC_TEMPORARY_REDIRECT);
+            }
+
+        });
+        final HttpHost target = start();
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("/newlocation/", request.getRequestUri());
+        Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+    }
+
+    @Test(expected=ExecutionException.class)
+    public void testMaxRedirectCheck() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new CircularRedirectService();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final RequestConfig config = RequestConfig.custom()
+                .setCircularRedirectsAllowed(true)
+                .setMaxRedirects(5).build();
+        try {
+            final SimpleHttpRequest request = SimpleHttpRequest.get(target, "/circular-oldlocation/");
+            request.setConfig(config);
+            final Future<SimpleHttpResponse> future = httpclient.execute(request, null);
+            future.get();
+        } catch (final ExecutionException e) {
+            Assert.assertTrue(e.getCause() instanceof RedirectException);
+            throw e;
+        }
+    }
+
+    @Test(expected=ExecutionException.class)
+    public void testCircularRedirect() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new CircularRedirectService();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final RequestConfig config = RequestConfig.custom()
+                .setCircularRedirectsAllowed(false)
+                .build();
+        try {
+            final SimpleHttpRequest request = SimpleHttpRequest.get(target, "/circular-oldlocation/");
+            request.setConfig(config);
+            final Future<SimpleHttpResponse> future = httpclient.execute(request, null);
+            future.get();
+        } catch (final ExecutionException e) {
+            Assert.assertTrue(e.getCause() instanceof CircularRedirectException);
+            throw e;
+        }
+    }
+
+    @Test
+    public void testPostRedirectSeeOther() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BasicRedirectService(HttpStatus.SC_SEE_OTHER);
+            }
+
+        });
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final SimpleHttpRequest post = SimpleHttpRequest.post(target, "/oldlocation/");
+        post.setBodyText("stuff", ContentType.TEXT_PLAIN);
+        final Future<SimpleHttpResponse> future = httpclient.execute(post, context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("/newlocation/", request.getRequestUri());
+        Assert.assertEquals("GET", request.getMethod());
+    }
+
+    @Test
+    public void testRelativeRedirect() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new RelativeRedirectService();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("/relativelocation/", request.getRequestUri());
+        Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+    }
+
+    @Test
+    public void testRelativeRedirect2() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new RelativeRedirectService2();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/test/oldlocation"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("/test/relativelocation", request.getRequestUri());
+        Assert.assertEquals(target, new HttpHost(request.getAuthority(), request.getScheme()));
+    }
+
+    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.setBodyText("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)
+    public void testRejectBogusRedirectLocation() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BogusRedirectService("xxx://bogus");
+            }
+
+        });
+        final HttpHost target = start();
+
+        try {
+            final Future<SimpleHttpResponse> future = httpclient.execute(
+                    SimpleHttpRequest.get(target, "/oldlocation/"), null);
+            future.get();
+        } catch (final ExecutionException ex) {
+            Assert.assertTrue(ex.getCause() instanceof HttpException);
+            throw ex;
+        }
+    }
+
+    @Test(expected=ExecutionException.class)
+    public void testRejectInvalidRedirectLocation() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BogusRedirectService("/newlocation/?p=I have spaces");
+            }
+
+        });
+        final HttpHost target = start();
+
+        try {
+            final Future<SimpleHttpResponse> future = httpclient.execute(
+                    SimpleHttpRequest.get(target, "/oldlocation/"), null);
+            future.get();
+        } catch (final ExecutionException e) {
+            Assert.assertTrue(e.getCause() instanceof ProtocolException);
+            throw e;
+        }
+    }
+
+    @Test
+    public void testRedirectWithCookie() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new BasicRedirectService(HttpStatus.SC_MOVED_TEMPORARILY);
+            }
+
+        });
+        final HttpHost target = start();
+
+        final CookieStore cookieStore = new BasicCookieStore();
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCookieStore(cookieStore);
+
+        final BasicClientCookie cookie = new BasicClientCookie("name", "value");
+        cookie.setDomain(target.getHostName());
+        cookie.setPath("/");
+
+        cookieStore.addCookie(cookie);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target, "/oldlocation/"), context, null);
+        final HttpResponse response = future.get();
+        Assert.assertNotNull(response);
+
+        final HttpRequest request = context.getRequest();
+
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("/newlocation/", request.getRequestUri());
+
+        final Header[] headers = request.getHeaders("Cookie");
+        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
+    public void testCrossSiteRedirect() throws Exception {
+        server.register("/random/*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AsyncRandomHandler();
+            }
+
+        });
+        final HttpHost redirectTarget = start();
+
+        final Http2TestServer secondServer = new Http2TestServer(IOReactorConfig.DEFAULT,
+                scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null);
+        try {
+            secondServer.register("/redirect/*", new Supplier<AsyncServerExchangeHandler>() {
+
+                @Override
+                public AsyncServerExchangeHandler get() {
+                    return new CrossSiteRedirectService(redirectTarget);
+                }
+
+            });
+
+            secondServer.start();
+            final Future<ListenerEndpoint> endpointFuture = secondServer.listen(new InetSocketAddress(0));
+            final ListenerEndpoint endpoint2 = endpointFuture.get();
+
+            final InetSocketAddress address2 = (InetSocketAddress) endpoint2.getAddress();
+            final HttpHost initialTarget = new HttpHost("localhost", address2.getPort(), scheme.name());
+
+            final Queue<Future<SimpleHttpResponse>> queue = new ConcurrentLinkedQueue<>();
+            for (int i = 0; i < 1; i++) {
+                queue.add(httpclient.execute(SimpleHttpRequest.get(initialTarget, "/redirect/anywhere"), null));
+            }
+            while (!queue.isEmpty()) {
+                final Future<SimpleHttpResponse> future = queue.remove();
+                final HttpResponse response = future.get();
+                Assert.assertNotNull(response);
+                Assert.assertEquals(200, response.getCode());
+            }
+        } finally {
+            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.setBodyText("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(
+                SimpleHttpRequest.get(target, "/rome"), context, null);
+        final HttpResponse response1 = future1.get();
+        Assert.assertNotNull(response1);
+
+        final Future<SimpleHttpResponse> future2 = httpclient.execute(
+                SimpleHttpRequest.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.getAuthority(), request.getScheme()));
+    }
+
+    @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(
+                SimpleHttpRequest.get(target, "/lille"), context, null);
+        final HttpResponse response1 = future1.get();
+        Assert.assertNotNull(response1);
+
+        final Future<SimpleHttpResponse> future2 = httpclient.execute(
+                SimpleHttpRequest.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.getAuthority(), request.getScheme()));
+    }
+
+    @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(
+                SimpleHttpRequest.get(target, "/alian"), context, null);
+        final HttpResponse response1 = future1.get();
+        Assert.assertNotNull(response1);
+
+        final Future<SimpleHttpResponse> future2 = httpclient.execute(
+                SimpleHttpRequest.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.getAuthority(), request.getScheme()));
+    }
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6228a736/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractIntegrationTestBase.java
----------------------------------------------------------------------
diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractIntegrationTestBase.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractIntegrationTestBase.java
new file mode 100644
index 0000000..c193785
--- /dev/null
+++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractIntegrationTestBase.java
@@ -0,0 +1,114 @@
+/*
+ * ====================================================================
+ * 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.net.InetSocketAddress;
+import java.util.concurrent.Future;
+
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.core5.function.Decorator;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.config.H1Config;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http2.config.H2Config;
+import org.apache.hc.core5.io.ShutdownType;
+import org.apache.hc.core5.reactor.ListenerEndpoint;
+import org.junit.Rule;
+import org.junit.rules.ExternalResource;
+
+public abstract class AbstractIntegrationTestBase<T extends CloseableHttpAsyncClient> extends AbstractServerTestBase {
+
+    public AbstractIntegrationTestBase(final URIScheme scheme) {
+        super(scheme);
+    }
+
+    public AbstractIntegrationTestBase() {
+        super(URIScheme.HTTP);
+    }
+
+    protected T httpclient;
+
+    protected abstract T createClient() throws Exception;
+
+    @Rule
+    public ExternalResource clientResource = new ExternalResource() {
+
+        @Override
+        protected void after() {
+            if (httpclient != null) {
+                httpclient.shutdown(ShutdownType.GRACEFUL);
+                httpclient = null;
+            }
+        }
+
+    };
+
+    public abstract HttpHost start() throws Exception;
+
+    public final HttpHost start(
+            final HttpProcessor httpProcessor,
+            final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator,
+            final H1Config h1Config) throws Exception {
+        server.start(httpProcessor, exchangeHandlerDecorator, h1Config);
+        final Future<ListenerEndpoint> endpointFuture = server.listen(new InetSocketAddress(0));
+        httpclient = createClient();
+        httpclient.start();
+        final ListenerEndpoint endpoint = endpointFuture.get();
+        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
+        return new HttpHost("localhost", address.getPort(), scheme.name());
+    }
+
+    public final HttpHost start(
+            final HttpProcessor httpProcessor,
+            final H1Config h1Config) throws Exception {
+        return start(httpProcessor, null, h1Config);
+    }
+
+    public final HttpHost start(
+            final HttpProcessor httpProcessor,
+            final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator,
+            final H2Config h2Config) throws Exception {
+        server.start(httpProcessor, exchangeHandlerDecorator, h2Config);
+        final Future<ListenerEndpoint> endpointFuture = server.listen(new InetSocketAddress(0));
+        httpclient = createClient();
+        httpclient.start();
+        final ListenerEndpoint endpoint = endpointFuture.get();
+        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
+        return new HttpHost("localhost", address.getPort(), scheme.name());
+    }
+
+
+    public final HttpHost start(
+            final HttpProcessor httpProcessor,
+            final H2Config h2Config) throws Exception {
+        return start(httpProcessor, null, h2Config);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6228a736/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractServerTestBase.java
----------------------------------------------------------------------
diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractServerTestBase.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractServerTestBase.java
new file mode 100644
index 0000000..090dd68
--- /dev/null
+++ b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/AbstractServerTestBase.java
@@ -0,0 +1,96 @@
+/*
+ * ====================================================================
+ * 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 org.apache.hc.client5.testing.SSLTestContexts;
+import org.apache.hc.core5.function.Supplier;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.testing.nio.Http2TestServer;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.hc.core5.util.Timeout;
+import org.junit.Rule;
+import org.junit.rules.ExternalResource;
+
+public abstract class AbstractServerTestBase {
+
+    public static final Timeout TIMEOUT = Timeout.ofSeconds(30);
+    public static final Timeout LONG_TIMEOUT = Timeout.ofSeconds(60);
+
+    protected final URIScheme scheme;
+
+    public AbstractServerTestBase(final URIScheme scheme) {
+        this.scheme = scheme;
+    }
+
+    public AbstractServerTestBase() {
+        this(URIScheme.HTTP);
+    }
+
+    protected Http2TestServer server;
+
+    @Rule
+    public ExternalResource serverResource = new ExternalResource() {
+
+        @Override
+        protected void before() throws Throwable {
+            server = new Http2TestServer(
+                    IOReactorConfig.custom()
+                        .setSoTimeout(TIMEOUT)
+                        .build(),
+                    scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null);
+            server.register("/echo/*", new Supplier<AsyncServerExchangeHandler>() {
+
+                @Override
+                public AsyncServerExchangeHandler get() {
+                    return new AsyncEchoHandler();
+                }
+
+            });
+            server.register("/random/*", new Supplier<AsyncServerExchangeHandler>() {
+
+                @Override
+                public AsyncServerExchangeHandler get() {
+                    return new AsyncRandomHandler();
+                }
+
+            });
+        }
+
+        @Override
+        protected void after() {
+            if (server != null) {
+                server.shutdown(TimeValue.ofSeconds(5));
+                server = null;
+            }
+        }
+
+    };
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6228a736/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java
----------------------------------------------------------------------
diff --git a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java b/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java
deleted file mode 100644
index 3180227..0000000
--- a/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * ====================================================================
- * 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.net.InetSocketAddress;
-import java.util.concurrent.Future;
-
-import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
-import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
-import org.apache.hc.core5.function.Decorator;
-import org.apache.hc.core5.http.HttpHost;
-import org.apache.hc.core5.http.URIScheme;
-import org.apache.hc.core5.http.config.H1Config;
-import org.apache.hc.core5.http.impl.HttpProcessors;
-import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
-import org.apache.hc.core5.http.protocol.HttpProcessor;
-import org.apache.hc.core5.io.ShutdownType;
-import org.apache.hc.core5.reactor.ListenerEndpoint;
-import org.junit.Rule;
-import org.junit.rules.ExternalResource;
-
-public abstract class IntegrationTestBase extends LocalAsyncServerTestBase {
-
-    public IntegrationTestBase(final URIScheme scheme) {
-        super(scheme);
-    }
-
-    public IntegrationTestBase() {
-        super(URIScheme.HTTP);
-    }
-
-    protected HttpAsyncClientBuilder clientBuilder;
-    protected CloseableHttpAsyncClient httpclient;
-
-    @Rule
-    public ExternalResource clientResource = new ExternalResource() {
-
-        @Override
-        protected void before() throws Throwable {
-            clientBuilder = HttpAsyncClientBuilder.create()
-                    .setDefaultRequestConfig(RequestConfig.custom()
-                            .setSocketTimeout(TIMEOUT)
-                            .setConnectTimeout(TIMEOUT)
-                            .setConnectionRequestTimeout(TIMEOUT)
-                            .build())
-                    .setConnectionManager(connManager);
-        }
-
-        @Override
-        protected void after() {
-            if (httpclient != null) {
-                httpclient.shutdown(ShutdownType.GRACEFUL);
-                httpclient = null;
-            }
-        }
-
-    };
-
-    public HttpHost start(
-            final HttpProcessor httpProcessor,
-            final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator,
-            final H1Config h1Config) throws Exception {
-        server.start(httpProcessor, exchangeHandlerDecorator, h1Config);
-        final Future<ListenerEndpoint> endpointFuture = server.listen(new InetSocketAddress(0));
-        httpclient = clientBuilder.build();
-        httpclient.start();
-        final ListenerEndpoint endpoint = endpointFuture.get();
-        final InetSocketAddress address = (InetSocketAddress) endpoint.getAddress();
-        return new HttpHost("localhost", address.getPort(), scheme.name());
-    }
-
-    public HttpHost start(
-            final HttpProcessor httpProcessor,
-            final H1Config h1Config) throws Exception {
-        return start(httpProcessor, null, h1Config);
-    }
-
-    public HttpHost start() throws Exception {
-        return start(HttpProcessors.server(), H1Config.DEFAULT);
-    }
-
-}


Mime
View raw message