hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject [13/50] [abbrv] httpcomponents-core git commit: HTTP protocol version negotiation
Date Tue, 09 May 2017 19:58:05 GMT
HTTP protocol version negotiation

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk@1792442 13f79535-47bb-0310-9956-ffa450edef68


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

Branch: refs/heads/trunk
Commit: ed4b808c0d0c6e9754bf5f4e226f055c367b8372
Parents: 3d507fc
Author: Oleg Kalnichevski <olegk@apache.org>
Authored: Mon Apr 24 09:23:56 2017 +0000
Committer: Oleg Kalnichevski <olegk@apache.org>
Committed: Mon Apr 24 09:23:56 2017 +0000

----------------------------------------------------------------------
 .../Http2SSLRequestExecutionExample.java        | 196 +++++++++++++
 .../hc/core5/http2/HttpVersionPolicy.java       |  33 +++
 .../nio/AbstractHttp2StreamMultiplexer.java     |   4 +-
 .../impl/nio/ClientHttp2StreamMultiplexer.java  |  16 +-
 .../ClientHttp2StreamMultiplexerFactory.java    |  90 ++++++
 .../impl/nio/ClientHttpProtocolNegotiator.java  | 167 ++++++-----
 .../ClientHttpProtocolNegotiatorFactory.java    |  56 ++--
 .../ServerHttp2StreamMultiplexerFactory.java    |  82 ++++++
 .../impl/nio/ServerHttpProtocolNegotiator.java  | 109 ++++---
 .../ServerHttpProtocolNegotiatorFactory.java    |  52 ++--
 .../nio/bootstrap/H2RequesterBootstrap.java     |  56 +++-
 .../impl/nio/bootstrap/H2ServerBootstrap.java   |  71 ++++-
 .../impl/nio/bootstrap/Http2AsyncRequester.java |  73 +++++
 .../core5/http2/ssl/ApplicationProtocols.java   |  50 ++++
 .../hc/core5/http2/ssl/FixedPortStrategy.java   |  56 ++++
 .../hc/core5/http2/ssl/H2ClientTlsStrategy.java |   4 +-
 .../hc/core5/http2/ssl/H2ServerTlsStrategy.java |  35 +--
 .../apache/hc/core5/http2/ssl/H2TlsSupport.java |  63 ++--
 .../hc/core5/http2/ssl/SecurePortStrategy.java  |  41 +++
 .../hc/core5/testing/nio/Http2TestClient.java   |  21 +-
 .../hc/core5/testing/nio/Http2TestServer.java   |  11 +-
 .../InternalClientHttp1EventHandlerFactory.java |  69 +----
 .../InternalClientHttp2EventHandlerFactory.java |  80 +++---
 .../testing/nio/InternalConnectionListener.java |  67 +++++
 .../nio/InternalHttp1StreamListener.java        |  96 +++++++
 .../InternalServerHttp1EventHandlerFactory.java |  69 +----
 .../InternalServerHttp2EventHandlerFactory.java |  82 +++---
 .../hc/core5/testing/nio/LoggingIOSession.java  |  12 +-
 .../hc/core5/testing/SSLTestContexts.java       |  55 ++++
 .../core5/testing/nio/Http1IntegrationTest.java |   5 +-
 .../core5/testing/nio/Http2IntegrationTest.java |   3 +-
 .../nio/Http2ProtocolNegotiationTest.java       | 251 ++++++++++++++++
 .../nio/Http2ServerAndRequesterTest.java        | 288 +++++++++++++++++++
 .../nio/InternalHttp1ServerTestBase.java        |  25 +-
 .../nio/InternalHttp2ServerTestBase.java        |  25 +-
 .../http/examples/AsyncReverseProxyExample.java |   2 +-
 .../http/impl/bootstrap/AsyncRequester.java     |   5 +-
 .../impl/bootstrap/AsyncRequesterBootstrap.java |  30 +-
 .../impl/bootstrap/AsyncServerBootstrap.java    |  21 +-
 .../http/impl/bootstrap/HttpAsyncRequester.java |  28 +-
 .../impl/nio/AbstractHttp1IOEventHandler.java   |   2 +-
 .../impl/nio/AbstractHttp1StreamDuplexer.java   |  14 +-
 .../nio/ClientHttp1IOEventHandlerFactory.java   |  92 +-----
 .../nio/ClientHttp1StreamDuplexerFactory.java   | 137 +++++++++
 .../nio/ServerHttp1IOEventHandlerFactory.java   | 109 +------
 .../nio/ServerHttp1StreamDuplexerFactory.java   | 136 +++++++++
 .../http/impl/nio/SessionInputBufferImpl.java   |   8 +
 .../http/nio/ssl/BasicClientTlsStrategy.java    |   2 +-
 .../http/nio/ssl/BasicServerTlsStrategy.java    |   2 +-
 .../hc/core5/http/nio/ssl/TlsStrategy.java      |   2 +-
 .../hc/core5/reactor/InternalIOSession.java     |   6 +-
 .../hc/core5/reactor/ssl/SSLIOSession.java      |  20 +-
 .../reactor/ssl/SSLSessionInitializer.java      |   9 +-
 .../core5/reactor/ssl/SSLSessionVerifier.java   |   8 +-
 .../apache/hc/core5/reactor/ssl/TlsDetails.java |  63 ++++
 .../reactor/ssl/TransportSecurityLayer.java     |   3 +-
 .../apache/hc/core5/ssl/ReflectionSupport.java  |  54 ++++
 57 files changed, 2385 insertions(+), 781 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/examples/org/apache/hc/core5/http/examples/Http2SSLRequestExecutionExample.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/examples/org/apache/hc/core5/http/examples/Http2SSLRequestExecutionExample.java b/httpcore5-h2/src/examples/org/apache/hc/core5/http/examples/Http2SSLRequestExecutionExample.java
