hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject [12/50] [abbrv] httpcomponents-core git commit: HTTP protocol version negotiation
Date Tue, 09 May 2017 19:58:04 GMT
http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2TlsSupport.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2TlsSupport.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2TlsSupport.java
index 836ccaa..d4333ad 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2TlsSupport.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2TlsSupport.java
@@ -27,17 +27,19 @@
 
 package org.apache.hc.core5.http2.ssl;
 
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArraySet;
 
+import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLParameters;
 
+import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
+import org.apache.hc.core5.ssl.ReflectionSupport;
 
 public final class H2TlsSupport {
 
@@ -359,54 +361,43 @@ public final class H2TlsSupport {
         return enabledCiphers != null ? enabledCiphers.toArray(new String[enabledCiphers.size()]) : ciphers;
     }
 
-    static void applyParameter(final SSLParameters sslParameters, final String name, final Class type, final Object value) {
-        try {
-            final Class<? extends SSLParameters> clazz = sslParameters.getClass();
-            final Method method = clazz.getMethod("set" + name, type);
-            method.invoke(sslParameters, value);
-        } catch (final Exception ignore) {
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    static <T> T getParameter(final SSLParameters sslParameters, final String name, final Class<T> resultType) {
-        try {
-            final Class<? extends SSLParameters> clazz = sslParameters.getClass();
-            final Method method = clazz.getMethod("get" + name);
-            return resultType.cast(method.invoke(sslParameters));
-        } catch (final Exception ignore) {
-            return null;
-        }
-    }
-
     public static void setEnableRetransmissions(final SSLParameters sslParameters, final boolean value) {
-        applyParameter(sslParameters, "EnableRetransmissions", Boolean.TYPE, value);
+        ReflectionSupport.callSetter(sslParameters, "EnableRetransmissions", Boolean.TYPE, value);
     }
 
     public static void setApplicationProtocols(final SSLParameters sslParameters, final String[] values) {
-        applyParameter(sslParameters, "ApplicationProtocols", String[].class, values);
+        ReflectionSupport.callSetter(sslParameters, "ApplicationProtocols", String[].class, values);
     }
 
-    public static Boolean getEnableRetransmissions(final SSLParameters sslParameters) {
-        return getParameter(sslParameters, "EnableRetransmissions", Boolean.class);
-    }
-
-    public static String[] getApplicationProtocols(final SSLParameters sslParameters) {
-        return getParameter(sslParameters, "ApplicationProtocols", String[].class);
-    }
-
-    public static SSLSessionInitializer enforceRequirements(final SSLSessionInitializer initializer) {
+    public static SSLSessionInitializer enforceRequirements(
+            final Object attachment,
+            final SSLSessionInitializer initializer) {
         return new SSLSessionInitializer() {
 
             @Override
-            public void initialize(final NamedEndpoint endpoint, final SSLParameters sslParameters) {
+            public void initialize(final NamedEndpoint endpoint, final SSLEngine sslEngine) {
+                final SSLParameters sslParameters = sslEngine.getSSLParameters();
                 sslParameters.setProtocols(excludeBlacklistedProtocols(sslParameters.getProtocols()));
                 sslParameters.setCipherSuites(excludeBlacklistedCiphers(sslParameters.getCipherSuites()));
                 setEnableRetransmissions(sslParameters, false);
-                setApplicationProtocols(sslParameters, new String[] { "h2" });
-
+                final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ?
+                        (HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE;
+                final String[] appProtocols;
+                switch (versionPolicy) {
+                    case FORCE_HTTP_1:
+                        appProtocols = new String[] { ApplicationProtocols.HTTP_1_1.id };
+                        break;
+                    case FORCE_HTTP_2:
+                        appProtocols = new String[] { ApplicationProtocols.HTTP_2.id };
+                        break;
+                    default:
+                        appProtocols = new String[] { ApplicationProtocols.HTTP_2.id, ApplicationProtocols.HTTP_1_1.id };
+                        break;
+                }
+                setApplicationProtocols(sslParameters, appProtocols);
+                sslEngine.setSSLParameters(sslParameters);
                 if (initializer != null) {
-                    initializer.initialize(endpoint, sslParameters);
+                    initializer.initialize(endpoint, sslEngine);
                 }
             }
 

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/SecurePortStrategy.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/SecurePortStrategy.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/SecurePortStrategy.java
new file mode 100644
index 0000000..3a4538d
--- /dev/null
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/SecurePortStrategy.java
@@ -0,0 +1,41 @@
+/*
+ * ====================================================================
+ * 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.core5.http2.ssl;
+
+import java.net.SocketAddress;
+
+/**
+ * Side-side strategy to determine if local endpoint should be secured with TLS.
+ *
+ * @since 5.0
+ */
+public interface SecurePortStrategy {
+
+    boolean isSecure(SocketAddress localAddress);
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestClient.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestClient.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestClient.java
index f846498..b956aa0 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestClient.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestClient.java
@@ -38,8 +38,10 @@ import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.config.CharCodingConfig;
+import org.apache.hc.core5.http.config.H1Config;
 import org.apache.hc.core5.http.nio.AsyncPushConsumer;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.http2.config.H2Config;
 import org.apache.hc.core5.http2.impl.Http2Processors;
 import org.apache.hc.core5.http2.impl.nio.bootstrap.AsyncPushConsumerRegistry;
@@ -80,8 +82,21 @@ public class Http2TestClient extends AsyncRequester {
         start(new InternalClientHttp2EventHandlerFactory(
                 httpProcessor,
                 pushConsumerRegistry,
-                CharCodingConfig.DEFAULT,
+                HttpVersionPolicy.FORCE_HTTP_2,
                 h2Config,
+                H1Config.DEFAULT,
+                CharCodingConfig.DEFAULT,
+                sslContext));
+    }
+
+    public void start(final HttpProcessor httpProcessor, final H1Config h1Config) throws IOException {
+        start(new InternalClientHttp2EventHandlerFactory(
+                httpProcessor,
+                pushConsumerRegistry,
+                HttpVersionPolicy.FORCE_HTTP_1,
+                H2Config.DEFAULT,
+                h1Config,
+                CharCodingConfig.DEFAULT,
                 sslContext));
     }
 
@@ -89,6 +104,10 @@ public class Http2TestClient extends AsyncRequester {
         start(Http2Processors.client(), h2Config);
     }
 
+    public void start(final H1Config h1Config) throws IOException {
+        start(Http2Processors.client(), h1Config);
+    }
+
     public void start() throws Exception {
         start(H2Config.DEFAULT);
     }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
index cbc0f7f..3264cb6 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
@@ -35,7 +35,6 @@ import javax.net.ssl.SSLContext;
 import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.config.H1Config;
-import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
 import org.apache.hc.core5.http.impl.HttpProcessors;
 import org.apache.hc.core5.http.impl.bootstrap.AsyncServerExchangeHandlerRegistry;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
@@ -43,6 +42,7 @@ import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
 import org.apache.hc.core5.http.nio.support.RequestConsumerSupplier;
 import org.apache.hc.core5.http.nio.support.ResponseHandler;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.http2.config.H2Config;
 import org.apache.hc.core5.http2.impl.Http2Processors;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
@@ -90,8 +90,10 @@ public class Http2TestServer extends AsyncServer {
         start(new InternalServerHttp2EventHandlerFactory(
                 httpProcessor,
                 handlerRegistry,
-                CharCodingConfig.DEFAULT,
+                HttpVersionPolicy.FORCE_HTTP_2,
                 h2Config,
+                H1Config.DEFAULT,
+                CharCodingConfig.DEFAULT,
                 sslContext));
         final ListenerEndpoint listener = listen(new InetSocketAddress(0));
         listener.waitFor();
@@ -99,12 +101,13 @@ public class Http2TestServer extends AsyncServer {
     }
 
     public InetSocketAddress start(final HttpProcessor httpProcessor, final H1Config h1Config) throws Exception {
-        start(new InternalServerHttp1EventHandlerFactory(
+        start(new InternalServerHttp2EventHandlerFactory(
                 httpProcessor,
                 handlerRegistry,
+                HttpVersionPolicy.FORCE_HTTP_1,
+                H2Config.DEFAULT,
                 h1Config,
                 CharCodingConfig.DEFAULT,
-                DefaultConnectionReuseStrategy.INSTANCE,
                 sslContext));
         final ListenerEndpoint listener = listen(new InetSocketAddress(0));
         listener.waitFor();

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp1EventHandlerFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp1EventHandlerFactory.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp1EventHandlerFactory.java
index 75a2f62..e32cee7 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp1EventHandlerFactory.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp1EventHandlerFactory.java
@@ -27,17 +27,12 @@
 
 package org.apache.hc.core5.testing.nio;
 
-import java.util.Iterator;
-
 import javax.net.ssl.SSLContext;
 
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
-import org.apache.hc.core5.http.ConnectionClosedException;
 import org.apache.hc.core5.http.ConnectionReuseStrategy;
 import org.apache.hc.core5.http.ContentLengthStrategy;
-import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.HttpConnection;
 import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.config.CharCodingConfig;
@@ -50,8 +45,6 @@ import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler;
 import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexer;
 import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestWriterFactory;
 import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseParserFactory;
-import org.apache.hc.core5.http.message.RequestLine;
-import org.apache.hc.core5.http.message.StatusLine;
 import org.apache.hc.core5.http.nio.NHttpMessageParser;
 import org.apache.hc.core5.http.nio.NHttpMessageParserFactory;
 import org.apache.hc.core5.http.nio.NHttpMessageWriter;
@@ -120,7 +113,6 @@ class InternalClientHttp1EventHandlerFactory implements IOEventHandlerFactory {
         }
         final Logger sessionLog = LogManager.getLogger(ioSession.getClass());
         final Logger wireLog = LogManager.getLogger("org.apache.hc.core5.http.wire");
-        final Logger headerLog = LogManager.getLogger("org.apache.hc.core5.http.headers");
         final ClientHttp1StreamDuplexer streamDuplexer = createClientHttp1StreamDuplexer(
                 new LoggingIOSession(ioSession, sessionLog, wireLog),
                 httpProcessor,
@@ -131,65 +123,8 @@ class InternalClientHttp1EventHandlerFactory implements IOEventHandlerFactory {
                 requestWriterFactory.create(),
                 DefaultContentLengthStrategy.INSTANCE,
                 DefaultContentLengthStrategy.INSTANCE,
-                new ConnectionListener() {
-
-                    @Override
-                    public void onConnect(final HttpConnection connection) {
-                        if (sessionLog.isDebugEnabled()) {
-                            sessionLog.debug(id + " "  + connection + " connected");
-                        }
-                    }
-
-                    @Override
-                    public void onDisconnect(final HttpConnection connection) {
-                        if (sessionLog.isDebugEnabled()) {
-                            sessionLog.debug(id + " "  + connection + " disconnected");
-                        }
-                    }
-
-                    @Override
-                    public void onError(final HttpConnection connection, final Exception ex) {
-                        if (ex instanceof ConnectionClosedException) {
-                            return;
-                        }
-                        sessionLog.error(id + " "  + ex.getMessage(), ex);
-                    }
-
-                },
-                new Http1StreamListener() {
-
-                    @Override
-                    public void onRequestHead(final HttpConnection connection, final HttpRequest request) {
-                        if (headerLog.isDebugEnabled()) {
-                            headerLog.debug(id + " >> " + new RequestLine(request));
-                            for (final Iterator<Header> it = request.headerIterator(); it.hasNext(); ) {
-                                headerLog.debug(id + " >> " + it.next());
-                            }
-                        }
-                    }
-
-                    @Override
-                    public void onResponseHead(final HttpConnection connection, final HttpResponse response) {
-                        if (headerLog.isDebugEnabled()) {
-                            headerLog.debug(id + " << " + new StatusLine(response));
-                            for (final Iterator<Header> it = response.headerIterator(); it.hasNext(); ) {
-                                headerLog.debug(id + " << " + it.next());
-                            }
-                        }
-                    }
-
-                    @Override
-                    public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) {
-                        if (sessionLog.isDebugEnabled()) {
-                            if (keepAlive) {
-                                sessionLog.debug(id + " Connection is kept alive");
-                            } else {
-                                sessionLog.debug(id + " Connection is not kept alive");
-                            }
-                        }
-                    }
-
-                });
+                new InternalConnectionListener(id, sessionLog),
+                new InternalHttp1StreamListener(id, InternalHttp1StreamListener.Type.CLIENT, sessionLog));
         return new LoggingIOEventHandler(new ClientHttp1IOEventHandler(streamDuplexer), id, sessionLog);
     }
 

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp2EventHandlerFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp2EventHandlerFactory.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp2EventHandlerFactory.java
index 8f50052..fcbaf8a 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp2EventHandlerFactory.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientHttp2EventHandlerFactory.java
@@ -29,15 +29,17 @@ package org.apache.hc.core5.testing.nio;
 
 import javax.net.ssl.SSLContext;
 
-import org.apache.hc.core5.http.ConnectionClosedException;
-import org.apache.hc.core5.http.HttpConnection;
 import org.apache.hc.core5.http.config.CharCodingConfig;
-import org.apache.hc.core5.http.impl.ConnectionListener;
+import org.apache.hc.core5.http.config.H1Config;
+import org.apache.hc.core5.http.impl.HttpProcessors;
+import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.http.nio.AsyncPushConsumer;
 import org.apache.hc.core5.http.nio.HandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.http2.config.H2Config;
-import org.apache.hc.core5.http2.impl.nio.ClientHttp2StreamMultiplexer;
+import org.apache.hc.core5.http2.impl.Http2Processors;
+import org.apache.hc.core5.http2.impl.nio.ClientHttp2StreamMultiplexerFactory;
 import org.apache.hc.core5.http2.impl.nio.ClientHttpProtocolNegotiator;
 import org.apache.hc.core5.reactor.IOEventHandler;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
@@ -50,20 +52,26 @@ class InternalClientHttp2EventHandlerFactory implements IOEventHandlerFactory {
 
     private final HttpProcessor httpProcessor;
     private final HandlerFactory<AsyncPushConsumer> exchangeHandlerFactory;
-    private final CharCodingConfig charCodingConfig;
+    private final HttpVersionPolicy versionPolicy;
     private final H2Config h2Config;
+    private final H1Config h1Config;
+    private final CharCodingConfig charCodingConfig;
     private final SSLContext sslContext;
 
     InternalClientHttp2EventHandlerFactory(
             final HttpProcessor httpProcessor,
             final HandlerFactory<AsyncPushConsumer> exchangeHandlerFactory,
-            final CharCodingConfig charCodingConfig,
+            final HttpVersionPolicy versionPolicy,
             final H2Config h2Config,
+            final H1Config h1Config,
+            final CharCodingConfig charCodingConfig,
             final SSLContext sslContext) {
         this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
         this.exchangeHandlerFactory = exchangeHandlerFactory;
-        this.charCodingConfig = charCodingConfig;
-        this.h2Config = h2Config;
+        this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
+        this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT;
+        this.h1Config = h1Config != null ? h1Config : H1Config.DEFAULT;
+        this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT;
         this.sslContext = sslContext;
     }
 
@@ -74,40 +82,28 @@ class InternalClientHttp2EventHandlerFactory implements IOEventHandlerFactory {
             ioSession.startTls(sslContext, null ,null, null);
         }
         final Logger sessionLog = LogManager.getLogger(ioSession.getClass());
-        return new LoggingIOEventHandler(new ClientHttpProtocolNegotiator(
-                ioSession, httpProcessor, exchangeHandlerFactory, charCodingConfig, h2Config,
-                new ConnectionListener() {
-
-                    @Override
-                    public void onConnect(final HttpConnection connection) {
-                        if (sessionLog.isDebugEnabled()) {
-                            sessionLog.debug(id + " "  + connection + " connected");
-                        }
-                    }
-
-                    @Override
-                    public void onDisconnect(final HttpConnection connection) {
-                        if (sessionLog.isDebugEnabled()) {
-                            sessionLog.debug(id + " "  + connection + " disconnected");
-                        }
-                    }
-
-                    @Override
-                    public void onError(final HttpConnection connection, final Exception ex) {
-                        if (ex instanceof ConnectionClosedException) {
-                            return;
-                        }
-                        sessionLog.error(id + " "  + ex.getMessage(), ex);
-                    }
-
-                }, new InternalHttp2StreamListener(id)) {
-
-            @Override
-            protected ClientHttp2StreamMultiplexer createStreamMultiplexer(final TlsCapableIOSession ioSession) {
-                return super.createStreamMultiplexer(new LoggingIOSession(ioSession, sessionLog));
-            }
-        }, id, sessionLog);
-
+        final LoggingIOSession loggingIOSession = new LoggingIOSession(ioSession, sessionLog);
+        final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory(
+                httpProcessor != null ? httpProcessor : HttpProcessors.client(),
+                h1Config,
+                charCodingConfig,
+                new InternalConnectionListener(id, sessionLog),
+                new InternalHttp1StreamListener(id, InternalHttp1StreamListener.Type.CLIENT, sessionLog));
+        final ClientHttp2StreamMultiplexerFactory http2StreamHandlerFactory = new ClientHttp2StreamMultiplexerFactory(
+                httpProcessor != null ? httpProcessor : Http2Processors.client(),
+                exchangeHandlerFactory,
+                h2Config,
+                charCodingConfig,
+                new InternalConnectionListener(id, sessionLog),
+                new InternalHttp2StreamListener(id));
+        return new LoggingIOEventHandler(
+                new ClientHttpProtocolNegotiator(
+                        loggingIOSession,
+                        http1StreamHandlerFactory,
+                        http2StreamHandlerFactory,
+                        versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
+                        new InternalConnectionListener(id, sessionLog)),
+                id, sessionLog);
    }
 
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalConnectionListener.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalConnectionListener.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalConnectionListener.java
new file mode 100644
index 0000000..5c0ef96
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalConnectionListener.java
@@ -0,0 +1,67 @@
+/*
+ * ====================================================================
+ * 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.core5.testing.nio;
+
+import org.apache.hc.core5.http.ConnectionClosedException;
+import org.apache.hc.core5.http.HttpConnection;
+import org.apache.hc.core5.http.impl.ConnectionListener;
+import org.apache.logging.log4j.Logger;
+
+class InternalConnectionListener implements ConnectionListener {
+
+    private final String id;
+    private final Logger log;
+
+    public InternalConnectionListener(final String id, final Logger log) {
+        this.log = log;
+        this.id = id;
+    }
+
+    @Override
+    public void onConnect(final HttpConnection connection) {
+        if (log.isDebugEnabled()) {
+            log.debug(id + " " + connection + " connected");
+        }
+    }
+
+    @Override
+    public void onDisconnect(final HttpConnection connection) {
+        if (log.isDebugEnabled()) {
+            log.debug(id + " " + connection + " disconnected");
+        }
+    }
+
+    @Override
+    public void onError(final HttpConnection connection, final Exception ex) {
+        if (ex instanceof ConnectionClosedException) {
+            return;
+        }
+        log.error(id + " " + ex.getMessage(), ex);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalHttp1StreamListener.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalHttp1StreamListener.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalHttp1StreamListener.java
new file mode 100644
index 0000000..48b9608
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalHttp1StreamListener.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.core5.testing.nio;
+
+import java.util.Iterator;
+
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpConnection;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.impl.Http1StreamListener;
+import org.apache.hc.core5.http.message.RequestLine;
+import org.apache.hc.core5.http.message.StatusLine;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+class InternalHttp1StreamListener implements Http1StreamListener {
+
+    enum Type { CLIENT, SERVER }
+
+    private final String id;
+    private final Type type;
+    private final Logger sessionLog;
+    private final Logger headerLog = LogManager.getLogger("org.apache.hc.core5.http.headers");
+
+    public InternalHttp1StreamListener(final String id, final Type type, final Logger sessionLog) {
+        this.id = id;
+        this.type = type;
+        this.sessionLog = sessionLog;
+    }
+
+    private String requestDirection() {
+        return type == Type.CLIENT ? " >> " : " << ";
+    }
+
+    private String responseDirection() {
+        return type == Type.CLIENT ? " << " : " >> ";
+    }
+
+    @Override
+    public void onRequestHead(final HttpConnection connection, final HttpRequest request) {
+        if (headerLog.isDebugEnabled()) {
+            headerLog.debug(id + requestDirection() + new RequestLine(request));
+            for (final Iterator<Header> it = request.headerIterator(); it.hasNext(); ) {
+                headerLog.debug(id + requestDirection() + it.next());
+            }
+        }
+    }
+
+    @Override
+    public void onResponseHead(final HttpConnection connection, final HttpResponse response) {
+        if (headerLog.isDebugEnabled()) {
+            headerLog.debug(id + responseDirection() + new StatusLine(response));
+            for (final Iterator<Header> it = response.headerIterator(); it.hasNext(); ) {
+                headerLog.debug(id + responseDirection() + it.next());
+            }
+        }
+    }
+
+    @Override
+    public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) {
+        if (sessionLog.isDebugEnabled()) {
+            if (keepAlive) {
+                sessionLog.debug(id + " Connection is kept alive");
+            } else {
+                sessionLog.debug(id + " Connection is not kept alive");
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp1EventHandlerFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp1EventHandlerFactory.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp1EventHandlerFactory.java
index a051d51..bf1ad15 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp1EventHandlerFactory.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp1EventHandlerFactory.java
@@ -27,17 +27,12 @@
 
 package org.apache.hc.core5.testing.nio;
 
-import java.util.Iterator;
-
 import javax.net.ssl.SSLContext;
 
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
-import org.apache.hc.core5.http.ConnectionClosedException;
 import org.apache.hc.core5.http.ConnectionReuseStrategy;
 import org.apache.hc.core5.http.ContentLengthStrategy;
-import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.HttpConnection;
 import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.config.CharCodingConfig;
@@ -50,8 +45,6 @@ import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestParserFactory;
 import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseWriterFactory;
 import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandler;
 import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexer;
-import org.apache.hc.core5.http.message.RequestLine;
-import org.apache.hc.core5.http.message.StatusLine;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.HandlerFactory;
 import org.apache.hc.core5.http.nio.NHttpMessageParser;
@@ -125,7 +118,6 @@ class InternalServerHttp1EventHandlerFactory implements IOEventHandlerFactory {
         }
         final Logger sessionLog = LogManager.getLogger(ioSession.getClass());
         final Logger wireLog = LogManager.getLogger("org.apache.hc.core5.http.wire");
-        final Logger headerLog = LogManager.getLogger("org.apache.hc.core5.http.headers");
 
         final ServerHttp1StreamDuplexer streamDuplexer = createServerHttp1StreamDuplexer(
                 new LoggingIOSession(ioSession, sessionLog, wireLog),
@@ -138,65 +130,8 @@ class InternalServerHttp1EventHandlerFactory implements IOEventHandlerFactory {
                 responseWriterFactory.create(),
                 DefaultContentLengthStrategy.INSTANCE,
                 DefaultContentLengthStrategy.INSTANCE,
-                new ConnectionListener() {
-
-                    @Override
-                    public void onConnect(final HttpConnection connection) {
-                        if (sessionLog.isDebugEnabled()) {
-                            sessionLog.debug(id + " "  + connection + " connected");
-                        }
-                    }
-
-                    @Override
-                    public void onDisconnect(final HttpConnection connection) {
-                        if (sessionLog.isDebugEnabled()) {
-                            sessionLog.debug(id + " "  + connection + " disconnected");
-                        }
-                    }
-
-                    @Override
-                    public void onError(final HttpConnection connection, final Exception ex) {
-                        if (ex instanceof ConnectionClosedException) {
-                            return;
-                        }
-                        sessionLog.error(id + " "  + ex.getMessage(), ex);
-                    }
-
-                },
-                new Http1StreamListener() {
-
-                    @Override
-                    public void onRequestHead(final HttpConnection connection, final HttpRequest request) {
-                        if (headerLog.isDebugEnabled()) {
-                            headerLog.debug(id + " << " + new RequestLine(request));
-                            for (final Iterator<Header> it = request.headerIterator(); it.hasNext(); ) {
-                                headerLog.debug(id + " << " + it.next());
-                            }
-                        }
-                    }
-
-                    @Override
-                    public void onResponseHead(final HttpConnection connection, final HttpResponse response) {
-                        if (headerLog.isDebugEnabled()) {
-                            headerLog.debug(id + " >> " + new StatusLine(response));
-                            for (final Iterator<Header> it = response.headerIterator(); it.hasNext(); ) {
-                                headerLog.debug(id + " >> " + it.next());
-                            }
-                        }
-                    }
-
-                    @Override
-                    public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) {
-                        if (sessionLog.isDebugEnabled()) {
-                            if (keepAlive) {
-                                sessionLog.debug(id + " Connection is kept alive");
-                            } else {
-                                sessionLog.debug(id + " Connection is not kept alive");
-                            }
-                        }
-                    }
-
-                });
+                new InternalConnectionListener(id, sessionLog),
+                new InternalHttp1StreamListener(id, InternalHttp1StreamListener.Type.SERVER, sessionLog));
         return new LoggingIOEventHandler(new ServerHttp1IOEventHandler(streamDuplexer), id, sessionLog);
     }
 

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp2EventHandlerFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp2EventHandlerFactory.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp2EventHandlerFactory.java
index bed94ed..4a69374 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp2EventHandlerFactory.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerHttp2EventHandlerFactory.java
@@ -29,15 +29,17 @@ package org.apache.hc.core5.testing.nio;
 
 import javax.net.ssl.SSLContext;
 
-import org.apache.hc.core5.http.ConnectionClosedException;
-import org.apache.hc.core5.http.HttpConnection;
 import org.apache.hc.core5.http.config.CharCodingConfig;
-import org.apache.hc.core5.http.impl.ConnectionListener;
+import org.apache.hc.core5.http.config.H1Config;
+import org.apache.hc.core5.http.impl.HttpProcessors;
+import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.HandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.http2.config.H2Config;
-import org.apache.hc.core5.http2.impl.nio.ServerHttp2StreamMultiplexer;
+import org.apache.hc.core5.http2.impl.Http2Processors;
+import org.apache.hc.core5.http2.impl.nio.ServerHttp2StreamMultiplexerFactory;
 import org.apache.hc.core5.http2.impl.nio.ServerHttpProtocolNegotiator;
 import org.apache.hc.core5.reactor.IOEventHandler;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
@@ -50,20 +52,26 @@ class InternalServerHttp2EventHandlerFactory implements IOEventHandlerFactory {
 
     private final HttpProcessor httpProcessor;
     private final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory;
-    private final CharCodingConfig charCodingConfig;
+    private final HttpVersionPolicy versionPolicy;
     private final H2Config h2Config;
+    private final H1Config h1Config;
+    private final CharCodingConfig charCodingConfig;
     private final SSLContext sslContext;
 
     public InternalServerHttp2EventHandlerFactory(
             final HttpProcessor httpProcessor,
             final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory,
-            final CharCodingConfig charCodingConfig,
+            final HttpVersionPolicy versionPolicy,
             final H2Config h2Config,
+            final H1Config h1Config,
+            final CharCodingConfig charCodingConfig,
             final SSLContext sslContext) {
         this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
         this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Exchange handler factory");
-        this.charCodingConfig = charCodingConfig;
-        this.h2Config = h2Config;
+        this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
+        this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT;
+        this.h1Config = h1Config != null ? h1Config : H1Config.DEFAULT;
+        this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT;
         this.sslContext = sslContext;
     }
 
@@ -74,39 +82,29 @@ class InternalServerHttp2EventHandlerFactory implements IOEventHandlerFactory {
             ioSession.startTls(sslContext, null ,null, null);
         }
         final Logger sessionLog = LogManager.getLogger(ioSession.getClass());
-        return new LoggingIOEventHandler(new ServerHttpProtocolNegotiator(
-                ioSession, httpProcessor, exchangeHandlerFactory, charCodingConfig, h2Config,
-                new ConnectionListener() {
-
-                    @Override
-                    public void onConnect(final HttpConnection connection) {
-                        if (sessionLog.isDebugEnabled()) {
-                            sessionLog.debug(id + " "  + connection + " connected");
-                        }
-                    }
-
-                    @Override
-                    public void onDisconnect(final HttpConnection connection) {
-                        if (sessionLog.isDebugEnabled()) {
-                            sessionLog.debug(id + " "  + connection + " disconnected");
-                        }
-                    }
-
-                    @Override
-                    public void onError(final HttpConnection connection, final Exception ex) {
-                        if (ex instanceof ConnectionClosedException) {
-                            return;
-                        }
-                        sessionLog.error(id + " "  + ex.getMessage(), ex);
-                    }
-
-                }, new InternalHttp2StreamListener(id)) {
-
-            @Override
-            protected ServerHttp2StreamMultiplexer createStreamMultiplexer(final TlsCapableIOSession ioSession) {
-                return super.createStreamMultiplexer(new LoggingIOSession(ioSession, sessionLog));
-            }
-        }, id, sessionLog);
-
+        final LoggingIOSession loggingIOSession = new LoggingIOSession(ioSession, sessionLog);
+        final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ServerHttp1StreamDuplexerFactory(
+                httpProcessor != null ? httpProcessor : HttpProcessors.server(),
+                exchangeHandlerFactory,
+                h1Config,
+                charCodingConfig,
+                new InternalConnectionListener(id, sessionLog),
+                new InternalHttp1StreamListener(id, InternalHttp1StreamListener.Type.SERVER, sessionLog));
+        final ServerHttp2StreamMultiplexerFactory http2StreamHandlerFactory = new ServerHttp2StreamMultiplexerFactory(
+                httpProcessor != null ? httpProcessor : Http2Processors.server(),
+                exchangeHandlerFactory,
+                h2Config,
+                charCodingConfig,
+                new InternalConnectionListener(id, sessionLog),
+                new InternalHttp2StreamListener(id));
+        return new LoggingIOEventHandler(
+                new ServerHttpProtocolNegotiator(
+                        loggingIOSession,
+                        http1StreamHandlerFactory,
+                        http2StreamHandlerFactory,
+                        versionPolicy,
+                        new InternalConnectionListener(id, sessionLog)),
+                id, sessionLog);
     }
+
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingIOSession.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingIOSession.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingIOSession.java
index 096c03f..5ddefe0 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingIOSession.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/LoggingIOSession.java
@@ -34,7 +34,6 @@ import java.nio.channels.ByteChannel;
 import java.nio.channels.SelectionKey;
 
 import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSession;
 
 import org.apache.hc.core5.io.ShutdownType;
 import org.apache.hc.core5.reactor.Command;
@@ -43,6 +42,7 @@ import org.apache.hc.core5.reactor.TlsCapableIOSession;
 import org.apache.hc.core5.reactor.ssl.SSLBufferManagement;
 import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
 import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
+import org.apache.hc.core5.reactor.ssl.TlsDetails;
 import org.apache.hc.core5.testing.classic.Wire;
 import org.apache.logging.log4j.Logger;
 public class LoggingIOSession implements TlsCapableIOSession {
@@ -197,6 +197,11 @@ public class LoggingIOSession implements TlsCapableIOSession {
     }
 
     @Override
+    public TlsDetails getTlsDetails() {
+        return session.getTlsDetails();
+    }
+
+    @Override
     public void startTls(
             final SSLContext sslContext,
             final SSLBufferManagement sslBufferManagement,
@@ -206,11 +211,6 @@ public class LoggingIOSession implements TlsCapableIOSession {
     }
 
     @Override
-    public SSLSession getSSLSession() {
-        return session.getSSLSession();
-    }
-
-    @Override
     public String toString() {
         return this.session.toString();
     }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/SSLTestContexts.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/SSLTestContexts.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/SSLTestContexts.java
new file mode 100644
index 0000000..6668f7a
--- /dev/null
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/SSLTestContexts.java
@@ -0,0 +1,55 @@
+/*
+ * ====================================================================
+ * 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.core5.testing;
+
+import java.net.URL;
+
+import javax.net.ssl.SSLContext;
+
+import org.apache.hc.core5.ssl.SSLContextBuilder;
+
+public final class SSLTestContexts {
+
+    public static SSLContext createServerSSLContext() throws Exception {
+        final URL keyStoreURL = SSLTestContexts.class.getResource("/test.keystore");
+        final String storePassword = "nopassword";
+        return SSLContextBuilder.create()
+                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
+                .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
+                .build();
+    }
+
+    public static SSLContext createClientSSLContext() throws Exception {
+        final URL keyStoreURL = SSLTestContexts.class.getResource("/test.keystore");
+        final String storePassword = "nopassword";
+        return SSLContextBuilder.create()
+                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
+                .build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
index 6e0af70..2d0430a 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
@@ -124,6 +124,7 @@ import org.apache.hc.core5.reactor.IOSession;
 import org.apache.hc.core5.reactor.SessionRequest;
 import org.apache.hc.core5.reactor.TlsCapableIOSession;
 import org.apache.hc.core5.testing.ProtocolScheme;
+import org.apache.hc.core5.testing.SSLTestContexts;
 import org.apache.hc.core5.util.CharArrayBuffer;
 import org.apache.hc.core5.util.TextUtils;
 import org.apache.hc.core5.util.TimeValue;
@@ -158,7 +159,7 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
     public void setup() throws Exception {
         client = new Http1TestClient(
                 IOReactorConfig.DEFAULT,
-                scheme == ProtocolScheme.HTTPS ? createClientSSLContext() : null);
+                scheme == ProtocolScheme.HTTPS ? SSLTestContexts.createClientSSLContext() : null);
     }
 
     @After
@@ -1353,7 +1354,7 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                 H1Config.DEFAULT,
                 CharCodingConfig.DEFAULT,
                 DefaultConnectionReuseStrategy.INSTANCE,
-                scheme == ProtocolScheme.HTTPS ? createServerSSLContext() : null) {
+                scheme == ProtocolScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null) {
 
             @Override
             protected ServerHttp1StreamDuplexer createServerHttp1StreamDuplexer(

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2IntegrationTest.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2IntegrationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2IntegrationTest.java
index b67f8d9..438e6db 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2IntegrationTest.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2IntegrationTest.java
@@ -111,6 +111,7 @@ import org.apache.hc.core5.reactor.IOReactorConfig;
 import org.apache.hc.core5.reactor.IOSession;
 import org.apache.hc.core5.reactor.SessionRequest;
 import org.apache.hc.core5.testing.ProtocolScheme;
+import org.apache.hc.core5.testing.SSLTestContexts;
 import org.apache.hc.core5.util.TextUtils;
 import org.apache.hc.core5.util.TimeValue;
 import org.hamcrest.CoreMatchers;
@@ -144,7 +145,7 @@ public class Http2IntegrationTest extends InternalHttp2ServerTestBase {
     @Before
     public void setup() throws Exception {
         client = new Http2TestClient(IOReactorConfig.DEFAULT,
-                scheme == ProtocolScheme.HTTPS ? createClientSSLContext() : null);
+                scheme == ProtocolScheme.HTTPS ? SSLTestContexts.createClientSSLContext() : null);
     }
 
     @After

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2ProtocolNegotiationTest.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2ProtocolNegotiationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2ProtocolNegotiationTest.java
new file mode 100644
index 0000000..4fe5b0e
--- /dev/null
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2ProtocolNegotiationTest.java
@@ -0,0 +1,251 @@
+/*
+ * ====================================================================
+ * 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.core5.testing.nio;
+
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.util.concurrent.Future;
+
+import org.apache.hc.core5.function.Supplier;
+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.HttpStatus;
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.Message;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
+import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.nio.BasicRequestProducer;
+import org.apache.hc.core5.http.nio.BasicResponseConsumer;
+import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
+import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap;
+import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap;
+import org.apache.hc.core5.http2.impl.nio.bootstrap.Http2AsyncRequester;
+import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy;
+import org.apache.hc.core5.http2.ssl.H2ServerTlsStrategy;
+import org.apache.hc.core5.http2.ssl.SecurePortStrategy;
+import org.apache.hc.core5.io.ShutdownType;
+import org.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.reactor.ListenerEndpoint;
+import org.apache.hc.core5.testing.SSLTestContexts;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.logging.log4j.LogManager;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExternalResource;
+
+public class Http2ProtocolNegotiationTest {
+
+    private static final TimeValue TIMEOUT = TimeValue.ofSeconds(30);
+
+    private HttpAsyncServer server;
+
+    @Rule
+    public ExternalResource serverResource = new ExternalResource() {
+
+        @Override
+        protected void before() throws Throwable {
+            server = H2ServerBootstrap.bootstrap()
+                    .setTlsStrategy(new H2ServerTlsStrategy(SSLTestContexts.createServerSSLContext(), new SecurePortStrategy() {
+
+                        @Override
+                        public boolean isSecure(final SocketAddress localAddress) {
+                            return true;
+                        }
+
+                    }))
+                    .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
+                    .setIOReactorConfig(
+                            IOReactorConfig.custom()
+                                    .setSoTimeout(TIMEOUT)
+                                    .build())
+                    .register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+                        @Override
+                        public AsyncServerExchangeHandler get() {
+                            return new EchoHandler(2048);
+                        }
+
+                    })
+                    .setConnectionListener(new InternalConnectionListener("test", LogManager.getLogger(getClass())))
+                    .create();
+        }
+
+        @Override
+        protected void after() {
+            if (server != null) {
+                try {
+                    server.shutdown(ShutdownType.IMMEDIATE);
+                    server = null;
+                } catch (final Exception ignore) {
+                }
+            }
+        }
+
+    };
+
+    private Http2AsyncRequester requester;
+
+    @Rule
+    public ExternalResource clientResource = new ExternalResource() {
+
+        @Override
+        protected void before() throws Throwable {
+            requester = H2RequesterBootstrap.bootstrap()
+                    .setTlsStrategy(new H2ClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
+                    .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
+                    .setIOReactorConfig(IOReactorConfig.custom()
+                            .setSoTimeout(TIMEOUT)
+                            .build())
+                    .create();
+        }
+
+        @Override
+        protected void after() {
+            if (requester != null) {
+                try {
+                    requester.shutdown(ShutdownType.IMMEDIATE);
+                    requester = null;
+                } catch (final Exception ignore) {
+                }
+            }
+        }
+
+    };
+
+    private static int version;
+
+    @BeforeClass
+    public static void determineJavaVersion() {
+        version = 7;
+        final String s = System.getProperty("java.version");
+        if (s.equals("9-ea")) {
+            version = 9;
+        }
+        final String[] parts = s.split("\\.");
+        if (parts.length >= 2) {
+            if (parts[0].equals("1")) {
+                try {
+                    version = Integer.parseInt(parts[1]);
+                } catch (NumberFormatException ignore) {
+                }
+            }
+        }
+    }
+
+    @Before
+    public void checkVersion() {
+        Assume.assumeTrue("Java version must be 1.8 or greater", version > 7);
+    }
+
+    @Test
+    public void testForceHttp1() throws Exception {
+        server.start();
+        final ListenerEndpoint listener = server.listen(new InetSocketAddress(0));
+        listener.waitFor();
+        final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
+        requester.start();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
+        final Future<AsyncClientEndpoint> connectFuture = requester.connect(target, TIMEOUT, HttpVersionPolicy.FORCE_HTTP_1, null);
+        final AsyncClientEndpoint endpoint = connectFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+
+        final Future<Message<HttpResponse, String>> resultFuture1 = endpoint.execute(
+                new BasicRequestProducer("POST", target, "/stuff",
+                        new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)),
+                new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+        final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+        Assert.assertThat(message1, CoreMatchers.notNullValue());
+        final HttpResponse response1 = message1.getHead();
+        Assert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+        Assert.assertThat(response1.getVersion(), CoreMatchers.<ProtocolVersion>equalTo(HttpVersion.HTTP_1_1));
+    }
+
+    @Test
+    public void testForceHttp2() throws Exception {
+        server.start();
+        final ListenerEndpoint listener = server.listen(new InetSocketAddress(0));
+        listener.waitFor();
+        final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
+        requester.start();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
+        final Future<AsyncClientEndpoint> connectFuture = requester.connect(target, TIMEOUT, HttpVersionPolicy.FORCE_HTTP_2, null);
+        final AsyncClientEndpoint endpoint = connectFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+
+        final Future<Message<HttpResponse, String>> resultFuture1 = endpoint.execute(
+                new BasicRequestProducer("POST", target, "/stuff",
+                        new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)),
+                new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+        final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+        Assert.assertThat(message1, CoreMatchers.notNullValue());
+        final HttpResponse response1 = message1.getHead();
+        Assert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+        Assert.assertThat(response1.getVersion(), CoreMatchers.<ProtocolVersion>equalTo(HttpVersion.HTTP_2));
+    }
+
+    @Test
+    public void testNegotiateProtocol() throws Exception {
+        server.start();
+        final ListenerEndpoint listener = server.listen(new InetSocketAddress(0));
+        listener.waitFor();
+        final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
+        requester.start();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort(), "https");
+        final Future<AsyncClientEndpoint> connectFuture = requester.connect(target, TIMEOUT, HttpVersionPolicy.NEGOTIATE, null);
+        final AsyncClientEndpoint endpoint = connectFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+
+        final Future<Message<HttpResponse, String>> resultFuture1 = endpoint.execute(
+                new BasicRequestProducer("POST", target, "/stuff",
+                        new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)),
+                new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+        final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+        Assert.assertThat(message1, CoreMatchers.notNullValue());
+        final HttpResponse response1 = message1.getHead();
+        Assert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+
+        if (version < 9) {
+            Assert.assertThat(response1.getVersion(), CoreMatchers.<ProtocolVersion>equalTo(HttpVersion.HTTP_1_1));
+        } else {
+//            Assert.assertThat("Requires --add-opens java.base/sun.security.ssl=ALL-UNNAMED with Java 1.9+ " +
+//                    " in order to enable reflective access to SSLEngine",
+//                    response1.getVersion(), CoreMatchers.<ProtocolVersion>equalTo(HttpVersion.HTTP_2));
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2ServerAndRequesterTest.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2ServerAndRequesterTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2ServerAndRequesterTest.java
new file mode 100644
index 0000000..1b01ee8
--- /dev/null
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http2ServerAndRequesterTest.java
@@ -0,0 +1,288 @@
+/*
+ * ====================================================================
+ * 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.core5.testing.nio;
+
+import java.net.InetSocketAddress;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.Future;
+
+import org.apache.hc.core5.function.Supplier;
+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.HttpStatus;
+import org.apache.hc.core5.http.Message;
+import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
+import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
+import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.nio.BasicRequestProducer;
+import org.apache.hc.core5.http.nio.BasicResponseConsumer;
+import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
+import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap;
+import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap;
+import org.apache.hc.core5.io.ShutdownType;
+import org.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.reactor.ListenerEndpoint;
+import org.apache.hc.core5.util.TimeValue;
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExternalResource;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Http2ServerAndRequesterTest {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> protocols() {
+        return Arrays.asList(new Object[][]{
+                { HttpVersionPolicy.NEGOTIATE },
+                { HttpVersionPolicy.FORCE_HTTP_1 },
+                { HttpVersionPolicy.FORCE_HTTP_2 }
+        });
+    }
+
+    private static final TimeValue TIMEOUT = TimeValue.ofSeconds(30);
+
+    private final HttpVersionPolicy versionPolicy;
+
+    public Http2ServerAndRequesterTest(final HttpVersionPolicy versionPolicy) {
+        this.versionPolicy = versionPolicy;
+    }
+
+    private HttpAsyncServer server;
+
+    @Rule
+    public ExternalResource serverResource = new ExternalResource() {
+
+        @Override
+        protected void before() throws Throwable {
+            server = H2ServerBootstrap.bootstrap()
+                    .setVersionPolicy(versionPolicy)
+                    .setIOReactorConfig(
+                            IOReactorConfig.custom()
+                                    .setSoTimeout(TIMEOUT)
+                                    .build())
+                    .register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+                        @Override
+                        public AsyncServerExchangeHandler get() {
+                            return new EchoHandler(2048);
+                        }
+
+                    })
+                    .create();
+        }
+
+        @Override
+        protected void after() {
+            if (server != null) {
+                try {
+                    server.shutdown(ShutdownType.IMMEDIATE);
+                    server = null;
+                } catch (final Exception ignore) {
+                }
+            }
+        }
+
+    };
+
+    private HttpAsyncRequester requester;
+
+    @Rule
+    public ExternalResource clientResource = new ExternalResource() {
+
+        @Override
+        protected void before() throws Throwable {
+            requester = H2RequesterBootstrap.bootstrap()
+                    .setVersionPolicy(versionPolicy)
+                    .setIOReactorConfig(IOReactorConfig.custom()
+                            .setSoTimeout(TIMEOUT)
+                            .build())
+                    .create();
+        }
+
+        @Override
+        protected void after() {
+            if (requester != null) {
+                try {
+                    requester.shutdown(ShutdownType.IMMEDIATE);
+                    requester = null;
+                } catch (final Exception ignore) {
+                }
+            }
+        }
+
+    };
+
+    @Test
+    public void testSequentialRequests() throws Exception {
+        server.start();
+        final ListenerEndpoint listener = server.listen(new InetSocketAddress(0));
+        listener.waitFor();
+        final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
+        requester.start();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort());
+        final Future<Message<HttpResponse, String>> resultFuture1 = requester.execute(
+                new BasicRequestProducer("POST", target, "/stuff",
+                        new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)),
+                new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
+        final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+        Assert.assertThat(message1, CoreMatchers.notNullValue());
+        final HttpResponse response1 = message1.getHead();
+        Assert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+        final String body1 = message1.getBody();
+        Assert.assertThat(body1, CoreMatchers.equalTo("some stuff"));
+
+        final Future<Message<HttpResponse, String>> resultFuture2 = requester.execute(
+                new BasicRequestProducer("POST", target, "/other-stuff",
+                        new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)),
+                new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
+        final Message<HttpResponse, String> message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+        Assert.assertThat(message2, CoreMatchers.notNullValue());
+        final HttpResponse response2 = message2.getHead();
+        Assert.assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+        final String body2 = message2.getBody();
+        Assert.assertThat(body2, CoreMatchers.equalTo("some other stuff"));
+
+        final Future<Message<HttpResponse, String>> resultFuture3 = requester.execute(
+                new BasicRequestProducer("POST", target, "/more-stuff",
+                        new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)),
+                new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), TIMEOUT, null);
+        final Message<HttpResponse, String> message3 = resultFuture3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+        Assert.assertThat(message3, CoreMatchers.notNullValue());
+        final HttpResponse response3 = message3.getHead();
+        Assert.assertThat(response3.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+        final String body3 = message3.getBody();
+        Assert.assertThat(body3, CoreMatchers.equalTo("some more stuff"));
+    }
+
+    @Test
+    public void testSequentialRequestsSameEndpoint() throws Exception {
+        server.start();
+        final ListenerEndpoint listener = server.listen(new InetSocketAddress(0));
+        listener.waitFor();
+        final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
+        requester.start();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort());
+        final Future<AsyncClientEndpoint> endpointFuture = requester.connect(target, TimeValue.ofSeconds(5));
+        final AsyncClientEndpoint endpoint = endpointFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+        try {
+
+            final Future<Message<HttpResponse, String>> resultFuture1 = endpoint.execute(
+                    new BasicRequestProducer("POST", target, "/stuff",
+                            new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+            final Message<HttpResponse, String> message1 = resultFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+            Assert.assertThat(message1, CoreMatchers.notNullValue());
+            final HttpResponse response1 = message1.getHead();
+            Assert.assertThat(response1.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+            final String body1 = message1.getBody();
+            Assert.assertThat(body1, CoreMatchers.equalTo("some stuff"));
+
+            final Future<Message<HttpResponse, String>> resultFuture2 = endpoint.execute(
+                    new BasicRequestProducer("POST", target, "/other-stuff",
+                            new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+            final Message<HttpResponse, String> message2 = resultFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+            Assert.assertThat(message2, CoreMatchers.notNullValue());
+            final HttpResponse response2 = message2.getHead();
+            Assert.assertThat(response2.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+            final String body2 = message2.getBody();
+            Assert.assertThat(body2, CoreMatchers.equalTo("some other stuff"));
+
+            final Future<Message<HttpResponse, String>> resultFuture3 = endpoint.execute(
+                    new BasicRequestProducer("POST", target, "/more-stuff",
+                            new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+            final Message<HttpResponse, String> message3 = resultFuture3.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+            Assert.assertThat(message3, CoreMatchers.notNullValue());
+            final HttpResponse response3 = message3.getHead();
+            Assert.assertThat(response3.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+            final String body3 = message3.getBody();
+            Assert.assertThat(body3, CoreMatchers.equalTo("some more stuff"));
+
+        } finally {
+            endpoint.releaseAndReuse();
+        }
+    }
+
+    @Test
+    public void testPipelinedRequests() throws Exception {
+        server.start();
+        final ListenerEndpoint listener = server.listen(new InetSocketAddress(0));
+        listener.waitFor();
+        final InetSocketAddress address = (InetSocketAddress) listener.getAddress();
+        requester.start();
+
+        final HttpHost target = new HttpHost("localhost", address.getPort());
+        final Future<AsyncClientEndpoint> endpointFuture = requester.connect(target, TimeValue.ofSeconds(5));
+        final AsyncClientEndpoint endpoint = endpointFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+        try {
+
+            final Queue<Future<Message<HttpResponse, String>>> queue = new LinkedList<>();
+
+            queue.add(endpoint.execute(
+                    new BasicRequestProducer("POST", target, "/stuff",
+                            new StringAsyncEntityProducer("some stuff", ContentType.TEXT_PLAIN)),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null));
+            queue.add(endpoint.execute(
+                    new BasicRequestProducer("POST", target, "/other-stuff",
+                            new StringAsyncEntityProducer("some other stuff", ContentType.TEXT_PLAIN)),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null));
+            queue.add(endpoint.execute(
+                    new BasicRequestProducer("POST", target, "/more-stuff",
+                            new StringAsyncEntityProducer("some more stuff", ContentType.TEXT_PLAIN)),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null));
+
+            while (!queue.isEmpty()) {
+                final Future<Message<HttpResponse, String>> resultFuture = queue.remove();
+                final Message<HttpResponse, String> message = resultFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+                Assert.assertThat(message, CoreMatchers.notNullValue());
+                final HttpResponse response = message.getHead();
+                Assert.assertThat(response.getCode(), CoreMatchers.equalTo(HttpStatus.SC_OK));
+                final String body = message.getBody();
+                Assert.assertThat(body, CoreMatchers.containsString("stuff"));
+            }
+
+        } finally {
+            endpoint.releaseAndReuse();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/InternalHttp1ServerTestBase.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/InternalHttp1ServerTestBase.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/InternalHttp1ServerTestBase.java
index c63f510..94081f6 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/InternalHttp1ServerTestBase.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/InternalHttp1ServerTestBase.java
@@ -27,13 +27,9 @@
 
 package org.apache.hc.core5.testing.nio;
 
-import java.net.URL;
-
-import javax.net.ssl.SSLContext;
-
 import org.apache.hc.core5.reactor.IOReactorConfig;
-import org.apache.hc.core5.ssl.SSLContextBuilder;
 import org.apache.hc.core5.testing.ProtocolScheme;
+import org.apache.hc.core5.testing.SSLTestContexts;
 import org.apache.hc.core5.util.TimeValue;
 import org.junit.Rule;
 import org.junit.rules.ExternalResource;
@@ -59,7 +55,7 @@ public abstract class InternalHttp1ServerTestBase {
         protected void before() throws Throwable {
             server = new Http1TestServer(
                     IOReactorConfig.DEFAULT,
-                    scheme == ProtocolScheme.HTTPS ? createServerSSLContext() : null);
+                    scheme == ProtocolScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null);
         }
 
         @Override
@@ -75,21 +71,4 @@ public abstract class InternalHttp1ServerTestBase {
 
     };
 
-    protected SSLContext createServerSSLContext() throws Exception {
-        final URL keyStoreURL = getClass().getResource("/test.keystore");
-        final String storePassword = "nopassword";
-        return SSLContextBuilder.create()
-                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
-                .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
-                .build();
-    }
-
-    protected SSLContext createClientSSLContext() throws Exception {
-        final URL keyStoreURL = getClass().getResource("/test.keystore");
-        final String storePassword = "nopassword";
-        return SSLContextBuilder.create()
-                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
-                .build();
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/InternalHttp2ServerTestBase.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/InternalHttp2ServerTestBase.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/InternalHttp2ServerTestBase.java
index 7d49753..c2f0e28 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/InternalHttp2ServerTestBase.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/InternalHttp2ServerTestBase.java
@@ -27,13 +27,9 @@
 
 package org.apache.hc.core5.testing.nio;
 
-import java.net.URL;
-
-import javax.net.ssl.SSLContext;
-
 import org.apache.hc.core5.reactor.IOReactorConfig;
-import org.apache.hc.core5.ssl.SSLContextBuilder;
 import org.apache.hc.core5.testing.ProtocolScheme;
+import org.apache.hc.core5.testing.SSLTestContexts;
 import org.apache.hc.core5.util.TimeValue;
 import org.junit.Rule;
 import org.junit.rules.ExternalResource;
@@ -58,7 +54,7 @@ public abstract class InternalHttp2ServerTestBase {
         @Override
         protected void before() throws Throwable {
             server = new Http2TestServer(IOReactorConfig.DEFAULT,
-                    scheme == ProtocolScheme.HTTPS ? createServerSSLContext() : null);
+                    scheme == ProtocolScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null);
         }
 
         @Override
@@ -74,21 +70,4 @@ public abstract class InternalHttp2ServerTestBase {
 
     };
 
-    protected SSLContext createServerSSLContext() throws Exception {
-        final URL keyStoreURL = getClass().getResource("/test.keystore");
-        final String storePassword = "nopassword";
-        return SSLContextBuilder.create()
-                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
-                .loadKeyMaterial(keyStoreURL, storePassword.toCharArray(), storePassword.toCharArray())
-                .build();
-    }
-
-    protected SSLContext createClientSSLContext() throws Exception {
-        final URL keyStoreURL = getClass().getResource("/test.keystore");
-        final String storePassword = "nopassword";
-        return SSLContextBuilder.create()
-                .loadTrustMaterial(keyStoreURL, storePassword.toCharArray())
-                .build();
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java b/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java
index 42d12ff..0f0c787 100644
--- a/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java
+++ b/httpcore5/src/examples/org/apache/hc/core5/http/examples/AsyncReverseProxyExample.java
@@ -313,7 +313,7 @@ public class AsyncReverseProxyExample {
 
             System.out.println("[proxy->origin] " + exchangeState.id + " request connection to " + targetHost);
 
-            requester.connect(targetHost, TimeValue.ofSeconds(30), new FutureCallback<AsyncClientEndpoint>() {
+            requester.connect(targetHost, TimeValue.ofSeconds(30), null, new FutureCallback<AsyncClientEndpoint>() {
 
                 @Override
                 public void completed(final AsyncClientEndpoint clientEndpoint) {

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequester.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequester.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequester.java
index 94c92d5..f0da106 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequester.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequester.java
@@ -72,13 +72,14 @@ public class AsyncRequester extends IOReactorExecutor<DefaultConnectingIOReactor
         return new InetSocketAddress(hostName, port);
     }
 
-    protected SessionRequest requestSession(
+    public SessionRequest requestSession(
             final HttpHost host,
             final TimeValue timeout,
+            final Object attachment,
             final SessionRequestCallback callback) {
         Args.notNull(host, "Host");
         Args.notNull(timeout, "Timeout");
-        final SessionRequest  sessionRequest = reactor().connect(host, toSocketAddress(host), null, null, callback);
+        final SessionRequest  sessionRequest = reactor().connect(host, toSocketAddress(host), null, attachment, callback);
         sessionRequest.setConnectTimeout(timeout.toMillisIntBound());
         return sessionRequest;
     }


Mime
View raw message