new file mode 100644
index 0000000..0463420
--- /dev/null
+++ b/httpcore5-h2/src/examples/org/apache/hc/core5/http/examples/Http2SSLRequestExecutionExample.java
@@ -0,0 +1,196 @@
+/*
+ * ====================================================================
+ * 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.http.examples;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpConnection;
+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.impl.bootstrap.HttpAsyncRequester;
+import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
+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.http2.config.H2Config;
+import org.apache.hc.core5.http2.frame.RawFrame;
+import org.apache.hc.core5.http2.impl.nio.Http2StreamListener;
+import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap;
+import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy;
+import org.apache.hc.core5.io.ShutdownType;
+import org.apache.hc.core5.net.NamedEndpoint;
+import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
+import org.apache.hc.core5.reactor.ssl.TlsDetails;
+import org.apache.hc.core5.ssl.SSLContexts;
+import org.apache.hc.core5.ssl.TrustStrategy;
+import org.apache.hc.core5.util.TimeValue;
+
+/**
+ * This example demonstrates how to execute HTTP/2 requests over TLS connections.
+ */
+public class Http2SSLRequestExecutionExample {
+
+    public final static void main(final String[] args) throws Exception {
+        // Trust standard CA and those trusted by our custom strategy
+        final SSLContext sslcontext = SSLContexts.custom()
+                .loadTrustMaterial(new TrustStrategy() {
+
+                    @Override
+                    public boolean isTrusted(
+                            final X509Certificate[] chain,
+                            final String authType) throws CertificateException {
+                        final X509Certificate cert = chain[0];
+                        return "CN=http2bin.org".equalsIgnoreCase(cert.getSubjectDN().getName());
+                    }
+
+                })
+                .build();
+
+        // Create and start requester
+        H2Config h2Config = H2Config.custom()
+                .setPushEnabled(false)
+                .build();
+
+        final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap()
+                .setH2Config(h2Config)
+                .setTlsStrategy(new H2ClientTlsStrategy(sslcontext, new SSLSessionVerifier() {
+
+                    @Override
+                    public TlsDetails verify(final NamedEndpoint endpoint, final SSLEngine sslEngine) throws SSLException {
+                        // IMPORTANT
+                        // In order for HTTP/2 protocol negotiation to succeed one must allow access
+                        // to Java 9 specific properties of SSLEngine via reflection
+                        // by adding the following line to the JVM arguments
+                        //
+                        // --add-opens java.base/sun.security.ssl=ALL-UNNAMED
+                        //
+                        // or uncomment the method below
+
+                        // return new TlsDetails(sslEngine.getSession(), sslEngine.getApplicationProtocol());
+                        return null;
+                    }
+
+                }))
+                .setStreamListener(new Http2StreamListener() {
+
+                    @Override
+                    public void onHeaderInput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
+                        for (int i = 0; i < headers.size(); i++) {
+                            System.out.println(connection + " (" + streamId + ") << " + headers.get(i));
+                        }
+                    }
+
+                    @Override
+                    public void onHeaderOutput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
+                        for (int i = 0; i < headers.size(); i++) {
+                            System.out.println(connection + " (" + streamId + ") >> " + headers.get(i));
+                        }
+                    }
+
+                    @Override
+                    public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) {
+                    }
+
+                    @Override
+                    public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) {
+                    }
+
+                    @Override
+                    public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
+                    }
+
+                    @Override
+                    public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
+                    }
+
+                })
+                .create();
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            @Override
+            public void run() {
+                System.out.println("HTTP requester shutting down");
+                requester.shutdown(ShutdownType.GRACEFUL);
+            }
+        });
+        requester.start();
+
+        HttpHost target = new HttpHost("http2bin.org", 443, "https");
+        String[] requestUris = new String[] {"/", "/ip", "/user-agent", "/headers"};
+
+        final CountDownLatch latch = new CountDownLatch(requestUris.length);
+        for (final String requestUri: requestUris) {
+            final Future<AsyncClientEndpoint> future = requester.connect(target, TimeValue.ofSeconds(5));
+            final AsyncClientEndpoint clientEndpoint = future.get();
+            clientEndpoint.execute(
+                    new BasicRequestProducer("GET", target, requestUri),
+                    new BasicResponseConsumer<>(new StringAsyncEntityConsumer()),
+                    new FutureCallback<Message<HttpResponse, String>>() {
+
+                        @Override
+                        public void completed(final Message<HttpResponse, String> message) {
+                            clientEndpoint.releaseAndReuse();
+                            HttpResponse response = message.getHead();
+                            String body = message.getBody();
+                            System.out.println(requestUri + "->" + response.getCode() + " " + response.getVersion());
+                            System.out.println(body);
+                            latch.countDown();
+                        }
+
+                        @Override
+                        public void failed(final Exception ex) {
+                            clientEndpoint.releaseAndDiscard();
+                            System.out.println(requestUri + "->" + ex);
+                            latch.countDown();
+                        }
+
+                        @Override
+                        public void cancelled() {
+                            clientEndpoint.releaseAndDiscard();
+                            System.out.println(requestUri + " cancelled");
+                            latch.countDown();
+                        }
+
+                    });
+        }
+
+        latch.await();
+        System.out.println("Shutting down I/O reactor");
+        requester.initiateShutdown();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/HttpVersionPolicy.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/HttpVersionPolicy.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/HttpVersionPolicy.java
new file mode 100644
index 0000000..237c864
--- /dev/null
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/HttpVersionPolicy.java
@@ -0,0 +1,33 @@
+/*
+ * ====================================================================
+ * 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;
+
+public enum HttpVersionPolicy {
+
+    FORCE_HTTP_1, FORCE_HTTP_2, NEGOTIATE
+
+};

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractHttp2StreamMultiplexer.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractHttp2StreamMultiplexer.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractHttp2StreamMultiplexer.java
index 61c7fb9..697560b 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractHttp2StreamMultiplexer.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/AbstractHttp2StreamMultiplexer.java
@@ -84,6 +84,7 @@ import org.apache.hc.core5.io.ShutdownType;
 import org.apache.hc.core5.net.InetAddressUtils;
 import org.apache.hc.core5.reactor.Command;
 import org.apache.hc.core5.reactor.TlsCapableIOSession;
+import org.apache.hc.core5.reactor.ssl.TlsDetails;
 import org.apache.hc.core5.util.Args;
 import org.apache.hc.core5.util.ByteArrayBuffer;
 
@@ -1188,7 +1189,8 @@ abstract class AbstractHttp2StreamMultiplexer implements HttpConnection {
 
     @Override
     public SSLSession getSSLSession() {
-        return ioSession.getSSLSession();
+        final TlsDetails tlsDetails = ioSession.getTlsDetails();
+        return tlsDetails != null ? tlsDetails.getSSLSession() : null;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamMultiplexer.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamMultiplexer.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamMultiplexer.java
index 89e47d5..985fed7 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamMultiplexer.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamMultiplexer.java
@@ -55,8 +55,8 @@ public class ClientHttp2StreamMultiplexer extends AbstractHttp2StreamMultiplexer
             final FrameFactory frameFactory,
             final HttpProcessor httpProcessor,
             final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
-            final CharCodingConfig charCodingConfig,
             final H2Config h2Config,
+            final CharCodingConfig charCodingConfig,
             final ConnectionListener connectionListener,
             final Http2StreamListener streamListener) {
         super(Mode.CLIENT, ioSession, frameFactory, StreamIdGenerator.ODD, httpProcessor, charCodingConfig,
@@ -68,18 +68,18 @@ public class ClientHttp2StreamMultiplexer extends AbstractHttp2StreamMultiplexer
             final TlsCapableIOSession ioSession,
             final HttpProcessor httpProcessor,
             final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
-            final CharCodingConfig charCodingConfig,
-            final H2Config h2Config) {
-        this(ioSession, DefaultFrameFactory.INSTANCE, httpProcessor, pushHandlerFactory, charCodingConfig,
-                h2Config, null, null);
+            final H2Config h2Config,
+            final CharCodingConfig charCodingConfig) {
+        this(ioSession, DefaultFrameFactory.INSTANCE, httpProcessor, pushHandlerFactory,
+                h2Config, charCodingConfig, null, null);
     }
 
     public ClientHttp2StreamMultiplexer(
             final TlsCapableIOSession ioSession,
             final HttpProcessor httpProcessor,
-            final CharCodingConfig charCodingConfig,
-            final H2Config h2Config) {
-        this(ioSession, httpProcessor, null, charCodingConfig, h2Config);
+            final H2Config h2Config,
+            final CharCodingConfig charCodingConfig) {
+        this(ioSession, httpProcessor, null, h2Config, charCodingConfig);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamMultiplexerFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamMultiplexerFactory.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamMultiplexerFactory.java
new file mode 100644
index 0000000..0f15bc0
--- /dev/null
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp2StreamMultiplexerFactory.java
@@ -0,0 +1,90 @@
+/*
+ * ====================================================================
+ * 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.impl.nio;
+
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.http.config.CharCodingConfig;
+import org.apache.hc.core5.http.impl.ConnectionListener;
+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.config.H2Config;
+import org.apache.hc.core5.http2.frame.DefaultFrameFactory;
+import org.apache.hc.core5.reactor.TlsCapableIOSession;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * @since 5.0
+ */
+@Contract(threading = ThreadingBehavior.IMMUTABLE)
+public final class ClientHttp2StreamMultiplexerFactory {
+
+    private final HttpProcessor httpProcessor;
+    private final HandlerFactory<AsyncPushConsumer> pushHandlerFactory;
+    private final H2Config h2Config;
+    private final CharCodingConfig charCodingConfig;
+    private final ConnectionListener connectionListener;
+    private final Http2StreamListener streamListener;
+
+    public ClientHttp2StreamMultiplexerFactory(
+            final HttpProcessor httpProcessor,
+            final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
+            final H2Config h2Config,
+            final CharCodingConfig charCodingConfig,
+            final ConnectionListener connectionListener,
+            final Http2StreamListener streamListener) {
+        this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
+        this.pushHandlerFactory = pushHandlerFactory;
+        this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT;
+        this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT;
+        this.connectionListener = connectionListener;
+        this.streamListener = streamListener;
+    }
+
+    public ClientHttp2StreamMultiplexerFactory(
+            final HttpProcessor httpProcessor,
+            final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
+            final ConnectionListener connectionListener,
+            final Http2StreamListener streamListener) {
+        this(httpProcessor, pushHandlerFactory, null, null, connectionListener, streamListener);
+    }
+
+    public ClientHttp2StreamMultiplexerFactory(
+            final HttpProcessor httpProcessor,
+            final ConnectionListener connectionListener,
+            final Http2StreamListener streamListener) {
+        this(httpProcessor, null, connectionListener, streamListener);
+    }
+
+    public ClientHttp2StreamMultiplexer create(final TlsCapableIOSession ioSession) {
+        return new ClientHttp2StreamMultiplexer(ioSession, DefaultFrameFactory.INSTANCE, httpProcessor,
+                pushHandlerFactory, h2Config, charCodingConfig, connectionListener, streamListener);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiator.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiator.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiator.java
index b2f80cc..320b949 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiator.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiator.java
@@ -37,23 +37,24 @@ import javax.net.ssl.SSLSession;
 
 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.EndpointDetails;
 import org.apache.hc.core5.http.ProtocolVersion;
-import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.impl.ConnectionListener;
+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.ClientHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler;
 import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
-import org.apache.hc.core5.http.nio.AsyncPushConsumer;
-import org.apache.hc.core5.http.nio.HandlerFactory;
 import org.apache.hc.core5.http.nio.command.ExecutionCommand;
-import org.apache.hc.core5.http.protocol.HttpProcessor;
-import org.apache.hc.core5.http2.config.H2Config;
-import org.apache.hc.core5.http2.frame.DefaultFrameFactory;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.http2.ssl.ApplicationProtocols;
 import org.apache.hc.core5.io.ShutdownType;
 import org.apache.hc.core5.reactor.Command;
 import org.apache.hc.core5.reactor.IOEventHandler;
 import org.apache.hc.core5.reactor.IOSession;
 import org.apache.hc.core5.reactor.TlsCapableIOSession;
+import org.apache.hc.core5.reactor.ssl.TlsDetails;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -69,65 +70,85 @@ public class ClientHttpProtocolNegotiator implements HttpConnectionEventHandler
             0x0d, 0x0a, 0x0d, 0x0a};
 
     private final TlsCapableIOSession ioSession;
-    private final HttpProcessor httpProcessor;
-    private final CharCodingConfig charCodingConfig;
-    private final H2Config h2Config;
-    private final HandlerFactory<AsyncPushConsumer> pushHandlerFactory;
+    private final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory;
+    private final ClientHttp2StreamMultiplexerFactory http2StreamHandlerFactory;
+    private final HttpVersionPolicy versionPolicy;
     private final ConnectionListener connectionListener;
-    private final Http2StreamListener streamListener;
-    private final ByteBuffer preface;
+
+    private volatile ByteBuffer preface;
 
     public ClientHttpProtocolNegotiator(
             final TlsCapableIOSession ioSession,
-            final HttpProcessor httpProcessor,
-            final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
-            final CharCodingConfig charCodingConfig,
-            final H2Config h2Config,
-            final ConnectionListener connectionListener,
-            final Http2StreamListener streamListener) {
+            final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory,
+            final ClientHttp2StreamMultiplexerFactory http2StreamHandlerFactory,
+            final HttpVersionPolicy versionPolicy,
+            final ConnectionListener connectionListener) {
         this.ioSession = Args.notNull(ioSession, "I/O session");
-        this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
-        this.pushHandlerFactory = pushHandlerFactory;
-        this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT;
-        this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT;
-        this.streamListener = streamListener;
+        this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory");
+        this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory");
+        this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
         this.connectionListener = connectionListener;
-        this.preface = ByteBuffer.wrap(PREFACE);
     }
 
-    protected ClientHttp2StreamMultiplexer createStreamMultiplexer(final TlsCapableIOSession ioSession) {
-        return new ClientHttp2StreamMultiplexer(ioSession, DefaultFrameFactory.INSTANCE, httpProcessor,
-                pushHandlerFactory, charCodingConfig, h2Config, connectionListener, streamListener);
+    @Override
+    public void connected(final IOSession session) {
+        try {
+            switch (versionPolicy) {
+                case NEGOTIATE:
+                    final TlsDetails tlsDetails = ioSession.getTlsDetails();
+                    if (tlsDetails != null) {
+                        if (ApplicationProtocols.HTTP_2.id.equals(tlsDetails.getApplicationProtocol())) {
+                            // Proceed with the H2 preface
+                            preface = ByteBuffer.wrap(PREFACE);
+                        }
+                    }
+                    break;
+                case FORCE_HTTP_2:
+                    preface = ByteBuffer.wrap(PREFACE);
+                    break;
+            }
+            if (preface == null) {
+                final ClientHttp1StreamDuplexer http1StreamHandler = http1StreamHandlerFactory.create(ioSession);
+                session.setHandler(new ClientHttp1IOEventHandler(http1StreamHandler));
+                http1StreamHandler.onConnect(null);
+            } else {
+                writePreface(session);
+            }
+        } catch (final Exception ex) {
+            session.shutdown(ShutdownType.IMMEDIATE);
+            exception(session, ex);
+        }
     }
 
-    @Override
-    public void connected(final IOSession ioSession) {
-        outputReady(ioSession);
+    private void writePreface(final IOSession session) throws IOException  {
+        if (preface.hasRemaining()) {
+            final ByteChannel channel = session.channel();
+            channel.write(preface);
+        }
+        if (!preface.hasRemaining()) {
+            final ClientHttp2StreamMultiplexer streamMultiplexer = http2StreamHandlerFactory.create(ioSession);
+            final IOEventHandler newHandler = new ClientHttp2IOEventHandler(streamMultiplexer);
+            newHandler.connected(session);
+            session.setHandler(newHandler);
+        }
     }
 
     @Override
     public void inputReady(final IOSession session) {
+        outputReady(session);
     }
 
     @Override
     public void outputReady(final IOSession session) {
-        if (preface.hasRemaining()) {
-            try {
-                final ByteChannel channel = ioSession.channel();
-                channel.write(preface);
-            } catch (final IOException ex) {
-                ioSession.shutdown(ShutdownType.IMMEDIATE);
-                if (connectionListener != null) {
-                    connectionListener.onError(this, ex);
-                }
-                return;
+        try {
+            if (preface != null) {
+                writePreface(session);
+            } else {
+                session.shutdown(ShutdownType.IMMEDIATE);
             }
-        }
-        if (!preface.hasRemaining()) {
-            final ClientHttp2StreamMultiplexer streamMultiplexer = createStreamMultiplexer(ioSession);
-            final IOEventHandler newHandler = new ClientHttp2IOEventHandler(streamMultiplexer);
-            newHandler.connected(ioSession);
-            ioSession.setHandler(newHandler);
+        } catch (final IOException ex) {
+            session.shutdown(ShutdownType.IMMEDIATE);
+            exception(session, ex);
         }
     }
 
@@ -136,30 +157,27 @@ public class ClientHttpProtocolNegotiator implements HttpConnectionEventHandler
         exception(session, new SocketTimeoutException());
     }
 
-    private void failPendingCommands(final Exception cause) {
-        for (;;) {
-            final Command command = ioSession.getCommand();
-            if (command != null) {
-                if (command instanceof ExecutionCommand) {
-                    final ExecutionCommand executionCommand = (ExecutionCommand) command;
-                    final AsyncClientExchangeHandler exchangeHandler = executionCommand.getExchangeHandler();
-                    exchangeHandler.failed(cause);
-                } else {
-                    command.cancel();
-                }
-            } else {
-                break;
-            }
-        }
-    }
-
     @Override
     public void exception(final IOSession session, final Exception cause) {
         if (connectionListener != null) {
             connectionListener.onError(this, new SocketTimeoutException());
         }
         try {
-            failPendingCommands(cause);
+            for (;;) {
+                final Command command = ioSession.getCommand();
+                if (command != null) {
+                    if (command instanceof ExecutionCommand) {
+                        final ExecutionCommand executionCommand = (ExecutionCommand) command;
+                        final AsyncClientExchangeHandler exchangeHandler = executionCommand.getExchangeHandler();
+                        exchangeHandler.failed(cause);
+                        exchangeHandler.releaseResources();
+                    } else {
+                        command.cancel();
+                    }
+                } else {
+                    break;
+                }
+            }
         } finally {
             session.shutdown(ShutdownType.IMMEDIATE);
         }
@@ -167,11 +185,30 @@ public class ClientHttpProtocolNegotiator implements HttpConnectionEventHandler
 
     @Override
     public void disconnected(final IOSession session) {
+        if (connectionListener != null) {
+            connectionListener.onDisconnect(this);
+        }
+        for (;;) {
+            final Command command = ioSession.getCommand();
+            if (command != null) {
+                if (command instanceof ExecutionCommand) {
+                    final ExecutionCommand executionCommand = (ExecutionCommand) command;
+                    final AsyncClientExchangeHandler exchangeHandler = executionCommand.getExchangeHandler();
+                    exchangeHandler.failed(new ConnectionClosedException("Connection closed"));
+                    exchangeHandler.releaseResources();
+                } else {
+                    command.cancel();
+                }
+            } else {
+                break;
+            }
+        }
     }
 
     @Override
     public SSLSession getSSLSession() {
-        return ioSession.getSSLSession();
+        final TlsDetails tlsDetails = ioSession.getTlsDetails();
+        return tlsDetails != null ? tlsDetails.getSSLSession() : null;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiatorFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiatorFactory.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiatorFactory.java
index d99e46c..9a5d2e1 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiatorFactory.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiatorFactory.java
@@ -29,12 +29,9 @@ package org.apache.hc.core5.http2.impl.nio;
 
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
-import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.impl.ConnectionListener;
-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.config.H2Config;
+import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.TlsCapableIOSession;
 import org.apache.hc.core5.util.Args;
@@ -45,47 +42,30 @@ import org.apache.hc.core5.util.Args;
 @Contract(threading = ThreadingBehavior.IMMUTABLE)
 public class ClientHttpProtocolNegotiatorFactory implements IOEventHandlerFactory {
 
-    private final HttpProcessor httpProcessor;
-    private final HandlerFactory<AsyncPushConsumer> pushHandlerFactory;
-    private final CharCodingConfig charCodingConfig;
-    private final H2Config h2Config;
+    private final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory;
+    private final ClientHttp2StreamMultiplexerFactory http2StreamHandlerFactory;
+    private final HttpVersionPolicy versionPolicy;
     private final ConnectionListener connectionListener;
-    private final Http2StreamListener streamListener;
 
     public ClientHttpProtocolNegotiatorFactory(
-            final HttpProcessor httpProcessor,
-            final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
-            final CharCodingConfig charCodingConfig,
-            final H2Config h2Config,
-            final ConnectionListener connectionListener,
-            final Http2StreamListener streamListener) {
-        this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
-        this.pushHandlerFactory = pushHandlerFactory;
-        this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT;
-        this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT;
+            final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory,
+            final ClientHttp2StreamMultiplexerFactory http2StreamHandlerFactory,
+            final HttpVersionPolicy versionPolicy,
+            final ConnectionListener connectionListener) {
+        this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory");
+        this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory");
+        this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
         this.connectionListener = connectionListener;
-        this.streamListener = streamListener;
-    }
-
-    public ClientHttpProtocolNegotiatorFactory(
-            final HttpProcessor httpProcessor,
-            final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
-            final ConnectionListener connectionListener,
-            final Http2StreamListener streamListener) {
-        this(httpProcessor, pushHandlerFactory, null, null, connectionListener, streamListener);
-    }
-
-    public ClientHttpProtocolNegotiatorFactory(
-            final HttpProcessor httpProcessor,
-            final ConnectionListener connectionListener,
-            final Http2StreamListener streamListener) {
-        this(httpProcessor, null, connectionListener, streamListener);
     }
 
     @Override
     public ClientHttpProtocolNegotiator createHandler(final TlsCapableIOSession ioSession, final Object attachment) {
-        return new ClientHttpProtocolNegotiator(ioSession, httpProcessor, pushHandlerFactory,
-                charCodingConfig, h2Config, connectionListener, streamListener);
+        return new ClientHttpProtocolNegotiator(
+                ioSession,
+                http1StreamHandlerFactory,
+                http2StreamHandlerFactory,
+                attachment instanceof HttpVersionPolicy ? (HttpVersionPolicy) attachment : versionPolicy,
+                connectionListener);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamMultiplexerFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamMultiplexerFactory.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamMultiplexerFactory.java
new file mode 100644
index 0000000..0d21874
--- /dev/null
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamMultiplexerFactory.java
@@ -0,0 +1,82 @@
+/*
+ * ====================================================================
+ * 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.impl.nio;
+
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.http.config.CharCodingConfig;
+import org.apache.hc.core5.http.impl.ConnectionListener;
+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.config.H2Config;
+import org.apache.hc.core5.http2.frame.DefaultFrameFactory;
+import org.apache.hc.core5.reactor.TlsCapableIOSession;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * @since 5.0
+ */
+@Contract(threading = ThreadingBehavior.IMMUTABLE)
+public final class ServerHttp2StreamMultiplexerFactory {
+
+    private final HttpProcessor httpProcessor;
+    private final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory;
+    private final H2Config h2Config;
+    private final CharCodingConfig charCodingConfig;
+    private final ConnectionListener connectionListener;
+    private final Http2StreamListener streamListener;
+
+    public ServerHttp2StreamMultiplexerFactory(
+            final HttpProcessor httpProcessor,
+            final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory,
+            final H2Config h2Config,
+            final CharCodingConfig charCodingConfig,
+            final ConnectionListener connectionListener,
+            final Http2StreamListener streamListener) {
+        this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
+        this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Exchange handler factory");
+        this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT;
+        this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT;
+        this.connectionListener = connectionListener;
+        this.streamListener = streamListener;
+    }
+
+    public ServerHttp2StreamMultiplexer create(final TlsCapableIOSession ioSession) {
+        return new ServerHttp2StreamMultiplexer(
+                ioSession,
+                DefaultFrameFactory.INSTANCE,
+                httpProcessor,
+                exchangeHandlerFactory,
+                charCodingConfig,
+                h2Config,
+                connectionListener,
+                streamListener);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiator.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiator.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiator.java
index 65b1430..50c5f4c 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiator.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiator.java
@@ -38,19 +38,19 @@ import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.EndpointDetails;
 import org.apache.hc.core5.http.ProtocolVersion;
-import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.impl.ConnectionListener;
 import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler;
-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.http.impl.nio.ServerHttp1IOEventHandler;
+import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexer;
+import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.http2.H2ConnectionException;
 import org.apache.hc.core5.http2.H2Error;
-import org.apache.hc.core5.http2.config.H2Config;
-import org.apache.hc.core5.http2.frame.DefaultFrameFactory;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.http2.ssl.ApplicationProtocols;
 import org.apache.hc.core5.io.ShutdownType;
 import org.apache.hc.core5.reactor.IOSession;
 import org.apache.hc.core5.reactor.TlsCapableIOSession;
+import org.apache.hc.core5.reactor.ssl.TlsDetails;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -62,39 +62,51 @@ public class ServerHttpProtocolNegotiator implements HttpConnectionEventHandler
     final static byte[] PREFACE = ClientHttpProtocolNegotiator.PREFACE;
 
     private final TlsCapableIOSession ioSession;
-    private final HttpProcessor httpProcessor;
-    private final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory;
-    private final CharCodingConfig charCodingConfig;
-    private final H2Config h2Config;
-    private final ByteBuffer bytebuf;
+    private final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory;
+    private final ServerHttp2StreamMultiplexerFactory http2StreamHandlerFactory;
+    private final HttpVersionPolicy versionPolicy;
     private final ConnectionListener connectionListener;
-    private final Http2StreamListener streamListener;
+    private final ByteBuffer bytebuf;
+
+    private volatile boolean expectValidH2Preface;
 
     public ServerHttpProtocolNegotiator(
             final TlsCapableIOSession ioSession,
-            final HttpProcessor httpProcessor,
-            final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory,
-            final CharCodingConfig charCodingConfig,
-            final H2Config h2Config,
-            final ConnectionListener connectionListener,
-            final Http2StreamListener streamListener) {
+            final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory,
+            final ServerHttp2StreamMultiplexerFactory http2StreamHandlerFactory,
+            final HttpVersionPolicy versionPolicy,
+            final ConnectionListener connectionListener) {
         this.ioSession = Args.notNull(ioSession, "I/O session");
-        this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
-        this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Exchange handler factory");
-        this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT;
-        this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT;
-        this.bytebuf = ByteBuffer.allocate(1024);
+        this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory");
+        this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory");
+        this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
         this.connectionListener = connectionListener;
-        this.streamListener = streamListener;
-    }
-
-    protected ServerHttp2StreamMultiplexer createStreamMultiplexer(final TlsCapableIOSession ioSession) {
-        return new ServerHttp2StreamMultiplexer(ioSession, DefaultFrameFactory.INSTANCE, httpProcessor,
-                exchangeHandlerFactory, charCodingConfig, h2Config, connectionListener, streamListener);
+        this.bytebuf = ByteBuffer.allocate(1024);
     }
 
     @Override
     public void connected(final IOSession session) {
+        try {
+            switch (versionPolicy) {
+                case NEGOTIATE:
+                    final TlsDetails tlsDetails = ioSession.getTlsDetails();
+                    if (tlsDetails != null) {
+                        if (ApplicationProtocols.HTTP_2.id.equals(tlsDetails.getApplicationProtocol())) {
+                            expectValidH2Preface = true;
+                            // Proceed with the H2 preface
+                            break;
+                        }
+                    }
+                    break;
+                case FORCE_HTTP_1:
+                    final ServerHttp1StreamDuplexer http1StreamHandler = http1StreamHandlerFactory.create(ioSession);
+                    session.setHandler(new ServerHttp1IOEventHandler(http1StreamHandler));
+                    http1StreamHandler.onConnect(null);
+                    break;
+            }
+        } catch (final Exception ex) {
+            exception(session, ex);
+        }
     }
 
     @Override
@@ -105,21 +117,32 @@ public class ServerHttpProtocolNegotiator implements HttpConnectionEventHandler
             }
             if (bytebuf.position() >= PREFACE.length) {
                 bytebuf.flip();
+
+                boolean validH2Preface = true;
                 for (int i = 0; i < PREFACE.length; i++) {
                     if (bytebuf.get() != PREFACE[i]) {
-                        throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected HTTP/2 preface");
+                        if (expectValidH2Preface) {
+                            throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Unexpected HTTP/2 preface");
+                        } else {
+                            validH2Preface = false;
+                        }
                     }
                 }
-                final ServerHttp2StreamMultiplexer streamMultiplexer = createStreamMultiplexer(ioSession);
-                streamMultiplexer.onConnect(bytebuf.hasRemaining() ? bytebuf : null);
-                streamMultiplexer.onInput();
-                session.setHandler(new ServerHttp2IOEventHandler(streamMultiplexer));
+                if (validH2Preface) {
+                    final ServerHttp2StreamMultiplexer http2StreamHandler = http2StreamHandlerFactory.create(ioSession);
+                    session.setHandler(new ServerHttp2IOEventHandler(http2StreamHandler));
+                    http2StreamHandler.onConnect(bytebuf.hasRemaining() ? bytebuf : null);
+                    http2StreamHandler.onInput();
+                } else {
+                    final ServerHttp1StreamDuplexer http1StreamHandler = http1StreamHandlerFactory.create(ioSession);
+                    session.setHandler(new ServerHttp1IOEventHandler(http1StreamHandler));
+                    bytebuf.rewind();
+                    http1StreamHandler.onConnect(bytebuf);
+                    http1StreamHandler.onInput();
+                }
             }
         } catch (final Exception ex) {
-            session.close();
-            if (connectionListener != null) {
-                connectionListener.onError(this, ex);
-            }
+            exception(session, ex);
         }
     }
 
@@ -134,19 +157,23 @@ public class ServerHttpProtocolNegotiator implements HttpConnectionEventHandler
 
     @Override
     public void exception(final IOSession session, final Exception cause) {
-        session.close();
+        session.shutdown(ShutdownType.IMMEDIATE);
         if (connectionListener != null) {
-            connectionListener.onError(this, new SocketTimeoutException());
+            connectionListener.onError(this, cause);
         }
     }
 
     @Override
     public void disconnected(final IOSession session) {
+        if (connectionListener != null) {
+            connectionListener.onDisconnect(this);
+        }
     }
 
     @Override
     public SSLSession getSSLSession() {
-        return ioSession.getSSLSession();
+        final TlsDetails tlsDetails = ioSession.getTlsDetails();
+        return tlsDetails != null ? tlsDetails.getSSLSession() : null;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiatorFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiatorFactory.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiatorFactory.java
index 7394281..ee2873c 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiatorFactory.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiatorFactory.java
@@ -29,13 +29,10 @@ package org.apache.hc.core5.http2.impl.nio;
 
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
-import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.impl.ConnectionListener;
-import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
-import org.apache.hc.core5.http.nio.HandlerFactory;
+import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
-import org.apache.hc.core5.http.protocol.HttpProcessor;
-import org.apache.hc.core5.http2.config.H2Config;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.TlsCapableIOSession;
 import org.apache.hc.core5.util.Args;
@@ -46,38 +43,23 @@ import org.apache.hc.core5.util.Args;
 @Contract(threading = ThreadingBehavior.IMMUTABLE)
 public class ServerHttpProtocolNegotiatorFactory implements IOEventHandlerFactory {
 
-    private final HttpProcessor httpProcessor;
-    private final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory;
-    private final CharCodingConfig charCodingConfig;
-    private final H2Config h2Config;
+    private final ServerHttp1StreamDuplexerFactory http1StreamDuplexerFactory;
+    private final ServerHttp2StreamMultiplexerFactory http2StreamMultiplexerFactory;
+    private final HttpVersionPolicy versionPolicy;
     private final TlsStrategy tlsStrategy;
     private final ConnectionListener connectionListener;
-    private final Http2StreamListener streamListener;
 
     public ServerHttpProtocolNegotiatorFactory(
-            final HttpProcessor httpProcessor,
-            final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory,
-            final CharCodingConfig charCodingConfig,
-            final H2Config h2Config,
+            final ServerHttp1StreamDuplexerFactory http1StreamDuplexerFactory,
+            final ServerHttp2StreamMultiplexerFactory http2StreamMultiplexerFactory,
+            final HttpVersionPolicy versionPolicy,
             final TlsStrategy tlsStrategy,
-            final ConnectionListener connectionListener,
-            final Http2StreamListener streamListener) {
-        this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
-        this.exchangeHandlerFactory = Args.notNull(exchangeHandlerFactory, "Exchange handler factory");
-        this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT;
-        this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT;
+            final ConnectionListener connectionListener) {
+        this.http1StreamDuplexerFactory = Args.notNull(http1StreamDuplexerFactory, "HTTP/1.1 stream handler factory");
+        this.http2StreamMultiplexerFactory = Args.notNull(http2StreamMultiplexerFactory, "HTTP/2 stream handler factory");
+        this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
         this.tlsStrategy = tlsStrategy;
         this.connectionListener = connectionListener;
-        this.streamListener = streamListener;
-    }
-
-    public ServerHttpProtocolNegotiatorFactory(
-            final HttpProcessor httpProcessor,
-            final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory,
-            final TlsStrategy tlsStrategy,
-            final ConnectionListener connectionListener,
-            final Http2StreamListener streamListener) {
-        this(httpProcessor, exchangeHandlerFactory, null, null, tlsStrategy, connectionListener, streamListener);
     }
 
     @Override
@@ -87,10 +69,14 @@ public class ServerHttpProtocolNegotiatorFactory implements IOEventHandlerFactor
                     ioSession,
                     null,
                     ioSession.getLocalAddress(),
-                    ioSession.getRemoteAddress());
+                    ioSession.getRemoteAddress(),
+                    attachment != null ? attachment : versionPolicy);
         }
-        return new ServerHttpProtocolNegotiator(ioSession, httpProcessor, exchangeHandlerFactory,
-                charCodingConfig, h2Config, connectionListener, streamListener);
+        return new ServerHttpProtocolNegotiator(ioSession,
+                http1StreamDuplexerFactory,
+                http2StreamMultiplexerFactory,
+                versionPolicy,
+                connectionListener);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
index a6b0b35..e856800 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
@@ -33,13 +33,18 @@ import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.ExceptionListener;
 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.impl.ConnectionListener;
-import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
+import org.apache.hc.core5.http.impl.Http1StreamListener;
+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.ssl.TlsStrategy;
 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.ClientHttp2StreamMultiplexerFactory;
 import org.apache.hc.core5.http2.impl.nio.ClientHttpProtocolNegotiatorFactory;
 import org.apache.hc.core5.http2.impl.nio.Http2StreamListener;
 import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy;
@@ -62,7 +67,9 @@ public class H2RequesterBootstrap {
     private IOReactorConfig ioReactorConfig;
     private HttpProcessor httpProcessor;
     private CharCodingConfig charCodingConfig;
+    private HttpVersionPolicy versionPolicy;
     private H2Config h2Config;
+    private H1Config h1Config;
     private int defaultMaxPerRoute;
     private int maxTotal;
     private TimeValue timeToLive;
@@ -71,6 +78,7 @@ public class H2RequesterBootstrap {
     private ExceptionListener exceptionListener;
     private ConnectionListener connectionListener;
     private Http2StreamListener streamListener;
+    private Http1StreamListener http1StreamListener;
     private ConnPoolListener<HttpHost> connPoolListener;
 
     private H2RequesterBootstrap() {
@@ -98,6 +106,14 @@ public class H2RequesterBootstrap {
     }
 
     /**
+     * Sets HTTP protocol version policy
+     */
+    public final H2RequesterBootstrap setVersionPolicy(final HttpVersionPolicy versionPolicy) {
+        this.versionPolicy = versionPolicy;
+        return this;
+    }
+
+    /**
      * Sets HTTP/2 protocol parameters
      */
     public final H2RequesterBootstrap setH2Config(final H2Config h2Config) {
@@ -106,7 +122,15 @@ public class H2RequesterBootstrap {
     }
 
     /**
-     * Sets char coding for HTTP/2 messages.
+     * Sets HTTP/1.1 protocol parameters
+     */
+    public final H2RequesterBootstrap setH1Config(final H1Config h1Config) {
+        this.h1Config = h1Config;
+        return this;
+    }
+
+    /**
+     * Sets message char coding.
      */
     public final H2RequesterBootstrap setCharCodingConfig(final CharCodingConfig charCodingConfig) {
         this.charCodingConfig = charCodingConfig;
@@ -169,6 +193,14 @@ public class H2RequesterBootstrap {
     }
 
     /**
+     * Assigns {@link Http1StreamListener} instance.
+     */
+    public final H2RequesterBootstrap setStreamListener(final Http1StreamListener http1StreamListener) {
+        this.http1StreamListener = http1StreamListener;
+        return this;
+    }
+
+    /**
      * Assigns {@link ConnPoolListener} instance.
      */
     public final H2RequesterBootstrap setConnPoolListener(final ConnPoolListener<HttpHost> connPoolListener) {
@@ -191,7 +223,7 @@ public class H2RequesterBootstrap {
         return this;
     }
 
-    public HttpAsyncRequester create() {
+    public Http2AsyncRequester create() {
         final StrictConnPool<HttpHost, IOSession> connPool = new StrictConnPool<>(
                 defaultMaxPerRoute > 0 ? defaultMaxPerRoute : 20,
                 maxTotal > 0 ? maxTotal : 50,
@@ -202,15 +234,27 @@ public class H2RequesterBootstrap {
         for (final PushConsumerEntry entry: pushConsumerList) {
             pushConsumerRegistry.register(entry.hostname, entry.uriPattern, entry.supplier);
         }
-        final ClientHttpProtocolNegotiatorFactory ioEventHandlerFactory = new ClientHttpProtocolNegotiatorFactory(
+        final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory(
+                httpProcessor != null ? httpProcessor : HttpProcessors.client(),
+                h1Config != null ? h1Config : H1Config.DEFAULT,
+                charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
+                connectionListener,
+                http1StreamListener);
+        final ClientHttp2StreamMultiplexerFactory http2StreamHandlerFactory = new ClientHttp2StreamMultiplexerFactory(
                 httpProcessor != null ? httpProcessor : Http2Processors.client(),
                 pushConsumerRegistry,
-                charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 h2Config != null ? h2Config : H2Config.DEFAULT,
+                charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 connectionListener,
                 streamListener);
+        final ClientHttpProtocolNegotiatorFactory ioEventHandlerFactory = new ClientHttpProtocolNegotiatorFactory(
+                http1StreamHandlerFactory,
+                http2StreamHandlerFactory,
+                versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
+                connectionListener);
         try {
-            return new HttpAsyncRequester(
+            return new Http2AsyncRequester(
+                    versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
                     ioReactorConfig,
                     ioEventHandlerFactory,
                     connPool,

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
index 23d62a1..60eff6f 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
@@ -32,21 +32,32 @@ import java.util.List;
 import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.ExceptionListener;
 import org.apache.hc.core5.http.config.CharCodingConfig;
+import org.apache.hc.core5.http.config.H1Config;
 import org.apache.hc.core5.http.impl.ConnectionListener;
+import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
+import org.apache.hc.core5.http.impl.Http1StreamListener;
+import org.apache.hc.core5.http.impl.HttpProcessors;
 import org.apache.hc.core5.http.impl.bootstrap.AsyncServerExchangeHandlerRegistry;
 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
+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.ServerHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
 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.http2.impl.nio.Http2StreamListener;
+import org.apache.hc.core5.http2.impl.nio.ServerHttp2StreamMultiplexerFactory;
 import org.apache.hc.core5.http2.impl.nio.ServerHttpProtocolNegotiatorFactory;
 import org.apache.hc.core5.http2.ssl.H2ServerTlsStrategy;
 import org.apache.hc.core5.net.InetAddressUtils;
+import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.IOReactorConfig;
 import org.apache.hc.core5.reactor.IOReactorException;
 import org.apache.hc.core5.util.Args;
@@ -61,11 +72,14 @@ public class H2ServerBootstrap {
     private IOReactorConfig ioReactorConfig;
     private HttpProcessor httpProcessor;
     private CharCodingConfig charCodingConfig;
+    private HttpVersionPolicy versionPolicy;
     private H2Config h2Config;
+    private H1Config h1Config;
     private TlsStrategy tlsStrategy;
     private ExceptionListener exceptionListener;
     private ConnectionListener connectionListener;
-    private Http2StreamListener streamListener;
+    private Http2StreamListener http2StreamListener;
+    private Http1StreamListener http1StreamListener;
 
     private H2ServerBootstrap() {
         this.handlerList = new ArrayList<>();
@@ -102,6 +116,14 @@ public class H2ServerBootstrap {
     }
 
     /**
+     * Sets HTTP protocol version policy
+     */
+    public final H2ServerBootstrap setVersionPolicy(final HttpVersionPolicy versionPolicy) {
+        this.versionPolicy = versionPolicy;
+        return this;
+    }
+
+    /**
      * Sets HTTP/2 protocol parameters
      */
     public final H2ServerBootstrap setH2Config(final H2Config h2Config) {
@@ -110,7 +132,15 @@ public class H2ServerBootstrap {
     }
 
     /**
-     * Sets char coding for HTTP/2 messages.
+     * Sets HTTP/1.1 protocol parameters
+     */
+    public final H2ServerBootstrap setH1Config(final H1Config h1Config) {
+        this.h1Config = h1Config;
+        return this;
+    }
+
+    /**
+     * Sets message char coding.
      */
     public final H2ServerBootstrap setCharset(final CharCodingConfig charCodingConfig) {
         this.charCodingConfig = charCodingConfig;
@@ -144,8 +174,16 @@ public class H2ServerBootstrap {
     /**
      * Assigns {@link Http2StreamListener} instance.
      */
-    public final H2ServerBootstrap setStreamListener(final Http2StreamListener streamListener) {
-        this.streamListener = streamListener;
+    public final H2ServerBootstrap setStreamListener(final Http2StreamListener http2StreamListener) {
+        this.http2StreamListener = http2StreamListener;
+        return this;
+    }
+
+    /**
+     * Assigns {@link Http1StreamListener} instance.
+     */
+    public final H2ServerBootstrap setStreamListener(final Http1StreamListener http1StreamListener) {
+        this.http1StreamListener = http1StreamListener;
         return this;
     }
 
@@ -201,14 +239,31 @@ public class H2ServerBootstrap {
         for (final HandlerEntry entry: handlerList) {
             exchangeHandlerFactory.register(entry.hostname, entry.uriPattern, entry.supplier);
         }
-        final ServerHttpProtocolNegotiatorFactory ioEventHandlerFactory = new ServerHttpProtocolNegotiatorFactory(
+        final ServerHttp2StreamMultiplexerFactory http2StreamHandlerFactory = new ServerHttp2StreamMultiplexerFactory(
                 httpProcessor != null ? httpProcessor : Http2Processors.server(),
                 exchangeHandlerFactory,
-                charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 h2Config != null ? h2Config : H2Config.DEFAULT,
-                tlsStrategy != null ? tlsStrategy : new H2ServerTlsStrategy(new int[] {443, 8443}),
+                charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 connectionListener,
-                streamListener);
+                http2StreamListener);
+        final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ServerHttp1StreamDuplexerFactory(
+                httpProcessor != null ? httpProcessor : HttpProcessors.server(),
+                exchangeHandlerFactory,
+                h1Config != null ? h1Config : H1Config.DEFAULT,
+                charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
+                DefaultConnectionReuseStrategy.INSTANCE,
+                DefaultHttpRequestParserFactory.INSTANCE,
+                DefaultHttpResponseWriterFactory.INSTANCE,
+                DefaultContentLengthStrategy.INSTANCE,
+                DefaultContentLengthStrategy.INSTANCE,
+                connectionListener,
+                http1StreamListener);
+        final IOEventHandlerFactory ioEventHandlerFactory = new ServerHttpProtocolNegotiatorFactory(
+                http1StreamHandlerFactory,
+                http2StreamHandlerFactory,
+                versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
+                tlsStrategy != null ? tlsStrategy : new H2ServerTlsStrategy(new int[] {443, 8443}),
+                connectionListener);
         try {
             return new HttpAsyncServer(
                     ioEventHandlerFactory,

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/Http2AsyncRequester.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/Http2AsyncRequester.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/Http2AsyncRequester.java
new file mode 100644
index 0000000..5594fea
--- /dev/null
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/Http2AsyncRequester.java
@@ -0,0 +1,73 @@
+/*
+ * ====================================================================
+ * 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.impl.nio.bootstrap;
+
+import java.util.concurrent.Future;
+
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.ExceptionListener;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
+import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
+import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.pool.ControlledConnPool;
+import org.apache.hc.core5.reactor.IOEventHandlerFactory;
+import org.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.reactor.IOReactorException;
+import org.apache.hc.core5.reactor.IOSession;
+import org.apache.hc.core5.util.TimeValue;
+
+/**
+ * @since 5.0
+ */
+public class Http2AsyncRequester extends HttpAsyncRequester {
+
+    private final HttpVersionPolicy versionPolicy;
+
+    public Http2AsyncRequester(
+            final HttpVersionPolicy versionPolicy,
+            final IOReactorConfig ioReactorConfig,
+            final IOEventHandlerFactory eventHandlerFactory,
+            final ControlledConnPool<HttpHost, IOSession> connPool,
+            final TlsStrategy tlsStrategy,
+            final ExceptionListener exceptionListener) throws IOReactorException {
+        super(ioReactorConfig, eventHandlerFactory, connPool, tlsStrategy, exceptionListener);
+        this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
+    }
+
+    @Override
+    protected Future<AsyncClientEndpoint> doConnect(
+            final HttpHost host,
+            final TimeValue timeout,
+            final Object attachment,
+            final FutureCallback<AsyncClientEndpoint> callback) {
+        return super.doConnect(host, timeout, attachment != null ? attachment : versionPolicy, callback);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ApplicationProtocols.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ApplicationProtocols.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ApplicationProtocols.java
new file mode 100644
index 0000000..29f90c9
--- /dev/null
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ApplicationProtocols.java
@@ -0,0 +1,50 @@
+/*
+ * ====================================================================
+ * 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;
+
+/**
+ * Supported application protocols.
+ *
+ * @since 5.0
+ */
+public enum ApplicationProtocols {
+
+    HTTP_2("h2"), HTTP_1_1("http/1.1");
+
+    public final String id;
+
+    ApplicationProtocols(final String id) {
+        this.id = id;
+    }
+
+    @Override
+    public String toString() {
+        return id;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/FixedPortStrategy.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/FixedPortStrategy.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/FixedPortStrategy.java
new file mode 100644
index 0000000..35ebe2b
--- /dev/null
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/FixedPortStrategy.java
@@ -0,0 +1,56 @@
+/*
+ * ====================================================================
+ * 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.InetSocketAddress;
+import java.net.SocketAddress;
+
+import org.apache.hc.core5.util.Args;
+
+/**
+ * @since 5.0
+ */
+public final class FixedPortStrategy implements SecurePortStrategy {
+
+    private final int[] securePorts;
+
+    public FixedPortStrategy(final int[] securePorts) {
+        this.securePorts = Args.notNull(securePorts, "Secure ports");
+    }
+
+    public boolean isSecure(final SocketAddress localAddress) {
+        final int port = ((InetSocketAddress) localAddress).getPort();
+        for (final int securePort: securePorts) {
+            if (port == securePort) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ClientTlsStrategy.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ClientTlsStrategy.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ClientTlsStrategy.java
index b517459..4d3e24b 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ClientTlsStrategy.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ClientTlsStrategy.java
@@ -91,11 +91,11 @@ public class H2ClientTlsStrategy implements TlsStrategy {
             final HttpHost host,
             final SocketAddress localAddress,
             final SocketAddress remoteAddress,
-            final String... parameters) {
+            final Object attachment) {
         final String scheme = host != null ? host.getSchemeName() : null;
         if ("https".equalsIgnoreCase(scheme)) {
             tlsSession.startTls(sslContext, sslBufferManagement,
-                    H2TlsSupport.enforceRequirements(initializer),
+                    H2TlsSupport.enforceRequirements(attachment, initializer),
                     verifier);
         }
     }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/ed4b808c/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ServerTlsStrategy.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ServerTlsStrategy.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ServerTlsStrategy.java
index f449767..2cceb77 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ServerTlsStrategy.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ServerTlsStrategy.java
@@ -27,7 +27,6 @@
 
 package org.apache.hc.core5.http2.ssl;
 
-import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 
 import javax.net.ssl.SSLContext;
@@ -49,46 +48,46 @@ import org.apache.hc.core5.util.Args;
  */
 public class H2ServerTlsStrategy implements TlsStrategy {
 
-    private final int[] securePorts;
     private final SSLContext sslContext;
+    private final SecurePortStrategy securePortStrategy;
     private final SSLBufferManagement sslBufferManagement;
     private final SSLSessionInitializer initializer;
     private final SSLSessionVerifier verifier;
 
     public H2ServerTlsStrategy(
-            final int[] securePorts,
             final SSLContext sslContext,
+            final SecurePortStrategy securePortStrategy,
             final SSLBufferManagement sslBufferManagement,
             final SSLSessionInitializer initializer,
             final SSLSessionVerifier verifier) {
-        this.securePorts = Args.notNull(securePorts, "Array of ports");
         this.sslContext = Args.notNull(sslContext, "SSL context");
+        this.securePortStrategy = securePortStrategy;
         this.sslBufferManagement = sslBufferManagement;
         this.initializer = initializer;
         this.verifier = verifier;
     }
 
     public H2ServerTlsStrategy(
-            final int[] securePorts,
             final SSLContext sslContext,
+            final SecurePortStrategy securePortStrategy,
             final SSLSessionInitializer initializer,
             final SSLSessionVerifier verifier) {
-        this(securePorts, sslContext, null, initializer, verifier);
+        this(sslContext, securePortStrategy, null, initializer, verifier);
     }
 
     public H2ServerTlsStrategy(
-            final int[] securePorts,
             final SSLContext sslContext,
+            final SecurePortStrategy securePortStrategy,
             final SSLSessionVerifier verifier) {
-        this(securePorts, sslContext, null, null, verifier);
+        this(sslContext, securePortStrategy, null, null, verifier);
     }
 
-    public H2ServerTlsStrategy(final int[] securePorts, final SSLContext sslContext) {
-        this(securePorts, sslContext, null, null, null);
+    public H2ServerTlsStrategy(final SSLContext sslContext, final SecurePortStrategy securePortStrategy) {
+        this(sslContext, securePortStrategy, null, null, null);
     }
 
     public H2ServerTlsStrategy(final int[] securePorts) {
-        this(securePorts, SSLContexts.createSystemDefault());
+        this(SSLContexts.createSystemDefault(), new FixedPortStrategy(securePorts));
     }
 
     @Override
@@ -97,15 +96,11 @@ public class H2ServerTlsStrategy implements TlsStrategy {
             final HttpHost host,
             final SocketAddress localAddress,
             final SocketAddress remoteAddress,
-            final String... parameters) {
-        final int port = ((InetSocketAddress) localAddress).getPort();
-        for (final int securePort: securePorts) {
-            if (port == securePort) {
-                tlsSession.startTls(sslContext, sslBufferManagement,
-                        H2TlsSupport.enforceRequirements(initializer),
-                        verifier);
-                break;
-            }
+            final Object attachment) {
+        if (securePortStrategy != null && securePortStrategy.isSecure(localAddress)) {
+            tlsSession.startTls(sslContext, sslBufferManagement,
+                    H2TlsSupport.enforceRequirements(attachment, initializer),
+                    verifier);
         }
     }
 


Mime
View raw message