You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2022/02/02 17:24:54 UTC

[httpcomponents-core] branch master updated (66215a3 -> a60a060)

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git.


 discard 66215a3  Protocol negotiators now keep track the negotiated HTTP protocol version and can report it to the application layer; improved HTTP protocol negotiation
     new a60a060  Protocol negotiators now keep track the negotiated HTTP protocol version and can report it to the application layer; improved HTTP protocol negotiation

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (66215a3)
            \
             N -- N -- N   refs/heads/master (a60a060)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/hc/core5/http2/impl/nio/HttpProtocolNegotiator.java     | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

[httpcomponents-core] 01/01: Protocol negotiators now keep track the negotiated HTTP protocol version and can report it to the application layer; improved HTTP protocol negotiation

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git

commit a60a060e2e2a532f136f902f5ae4cb0436fde6ec
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Thu Jan 27 18:37:51 2022 +0100

    Protocol negotiators now keep track the negotiated HTTP protocol version and can report it to the application layer; improved HTTP protocol negotiation
---
 ...Negotiator.java => ClientH2PrefaceHandler.java} |  20 ++-
 .../http2/impl/nio/ClientH2UpgradeHandler.java     |   2 +-
 ...Handler.java => ClientHttp1UpgradeHandler.java} |  25 +--
 ...a => ClientHttpProtocolNegotiationStarter.java} |  32 ++--
 .../impl/nio/ClientHttpProtocolNegotiator.java     | 187 ---------------------
 ...tiatorBase.java => HttpProtocolNegotiator.java} |  82 ++++++---
 ...NegotiatorBase.java => PrefaceHandlerBase.java} |  11 +-
 ...Negotiator.java => ServerH2PrefaceHandler.java} |  13 +-
 .../http2/impl/nio/ServerH2UpgradeHandler.java     |   2 +-
 ...Handler.java => ServerHttp1UpgradeHandler.java} |  31 ++--
 ...a => ServerHttpProtocolNegotiationStarter.java} |  46 +++--
 .../impl/nio/ServerHttpProtocolNegotiator.java     | 182 --------------------
 .../H2MultiplexingRequesterBootstrap.java          |   4 +-
 .../impl/nio/bootstrap/H2RequesterBootstrap.java   |   4 +-
 .../impl/nio/bootstrap/H2ServerBootstrap.java      |   4 +-
 .../apache/hc/core5/testing/nio/H2TestClient.java  |   4 +-
 .../apache/hc/core5/testing/nio/H2TestServer.java  |   4 +-
 ... InternalClientProtocolNegotiationStarter.java} |  27 ++-
 ... InternalServerProtocolNegotiationStarter.java} |  30 +++-
 .../http/impl/nio/ClientHttp1IOEventHandler.java   |   8 +
 .../http/impl/nio/ServerHttp1IOEventHandler.java   |   8 +
 .../hc/core5/reactor/InternalDataChannel.java      |   6 +-
 22 files changed, 241 insertions(+), 491 deletions(-)

diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2OnlyClientProtocolNegotiator.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2PrefaceHandler.java
similarity index 89%
rename from httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2OnlyClientProtocolNegotiator.java
rename to httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2PrefaceHandler.java
index 388e034..212ae22 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2OnlyClientProtocolNegotiator.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2PrefaceHandler.java
@@ -34,6 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.HttpVersion;
 import org.apache.hc.core5.http.impl.nio.BufferedData;
 import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
 import org.apache.hc.core5.reactor.IOSession;
@@ -47,10 +48,16 @@ import org.apache.hc.core5.util.TextUtils;
  * client side of the HTTP/2 protocol negotiation handshake always forcing the choice
  * of HTTP/2.
  *
- * @since 5.0
+ * @since 5.2
  */
 @Internal
-public class H2OnlyClientProtocolNegotiator extends ProtocolNegotiatorBase {
+public class ClientH2PrefaceHandler extends PrefaceHandlerBase {
+
+    // PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
+    final static byte[] PREFACE = new byte[] {
+            0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 0x54, 0x50,
+            0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d,
+            0x0d, 0x0a, 0x0d, 0x0a};
 
     private final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory;
     private final boolean strictALPNHandshake;
@@ -59,7 +66,7 @@ public class H2OnlyClientProtocolNegotiator extends ProtocolNegotiatorBase {
     private volatile ByteBuffer preface;
     private volatile BufferedData inBuf;
 
-    public H2OnlyClientProtocolNegotiator(
+    public ClientH2PrefaceHandler(
             final ProtocolIOSession ioSession,
             final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory,
             final boolean strictALPNHandshake) {
@@ -69,7 +76,7 @@ public class H2OnlyClientProtocolNegotiator extends ProtocolNegotiatorBase {
     /**
      * @since 5.1
      */
-    public H2OnlyClientProtocolNegotiator(
+    public ClientH2PrefaceHandler(
             final ProtocolIOSession ioSession,
             final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory,
             final boolean strictALPNHandshake,
@@ -94,7 +101,7 @@ public class H2OnlyClientProtocolNegotiator extends ProtocolNegotiatorBase {
                 }
             }
         }
-        this.preface = ByteBuffer.wrap(ClientHttpProtocolNegotiator.PREFACE);
+        this.preface = ByteBuffer.wrap(PREFACE);
         ioSession.setEvent(SelectionKey.OP_WRITE);
     }
 
@@ -104,9 +111,8 @@ public class H2OnlyClientProtocolNegotiator extends ProtocolNegotiatorBase {
         }
         if (!preface.hasRemaining()) {
             session.clearEvent(SelectionKey.OP_WRITE);
-            final ClientH2StreamMultiplexer streamMultiplexer = http2StreamHandlerFactory.create(ioSession);
             final ByteBuffer data = inBuf != null ? inBuf.data() : null;
-            startProtocol(new ClientH2IOEventHandler(streamMultiplexer), data);
+            startProtocol(HttpVersion.HTTP_2, new ClientH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), data);
             if (inBuf != null) {
                 inBuf.clear();
             }
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2UpgradeHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2UpgradeHandler.java
index ee548f8..9009a9f 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2UpgradeHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2UpgradeHandler.java
@@ -56,7 +56,7 @@ public class ClientH2UpgradeHandler implements ProtocolUpgradeHandler {
 
     @Override
     public void upgrade(final ProtocolIOSession ioSession, final FutureCallback<ProtocolIOSession> callback) {
-        final HttpConnectionEventHandler protocolNegotiator = new H2OnlyClientProtocolNegotiator(
+        final HttpConnectionEventHandler protocolNegotiator = new ClientH2PrefaceHandler(
                 ioSession, http2StreamHandlerFactory, true, callback);
         ioSession.upgrade(protocolNegotiator);
         try {
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2UpgradeHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp1UpgradeHandler.java
similarity index 67%
copy from httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2UpgradeHandler.java
copy to httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp1UpgradeHandler.java
index ee548f8..082bc68 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientH2UpgradeHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttp1UpgradeHandler.java
@@ -33,36 +33,39 @@ import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.concurrent.FutureCallback;
-import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler;
+import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler;
+import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
 import org.apache.hc.core5.reactor.ProtocolUpgradeHandler;
 import org.apache.hc.core5.util.Args;
 
 /**
  * Protocol upgrade handler that upgrades the underlying {@link ProtocolIOSession}
- * to HTTP/2 in case of a successful protocol negotiation.
+ * to HTTP/1.1 in case of a successful protocol negotiation or as a default fall-back.
  *
  * @since 5.2
  */
 @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 @Internal
-public class ClientH2UpgradeHandler implements ProtocolUpgradeHandler {
+public class ClientHttp1UpgradeHandler implements ProtocolUpgradeHandler {
 
-    private final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory;
+    private final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory;
 
-    public ClientH2UpgradeHandler(final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory) {
-        this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory");
+    public ClientHttp1UpgradeHandler(final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory) {
+        this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory");
     }
 
     @Override
     public void upgrade(final ProtocolIOSession ioSession, final FutureCallback<ProtocolIOSession> callback) {
-        final HttpConnectionEventHandler protocolNegotiator = new H2OnlyClientProtocolNegotiator(
-                ioSession, http2StreamHandlerFactory, true, callback);
-        ioSession.upgrade(protocolNegotiator);
+        final ClientHttp1IOEventHandler eventHandler = new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession));
+        ioSession.upgrade(eventHandler);
         try {
-            protocolNegotiator.connected(ioSession);
+            eventHandler.connected(ioSession);
+            if (callback != null) {
+                callback.completed(ioSession);
+            }
         } catch (final IOException ex) {
-            protocolNegotiator.exception(ioSession, ex);
+            eventHandler.exception(ioSession, ex);
         }
     }
 
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/ClientHttpProtocolNegotiationStarter.java
similarity index 73%
rename from httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiatorFactory.java
rename to httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiationStarter.java
index 82fe49b..a07f38a 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/ClientHttpProtocolNegotiationStarter.java
@@ -31,9 +31,12 @@ import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler;
 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.ssl.TlsStrategy;
 import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
 import org.apache.hc.core5.reactor.EndpointParameters;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
@@ -41,13 +44,15 @@ import org.apache.hc.core5.util.Args;
 import org.apache.hc.core5.util.Timeout;
 
 /**
- * {@link ClientHttpProtocolNegotiator} factory.
+ * Client I/O event starter that prepares I/O sessions for an initial protocol handshake.
+ * This class may return a different {@link org.apache.hc.core5.reactor.IOEventHandler}
+ * implementation based on the current HTTP version policy.
  *
- * @since 5.0
+ * @since 5.1
  */
 @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 @Internal
-public class ClientHttpProtocolNegotiatorFactory implements IOEventHandlerFactory {
+public class ClientHttpProtocolNegotiationStarter implements IOEventHandlerFactory {
 
     private final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory;
     private final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory;
@@ -55,7 +60,7 @@ public class ClientHttpProtocolNegotiatorFactory implements IOEventHandlerFactor
     private final TlsStrategy tlsStrategy;
     private final Timeout handshakeTimeout;
 
-    public ClientHttpProtocolNegotiatorFactory(
+    public ClientHttpProtocolNegotiationStarter(
             final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory,
             final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory,
             final HttpVersionPolicy versionPolicy,
@@ -69,7 +74,7 @@ public class ClientHttpProtocolNegotiatorFactory implements IOEventHandlerFactor
     }
 
     @Override
-    public ClientHttpProtocolNegotiator createHandler(final ProtocolIOSession ioSession, final Object attachment) {
+    public HttpConnectionEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) {
         HttpVersionPolicy endpointPolicy = versionPolicy;
         if (attachment instanceof EndpointParameters) {
             final EndpointParameters params = (EndpointParameters) attachment;
@@ -80,11 +85,18 @@ public class ClientHttpProtocolNegotiatorFactory implements IOEventHandlerFactor
                 endpointPolicy = (HttpVersionPolicy) params.getAttachment();
             }
         }
-        return new ClientHttpProtocolNegotiator(
-                ioSession,
-                http1StreamHandlerFactory,
-                http2StreamHandlerFactory,
-                endpointPolicy);
+
+        ioSession.registerProtocol(ApplicationProtocol.HTTP_1_1.id, new ClientHttp1UpgradeHandler(http1StreamHandlerFactory));
+        ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ClientH2UpgradeHandler(http2StreamHandlerFactory));
+
+        switch (endpointPolicy) {
+            case FORCE_HTTP_2:
+                return new ClientH2PrefaceHandler(ioSession, http2StreamHandlerFactory, false);
+            case FORCE_HTTP_1:
+                return new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession));
+            default:
+                return new HttpProtocolNegotiator(ioSession, null);
+        }
     }
 
 }
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
deleted file mode 100644
index 9c2152f..0000000
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiator.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.hc.core5.http2.impl.nio;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.SelectionKey;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.hc.core5.annotation.Internal;
-import org.apache.hc.core5.concurrent.FutureCallback;
-import org.apache.hc.core5.http.impl.nio.BufferedData;
-import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler;
-import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory;
-import org.apache.hc.core5.http2.HttpVersionPolicy;
-import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
-import org.apache.hc.core5.reactor.IOSession;
-import org.apache.hc.core5.reactor.ProtocolIOSession;
-import org.apache.hc.core5.reactor.ssl.TlsDetails;
-import org.apache.hc.core5.util.Args;
-
-/**
- * I/O event handler for events fired by {@link ProtocolIOSession} that implements
- * client side of the HTTP/2 protocol negotiation handshake
- * based on {@link HttpVersionPolicy} configuration.
- *
- * @since 5.0
- */
-@Internal
-public class ClientHttpProtocolNegotiator extends ProtocolNegotiatorBase {
-
-    // PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
-    final static byte[] PREFACE = new byte[] {
-            0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, 0x54, 0x50,
-            0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d,
-            0x0d, 0x0a, 0x0d, 0x0a};
-
-    private final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory;
-    private final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory;
-    private final HttpVersionPolicy versionPolicy;
-    private final AtomicBoolean initialized;
-
-    private volatile ByteBuffer preface;
-    private volatile BufferedData inBuf;
-
-    public ClientHttpProtocolNegotiator(
-            final ProtocolIOSession ioSession,
-            final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory,
-            final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory,
-            final HttpVersionPolicy versionPolicy) {
-        this(ioSession, http1StreamHandlerFactory, http2StreamHandlerFactory, versionPolicy, null);
-    }
-
-    /**
-     * @since 5.1
-     */
-    public ClientHttpProtocolNegotiator(
-            final ProtocolIOSession ioSession,
-            final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory,
-            final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory,
-            final HttpVersionPolicy versionPolicy,
-            final FutureCallback<ProtocolIOSession> resultCallback) {
-        super(ioSession, resultCallback);
-        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.initialized = new AtomicBoolean();
-    }
-
-    private void startHttp1() throws IOException {
-        final ByteBuffer data = inBuf != null ? inBuf.data() : null;
-        startProtocol(new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession)), data);
-        ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ClientH2UpgradeHandler(http2StreamHandlerFactory));
-        if (inBuf != null) {
-            inBuf.clear();
-        }
-    }
-
-    private void startHttp2() throws IOException {
-        final ByteBuffer data = inBuf != null ? inBuf.data() : null;
-        startProtocol(new ClientH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), data);
-        if (inBuf != null) {
-            inBuf.clear();
-        }
-    }
-
-    private void initialize() throws IOException {
-        switch (versionPolicy) {
-            case NEGOTIATE:
-                final TlsDetails tlsDetails = ioSession.getTlsDetails();
-                if (tlsDetails != null) {
-                    if (ApplicationProtocol.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) {
-            startHttp1();
-        } else {
-            ioSession.setEvent(SelectionKey.OP_WRITE);
-        }
-    }
-
-    private void writeOutPreface(final IOSession session) throws IOException {
-        if (preface.hasRemaining()) {
-            session.write(preface);
-        }
-        if (!preface.hasRemaining()) {
-            session.clearEvent(SelectionKey.OP_WRITE);
-            startHttp2();
-            preface = null;
-        }
-    }
-
-    @Override
-    public void connected(final IOSession session) throws IOException {
-        if (initialized.compareAndSet(false, true)) {
-            initialize();
-        }
-        if (preface != null) {
-            writeOutPreface(session);
-        }
-    }
-
-    @Override
-    public void inputReady(final IOSession session, final ByteBuffer src) throws IOException  {
-        if (src != null) {
-            if (inBuf == null) {
-                inBuf = BufferedData.allocate(src.remaining());
-            }
-            inBuf.put(src);
-        }
-        if (preface != null) {
-            writeOutPreface(session);
-        } else {
-            throw new ProtocolNegotiationException("Unexpected input");
-        }
-    }
-
-    @Override
-    public void outputReady(final IOSession session) throws IOException {
-        if (initialized.compareAndSet(false, true)) {
-            initialize();
-        }
-        if (preface != null) {
-            writeOutPreface(session);
-        } else {
-            throw new ProtocolNegotiationException("Unexpected output");
-        }
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getName() + "/" + versionPolicy;
-    }
-
-}
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ProtocolNegotiatorBase.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/HttpProtocolNegotiator.java
similarity index 66%
copy from httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ProtocolNegotiatorBase.java
copy to httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/HttpProtocolNegotiator.java
index 89e787c..3c6e08f 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ProtocolNegotiatorBase.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/HttpProtocolNegotiator.java
@@ -35,46 +35,79 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import javax.net.ssl.SSLSession;
 
+import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.http.ConnectionClosedException;
 import org.apache.hc.core5.http.EndpointDetails;
+import org.apache.hc.core5.http.HttpVersion;
 import org.apache.hc.core5.http.ProtocolVersion;
 import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler;
 import org.apache.hc.core5.http.nio.command.CommandSupport;
+import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
 import org.apache.hc.core5.io.CloseMode;
 import org.apache.hc.core5.io.SocketTimeoutExceptionFactory;
 import org.apache.hc.core5.reactor.IOSession;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
 import org.apache.hc.core5.reactor.ssl.TlsDetails;
 import org.apache.hc.core5.util.Args;
+import org.apache.hc.core5.util.TextUtils;
 import org.apache.hc.core5.util.Timeout;
 
-abstract class ProtocolNegotiatorBase implements HttpConnectionEventHandler {
+/**
+ * @since 5.2
+ */
+@Internal
+public class HttpProtocolNegotiator implements HttpConnectionEventHandler {
 
-    final ProtocolIOSession ioSession;
-    private final AtomicReference<HttpConnectionEventHandler> protocolHandlerRef;
+    private final ProtocolIOSession ioSession;
     private final FutureCallback<ProtocolIOSession> resultCallback;
     private final AtomicBoolean completed;
+    private final AtomicReference<ProtocolVersion> negotiatedProtocolRef;
 
-    ProtocolNegotiatorBase(
+    public HttpProtocolNegotiator(
             final ProtocolIOSession ioSession,
             final FutureCallback<ProtocolIOSession> resultCallback) {
         this.ioSession = Args.notNull(ioSession, "I/O session");
-        this.protocolHandlerRef = new AtomicReference<>();
         this.resultCallback = resultCallback;
         this.completed = new AtomicBoolean();
+        this.negotiatedProtocolRef = new AtomicReference<>();
     }
 
-    void startProtocol(final HttpConnectionEventHandler protocolHandler, final ByteBuffer data) throws IOException {
-        protocolHandlerRef.set(protocolHandler);
-        ioSession.upgrade(protocolHandler);
-        protocolHandler.connected(ioSession);
-        if (data != null && data.hasRemaining()) {
-            protocolHandler.inputReady(ioSession, data);
-        }
-        if (completed.compareAndSet(false, true) && resultCallback != null) {
-            resultCallback.completed(ioSession);
+    void startProtocol(final HttpVersion httpVersion) {
+        ioSession.switchProtocol(
+                httpVersion == HttpVersion.HTTP_2 ? ApplicationProtocol.HTTP_2.id : ApplicationProtocol.HTTP_1_1.id,
+                resultCallback);
+        negotiatedProtocolRef.set(httpVersion);
+    }
+
+    @Override
+    public void connected(final IOSession session) throws IOException {
+        final HttpVersion httpVersion;
+        final TlsDetails tlsDetails = ioSession.getTlsDetails();
+        if (tlsDetails != null) {
+            final String appProtocol = tlsDetails.getApplicationProtocol();
+            if (TextUtils.isEmpty(appProtocol)) {
+                httpVersion = HttpVersion.HTTP_1_1;
+            } else if (appProtocol.equals(ApplicationProtocol.HTTP_1_1.id)) {
+                httpVersion = HttpVersion.HTTP_1_1;
+            } else if (appProtocol.equals(ApplicationProtocol.HTTP_2.id)) {
+                httpVersion = HttpVersion.HTTP_1_1;
+            } else {
+                throw new ProtocolNegotiationException("Unsupported application protocol: " + appProtocol);
+            }
+        } else {
+            httpVersion = HttpVersion.HTTP_1_1;
         }
+        startProtocol(httpVersion);
+    }
+    @Override
+    public void inputReady(final IOSession session, final ByteBuffer src) throws IOException  {
+        throw new ProtocolNegotiationException("Unexpected input");
+    }
+
+    @Override
+    public void outputReady(final IOSession session) throws IOException {
+        throw new ProtocolNegotiationException("Unexpected output");
     }
 
     @Override
@@ -84,14 +117,9 @@ abstract class ProtocolNegotiatorBase implements HttpConnectionEventHandler {
 
     @Override
     public void exception(final IOSession session, final Exception cause) {
-        final HttpConnectionEventHandler protocolHandler = protocolHandlerRef.get();
         try {
             session.close(CloseMode.IMMEDIATE);
-            if (protocolHandler != null) {
-                protocolHandler.exception(session, cause);
-            } else {
-                CommandSupport.failCommands(session, cause);
-            }
+            CommandSupport.failCommands(session, cause);
         } catch (final Exception ex) {
             if (completed.compareAndSet(false, true) && resultCallback != null) {
                 resultCallback.failed(ex);
@@ -101,13 +129,8 @@ abstract class ProtocolNegotiatorBase implements HttpConnectionEventHandler {
 
     @Override
     public void disconnected(final IOSession session) {
-        final HttpConnectionEventHandler protocolHandler = protocolHandlerRef.getAndSet(null);
         try {
-            if (protocolHandler != null) {
-                protocolHandler.disconnected(ioSession);
-            } else {
-                CommandSupport.cancelCommands(session);
-            }
+            CommandSupport.cancelCommands(session);
         } finally {
             if (completed.compareAndSet(false, true) && resultCallback != null) {
                 resultCallback.failed(new ConnectionClosedException());
@@ -138,7 +161,7 @@ abstract class ProtocolNegotiatorBase implements HttpConnectionEventHandler {
 
     @Override
     public ProtocolVersion getProtocolVersion() {
-        return null;
+        return negotiatedProtocolRef.get();
     }
 
     @Override
@@ -166,4 +189,9 @@ abstract class ProtocolNegotiatorBase implements HttpConnectionEventHandler {
         ioSession.close(closeMode);
     }
 
+    @Override
+    public String toString() {
+        return getClass().getName();
+    }
+
 }
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ProtocolNegotiatorBase.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/PrefaceHandlerBase.java
similarity index 91%
rename from httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ProtocolNegotiatorBase.java
rename to httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/PrefaceHandlerBase.java
index 89e787c..6d58932 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ProtocolNegotiatorBase.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/PrefaceHandlerBase.java
@@ -49,23 +49,26 @@ import org.apache.hc.core5.reactor.ssl.TlsDetails;
 import org.apache.hc.core5.util.Args;
 import org.apache.hc.core5.util.Timeout;
 
-abstract class ProtocolNegotiatorBase implements HttpConnectionEventHandler {
+abstract class PrefaceHandlerBase implements HttpConnectionEventHandler {
 
     final ProtocolIOSession ioSession;
     private final AtomicReference<HttpConnectionEventHandler> protocolHandlerRef;
     private final FutureCallback<ProtocolIOSession> resultCallback;
     private final AtomicBoolean completed;
+    private final AtomicReference<ProtocolVersion> negotiatedProtocolRef;
 
-    ProtocolNegotiatorBase(
+    PrefaceHandlerBase(
             final ProtocolIOSession ioSession,
             final FutureCallback<ProtocolIOSession> resultCallback) {
         this.ioSession = Args.notNull(ioSession, "I/O session");
         this.protocolHandlerRef = new AtomicReference<>();
         this.resultCallback = resultCallback;
         this.completed = new AtomicBoolean();
+        this.negotiatedProtocolRef = new AtomicReference<>();
     }
 
-    void startProtocol(final HttpConnectionEventHandler protocolHandler, final ByteBuffer data) throws IOException {
+    void startProtocol(final ProtocolVersion protocolVersion, final HttpConnectionEventHandler protocolHandler, final ByteBuffer data) throws IOException {
+        negotiatedProtocolRef.set(protocolVersion);
         protocolHandlerRef.set(protocolHandler);
         ioSession.upgrade(protocolHandler);
         protocolHandler.connected(ioSession);
@@ -138,7 +141,7 @@ abstract class ProtocolNegotiatorBase implements HttpConnectionEventHandler {
 
     @Override
     public ProtocolVersion getProtocolVersion() {
-        return null;
+        return negotiatedProtocolRef.get();
     }
 
     @Override
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2OnlyServerHttpProtocolNegotiator.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2PrefaceHandler.java
similarity index 89%
rename from httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2OnlyServerHttpProtocolNegotiator.java
rename to httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2PrefaceHandler.java
index a770f01..e918350 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/H2OnlyServerHttpProtocolNegotiator.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2PrefaceHandler.java
@@ -33,6 +33,7 @@ import java.nio.ByteBuffer;
 import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.http.ConnectionClosedException;
+import org.apache.hc.core5.http.HttpVersion;
 import org.apache.hc.core5.http.impl.nio.BufferedData;
 import org.apache.hc.core5.reactor.IOSession;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
@@ -42,23 +43,23 @@ import org.apache.hc.core5.util.Args;
  * I/O event handler for events fired by {@link ProtocolIOSession} that implements
  * server side of the HTTP/2 protocol negotiation handshake.
  *
- * @since 5.1
+ * @since 5.2
  */
 @Internal
-public class H2OnlyServerHttpProtocolNegotiator extends ProtocolNegotiatorBase {
+public class ServerH2PrefaceHandler extends PrefaceHandlerBase {
 
-    final static byte[] PREFACE = ClientHttpProtocolNegotiator.PREFACE;
+    final static byte[] PREFACE = ClientH2PrefaceHandler.PREFACE;
 
     private final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory;
     private final BufferedData inBuf;
 
-    public H2OnlyServerHttpProtocolNegotiator(
+    public ServerH2PrefaceHandler(
             final ProtocolIOSession ioSession,
             final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory) {
         this(ioSession, http2StreamHandlerFactory, null);
     }
 
-    public H2OnlyServerHttpProtocolNegotiator(
+    public ServerH2PrefaceHandler(
             final ProtocolIOSession ioSession,
             final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory,
             final FutureCallback<ProtocolIOSession> resultCallback) {
@@ -90,7 +91,7 @@ public class H2OnlyServerHttpProtocolNegotiator extends ProtocolNegotiatorBase {
                     throw new ProtocolNegotiationException("Unexpected HTTP/2 preface");
                 }
             }
-            startProtocol(new ServerH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), data.hasRemaining() ? data : null);
+            startProtocol(HttpVersion.HTTP_2, new ServerH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), data.hasRemaining() ? data : null);
         } else {
             if (endOfStream) {
                 throw new ConnectionClosedException();
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2UpgradeHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2UpgradeHandler.java
index 181a465..02b23f5 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2UpgradeHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2UpgradeHandler.java
@@ -56,7 +56,7 @@ public class ServerH2UpgradeHandler implements ProtocolUpgradeHandler {
 
     @Override
     public void upgrade(final ProtocolIOSession ioSession, final FutureCallback<ProtocolIOSession> callback) {
-        final HttpConnectionEventHandler protocolNegotiator = new H2OnlyServerHttpProtocolNegotiator(
+        final HttpConnectionEventHandler protocolNegotiator = new ServerH2PrefaceHandler(
                 ioSession, http2StreamHandlerFactory, callback);
         ioSession.upgrade(protocolNegotiator);
         try {
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2UpgradeHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp1UpgradeHandler.java
similarity index 61%
copy from httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2UpgradeHandler.java
copy to httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp1UpgradeHandler.java
index 181a465..3093612 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerH2UpgradeHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp1UpgradeHandler.java
@@ -33,36 +33,45 @@ import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.concurrent.FutureCallback;
-import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandler;
+import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
 import org.apache.hc.core5.reactor.ProtocolUpgradeHandler;
+import org.apache.hc.core5.reactor.ssl.TlsDetails;
 import org.apache.hc.core5.util.Args;
 
 /**
  * Protocol upgrade handler that upgrades the underlying {@link ProtocolIOSession}
- * to HTTP/2 in case of a successful protocol negotiation.
+ * to HTTP/1.1 in case of a successful protocol negotiation or as a default fall-back.
  *
  * @since 5.2
  */
 @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 @Internal
-public class ServerH2UpgradeHandler implements ProtocolUpgradeHandler {
+public class ServerHttp1UpgradeHandler implements ProtocolUpgradeHandler {
 
-    private final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory;
+    private final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory;
 
-    public ServerH2UpgradeHandler(final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory) {
-        this.http2StreamHandlerFactory = Args.notNull(http2StreamHandlerFactory, "HTTP/2 stream handler factory");
+    public ServerHttp1UpgradeHandler(final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory) {
+        this.http1StreamHandlerFactory = Args.notNull(http1StreamHandlerFactory, "HTTP/1.1 stream handler factory");
     }
 
     @Override
     public void upgrade(final ProtocolIOSession ioSession, final FutureCallback<ProtocolIOSession> callback) {
-        final HttpConnectionEventHandler protocolNegotiator = new H2OnlyServerHttpProtocolNegotiator(
-                ioSession, http2StreamHandlerFactory, callback);
-        ioSession.upgrade(protocolNegotiator);
+        final TlsDetails tlsDetails = ioSession.getTlsDetails();
+        final ServerHttp1IOEventHandler eventHandler = new ServerHttp1IOEventHandler(http1StreamHandlerFactory.create(
+                tlsDetails != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id,
+                ioSession));
+        ioSession.upgrade(eventHandler);
+        ioSession.upgrade(eventHandler);
         try {
-            protocolNegotiator.connected(ioSession);
+            eventHandler.connected(ioSession);
+            if (callback != null) {
+                callback.completed(ioSession);
+            }
         } catch (final IOException ex) {
-            protocolNegotiator.exception(ioSession, ex);
+            eventHandler.exception(ioSession, ex);
         }
     }
 
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/ServerHttpProtocolNegotiationStarter.java
similarity index 60%
rename from httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiatorFactory.java
rename to httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiationStarter.java
index 9143538..e9f1eac 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/ServerHttpProtocolNegotiationStarter.java
@@ -31,9 +31,12 @@ import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.impl.nio.HttpConnectionEventHandler;
+import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandler;
 import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
 import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
 import org.apache.hc.core5.reactor.EndpointParameters;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
@@ -41,50 +44,61 @@ import org.apache.hc.core5.util.Args;
 import org.apache.hc.core5.util.Timeout;
 
 /**
- * {@link ServerHttpProtocolNegotiator} factory.
+ * Server I/O event starter that prepares I/O sessions for an initial protocol handshake.
+ * This class may return a different {@link org.apache.hc.core5.reactor.IOEventHandler}
+ * implementation based on the current HTTP version policy.
  *
- * @since 5.0
+ * @since 5.1
  */
 @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 @Internal
-public class ServerHttpProtocolNegotiatorFactory implements IOEventHandlerFactory {
+public class ServerHttpProtocolNegotiationStarter implements IOEventHandlerFactory {
 
-    private final ServerHttp1StreamDuplexerFactory http1StreamDuplexerFactory;
-    private final ServerH2StreamMultiplexerFactory http2StreamMultiplexerFactory;
+    private final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory;
+    private final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory;
     private final HttpVersionPolicy versionPolicy;
     private final TlsStrategy tlsStrategy;
     private final Timeout handshakeTimeout;
 
-    public ServerHttpProtocolNegotiatorFactory(
-            final ServerHttp1StreamDuplexerFactory http1StreamDuplexerFactory,
-            final ServerH2StreamMultiplexerFactory http2StreamMultiplexerFactory,
+    public ServerHttpProtocolNegotiationStarter(
+            final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory,
+            final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory,
             final HttpVersionPolicy versionPolicy,
             final TlsStrategy tlsStrategy,
             final Timeout handshakeTimeout) {
-        this.http1StreamDuplexerFactory = Args.notNull(http1StreamDuplexerFactory, "HTTP/1.1 stream handler factory");
-        this.http2StreamMultiplexerFactory = Args.notNull(http2StreamMultiplexerFactory, "HTTP/2 stream handler factory");
+        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.tlsStrategy = tlsStrategy;
         this.handshakeTimeout = handshakeTimeout;
     }
 
     @Override
-    public ServerHttpProtocolNegotiator createHandler(final ProtocolIOSession ioSession, final Object attachment) {
+    public HttpConnectionEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) {
         HttpVersionPolicy endpointPolicy = versionPolicy;
+        URIScheme uriScheme = URIScheme.HTTP;
         if (attachment instanceof EndpointParameters) {
             final EndpointParameters params = (EndpointParameters) attachment;
             if (tlsStrategy != null && URIScheme.HTTPS.same(params.getScheme())) {
+                uriScheme = URIScheme.HTTPS;
                 tlsStrategy.upgrade(ioSession, params, params.getAttachment(), handshakeTimeout, null);
             }
             if (params.getAttachment() instanceof HttpVersionPolicy) {
                 endpointPolicy = (HttpVersionPolicy) params.getAttachment();
             }
         }
-        return new ServerHttpProtocolNegotiator(
-                ioSession,
-                http1StreamDuplexerFactory,
-                http2StreamMultiplexerFactory,
-                endpointPolicy);
+
+        ioSession.registerProtocol(ApplicationProtocol.HTTP_1_1.id, new ServerHttp1UpgradeHandler(http1StreamHandlerFactory));
+        ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ServerH2UpgradeHandler(http2StreamHandlerFactory));
+
+        switch (endpointPolicy) {
+            case FORCE_HTTP_2:
+                return new ServerH2PrefaceHandler(ioSession, http2StreamHandlerFactory);
+            case FORCE_HTTP_1:
+                return new ServerHttp1IOEventHandler(http1StreamHandlerFactory.create(uriScheme.id, ioSession));
+            default:
+                return new HttpProtocolNegotiator(ioSession, null);
+        }
     }
 
 }
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
deleted file mode 100644
index f6febff..0000000
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiator.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.hc.core5.http2.impl.nio;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.hc.core5.annotation.Internal;
-import org.apache.hc.core5.concurrent.FutureCallback;
-import org.apache.hc.core5.http.ConnectionClosedException;
-import org.apache.hc.core5.http.URIScheme;
-import org.apache.hc.core5.http.impl.nio.BufferedData;
-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.HttpVersionPolicy;
-import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
-import org.apache.hc.core5.reactor.IOSession;
-import org.apache.hc.core5.reactor.ProtocolIOSession;
-import org.apache.hc.core5.reactor.ssl.TlsDetails;
-import org.apache.hc.core5.util.Args;
-
-/**
- * I/O event handler for events fired by {@link ProtocolIOSession} that implements
- * server side of the HTTP/2 protocol negotiation handshake
- * based on {@link HttpVersionPolicy} configuration.
- *
- * @since 5.0
- */
-@Internal
-public class ServerHttpProtocolNegotiator extends ProtocolNegotiatorBase {
-
-    final static byte[] PREFACE = ClientHttpProtocolNegotiator.PREFACE;
-
-    private final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory;
-    private final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory;
-    private final HttpVersionPolicy versionPolicy;
-    private final BufferedData inBuf;
-    private final AtomicBoolean initialized;
-
-    private volatile boolean expectValidH2Preface;
-
-    public ServerHttpProtocolNegotiator(
-            final ProtocolIOSession ioSession,
-            final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory,
-            final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory,
-            final HttpVersionPolicy versionPolicy) {
-        this(ioSession, http1StreamHandlerFactory, http2StreamHandlerFactory, versionPolicy, null);
-    }
-
-    /**
-     * @since 5.1
-     */
-    public ServerHttpProtocolNegotiator(
-            final ProtocolIOSession ioSession,
-            final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory,
-            final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory,
-            final HttpVersionPolicy versionPolicy,
-            final FutureCallback<ProtocolIOSession> resultCallback) {
-        super(ioSession, resultCallback);
-        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.inBuf = BufferedData.allocate(1024);
-        this.initialized = new AtomicBoolean();
-    }
-
-    private void startHttp1(final TlsDetails tlsDetails, final ByteBuffer data) throws IOException {
-        final ServerHttp1StreamDuplexer http1StreamHandler = http1StreamHandlerFactory.create(
-                tlsDetails != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id,
-                ioSession);
-        startProtocol(new ServerHttp1IOEventHandler(http1StreamHandler), data);
-        ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ServerH2UpgradeHandler(http2StreamHandlerFactory));
-    }
-
-    private void startHttp2(final ByteBuffer data) throws IOException {
-        startProtocol(new ServerH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), data);
-    }
-
-    private void initialize() throws IOException {
-        final TlsDetails tlsDetails = ioSession.getTlsDetails();
-        switch (versionPolicy) {
-            case NEGOTIATE:
-                if (tlsDetails != null &&
-                        ApplicationProtocol.HTTP_2.id.equals(tlsDetails.getApplicationProtocol())) {
-                    expectValidH2Preface = true;
-                }
-                break;
-            case FORCE_HTTP_2:
-                if (tlsDetails == null ||
-                        !ApplicationProtocol.HTTP_1_1.id.equals(tlsDetails.getApplicationProtocol())) {
-                    expectValidH2Preface = true;
-                }
-                break;
-            case FORCE_HTTP_1:
-                startHttp1(tlsDetails, null);
-                break;
-        }
-    }
-
-    @Override
-    public void connected(final IOSession session) throws IOException {
-        if (initialized.compareAndSet(false, true)) {
-            initialize();
-        }
-    }
-
-    @Override
-    public void inputReady(final IOSession session, final ByteBuffer src) throws IOException {
-        if (src != null) {
-            inBuf.put(src);
-        }
-        boolean endOfStream = false;
-        if (inBuf.length() < PREFACE.length) {
-            final int bytesRead = inBuf.readFrom(session);
-            if (bytesRead == -1) {
-                endOfStream = true;
-            }
-        }
-        final ByteBuffer data = inBuf.data();
-        if (data.remaining() >= PREFACE.length) {
-            boolean validH2Preface = true;
-            for (int i = 0; i < PREFACE.length; i++) {
-                if (data.get() != PREFACE[i]) {
-                    if (expectValidH2Preface) {
-                        throw new ProtocolNegotiationException("Unexpected HTTP/2 preface");
-                    }
-                    validH2Preface = false;
-                }
-            }
-            if (validH2Preface) {
-                startHttp2(data.hasRemaining() ? data : null);
-            } else {
-                data.rewind();
-                startHttp1(ioSession.getTlsDetails(), data);
-            }
-        } else {
-            if (endOfStream) {
-                throw new ConnectionClosedException();
-            }
-        }
-    }
-
-    @Override
-    public void outputReady(final IOSession session) throws IOException {
-        if (initialized.compareAndSet(false, true)) {
-            initialize();
-        }
-    }
-
-    @Override
-    public String toString() {
-        return getClass().getName() + "/" + versionPolicy;
-    }
-
-}
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2MultiplexingRequesterBootstrap.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2MultiplexingRequesterBootstrap.java
index 4351b5c..57cd21d 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2MultiplexingRequesterBootstrap.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2MultiplexingRequesterBootstrap.java
@@ -42,7 +42,7 @@ import org.apache.hc.core5.http.protocol.UriPatternType;
 import org.apache.hc.core5.http2.config.H2Config;
 import org.apache.hc.core5.http2.impl.H2Processors;
 import org.apache.hc.core5.http2.impl.nio.ClientH2StreamMultiplexerFactory;
-import org.apache.hc.core5.http2.impl.nio.H2OnlyClientProtocolNegotiator;
+import org.apache.hc.core5.http2.impl.nio.ClientH2PrefaceHandler;
 import org.apache.hc.core5.http2.impl.nio.H2StreamListener;
 import org.apache.hc.core5.http2.nio.support.DefaultAsyncPushConsumerFactory;
 import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy;
@@ -207,7 +207,7 @@ public class H2MultiplexingRequesterBootstrap {
                 streamListener);
         return new H2MultiplexingRequester(
                 ioReactorConfig,
-                (ioSession, attachment) -> new H2OnlyClientProtocolNegotiator(ioSession, http2StreamHandlerFactory, strictALPNHandshake),
+                (ioSession, attachment) -> new ClientH2PrefaceHandler(ioSession, http2StreamHandlerFactory, strictALPNHandshake),
                 ioSessionDecorator,
                 exceptionCallback,
                 sessionListener,
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 dc5883c..8ea1490 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
@@ -52,7 +52,7 @@ import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.http2.config.H2Config;
 import org.apache.hc.core5.http2.impl.H2Processors;
 import org.apache.hc.core5.http2.impl.nio.ClientH2StreamMultiplexerFactory;
-import org.apache.hc.core5.http2.impl.nio.ClientHttpProtocolNegotiatorFactory;
+import org.apache.hc.core5.http2.impl.nio.ClientHttpProtocolNegotiationStarter;
 import org.apache.hc.core5.http2.impl.nio.H2StreamListener;
 import org.apache.hc.core5.http2.nio.support.DefaultAsyncPushConsumerFactory;
 import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy;
@@ -334,7 +334,7 @@ public class H2RequesterBootstrap {
                 DefaultContentLengthStrategy.INSTANCE,
                 http1StreamListener);
 
-        final IOEventHandlerFactory ioEventHandlerFactory = new ClientHttpProtocolNegotiatorFactory(
+        final IOEventHandlerFactory ioEventHandlerFactory = new ClientHttpProtocolNegotiationStarter(
                 http1StreamHandlerFactory,
                 http2StreamHandlerFactory,
                 versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
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 9201feb..d60f842 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
@@ -65,7 +65,7 @@ import org.apache.hc.core5.http2.config.H2Config;
 import org.apache.hc.core5.http2.impl.H2Processors;
 import org.apache.hc.core5.http2.impl.nio.H2StreamListener;
 import org.apache.hc.core5.http2.impl.nio.ServerH2StreamMultiplexerFactory;
-import org.apache.hc.core5.http2.impl.nio.ServerHttpProtocolNegotiatorFactory;
+import org.apache.hc.core5.http2.impl.nio.ServerHttpProtocolNegotiationStarter;
 import org.apache.hc.core5.http2.ssl.H2ServerTlsStrategy;
 import org.apache.hc.core5.net.InetAddressUtils;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
@@ -415,7 +415,7 @@ public class H2ServerBootstrap {
                 DefaultContentLengthStrategy.INSTANCE,
                 http1StreamListener);
 
-        final IOEventHandlerFactory ioEventHandlerFactory = new ServerHttpProtocolNegotiatorFactory(
+        final IOEventHandlerFactory ioEventHandlerFactory = new ServerHttpProtocolNegotiationStarter(
                 http1StreamHandlerFactory,
                 http2StreamHandlerFactory,
                 versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/H2TestClient.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/H2TestClient.java
index 48b3682..7197ef6 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/H2TestClient.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/H2TestClient.java
@@ -88,7 +88,7 @@ public class H2TestClient extends AsyncRequester {
     }
 
     public void start(final HttpProcessor httpProcessor, final H2Config h2Config) throws IOException {
-        start(new InternalClientH2EventHandlerFactory(
+        start(new InternalClientProtocolNegotiationStarter(
                 httpProcessor,
                 new DefaultAsyncPushConsumerFactory(registry),
                 HttpVersionPolicy.FORCE_HTTP_2,
@@ -101,7 +101,7 @@ public class H2TestClient extends AsyncRequester {
     }
 
     public void start(final HttpProcessor httpProcessor, final Http1Config http1Config) throws IOException {
-        start(new InternalClientH2EventHandlerFactory(
+        start(new InternalClientProtocolNegotiationStarter(
                 httpProcessor,
                 new DefaultAsyncPushConsumerFactory(registry),
                 HttpVersionPolicy.FORCE_HTTP_1,
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/H2TestServer.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/H2TestServer.java
index 5406f55..149af46 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/H2TestServer.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/H2TestServer.java
@@ -95,7 +95,7 @@ public class H2TestServer extends AsyncServer {
             final HttpProcessor httpProcessor,
             final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator,
             final H2Config h2Config) throws Exception {
-        start(new InternalServerH2EventHandlerFactory(
+        start(new InternalServerProtocolNegotiationStarter(
                 httpProcessor != null ? httpProcessor : H2Processors.server(),
                 new DefaultAsyncResponseExchangeHandlerFactory(
                         registry,
@@ -116,7 +116,7 @@ public class H2TestServer extends AsyncServer {
             final HttpProcessor httpProcessor,
             final Decorator<AsyncServerExchangeHandler> exchangeHandlerDecorator,
             final Http1Config http1Config) throws Exception {
-        start(new InternalServerH2EventHandlerFactory(
+        start(new InternalServerProtocolNegotiationStarter(
                 httpProcessor != null ? httpProcessor : HttpProcessors.server(),
                 new DefaultAsyncResponseExchangeHandlerFactory(
                         registry,
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientH2EventHandlerFactory.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientProtocolNegotiationStarter.java
similarity index 80%
rename from httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientH2EventHandlerFactory.java
rename to httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientProtocolNegotiationStarter.java
index 240eddd..2453214 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientH2EventHandlerFactory.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalClientProtocolNegotiationStarter.java
@@ -32,6 +32,7 @@ import javax.net.ssl.SSLContext;
 import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.config.Http1Config;
 import org.apache.hc.core5.http.impl.HttpProcessors;
+import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler;
 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;
@@ -39,8 +40,12 @@ 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.H2Processors;
+import org.apache.hc.core5.http2.impl.nio.ClientH2PrefaceHandler;
 import org.apache.hc.core5.http2.impl.nio.ClientH2StreamMultiplexerFactory;
-import org.apache.hc.core5.http2.impl.nio.ClientHttpProtocolNegotiator;
+import org.apache.hc.core5.http2.impl.nio.ClientH2UpgradeHandler;
+import org.apache.hc.core5.http2.impl.nio.ClientHttp1UpgradeHandler;
+import org.apache.hc.core5.http2.impl.nio.HttpProtocolNegotiator;
+import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
 import org.apache.hc.core5.reactor.IOEventHandler;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
@@ -48,7 +53,7 @@ import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
 import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
 import org.apache.hc.core5.util.Args;
 
-class InternalClientH2EventHandlerFactory implements IOEventHandlerFactory {
+class InternalClientProtocolNegotiationStarter implements IOEventHandlerFactory {
 
     private final HttpProcessor httpProcessor;
     private final HandlerFactory<AsyncPushConsumer> exchangeHandlerFactory;
@@ -60,7 +65,7 @@ class InternalClientH2EventHandlerFactory implements IOEventHandlerFactory {
     private final SSLSessionInitializer sslSessionInitializer;
     private final SSLSessionVerifier sslSessionVerifier;
 
-    InternalClientH2EventHandlerFactory(
+    InternalClientProtocolNegotiationStarter(
             final HttpProcessor httpProcessor,
             final HandlerFactory<AsyncPushConsumer> exchangeHandlerFactory,
             final HttpVersionPolicy versionPolicy,
@@ -97,11 +102,17 @@ class InternalClientH2EventHandlerFactory implements IOEventHandlerFactory {
                 h2Config,
                 charCodingConfig,
                 LoggingH2StreamListener.INSTANCE);
-        return new ClientHttpProtocolNegotiator(
-                        ioSession,
-                        http1StreamHandlerFactory,
-                        http2StreamHandlerFactory,
-                        versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE);
+        ioSession.registerProtocol(ApplicationProtocol.HTTP_1_1.id, new ClientHttp1UpgradeHandler(http1StreamHandlerFactory));
+        ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ClientH2UpgradeHandler(http2StreamHandlerFactory));
+
+        switch (versionPolicy) {
+            case FORCE_HTTP_2:
+                return new ClientH2PrefaceHandler(ioSession, http2StreamHandlerFactory, false);
+            case FORCE_HTTP_1:
+                return new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession));
+            default:
+                return new HttpProtocolNegotiator(ioSession, null);
+        }
    }
 
 }
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerH2EventHandlerFactory.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerProtocolNegotiationStarter.java
similarity index 78%
rename from httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerH2EventHandlerFactory.java
rename to httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerProtocolNegotiationStarter.java
index 6aefbf0..dccf104 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerH2EventHandlerFactory.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/InternalServerProtocolNegotiationStarter.java
@@ -29,9 +29,11 @@ package org.apache.hc.core5.testing.nio;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.http.URIScheme;
 import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.config.Http1Config;
 import org.apache.hc.core5.http.impl.HttpProcessors;
+import org.apache.hc.core5.http.impl.nio.ServerHttp1IOEventHandler;
 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;
@@ -39,8 +41,12 @@ 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.H2Processors;
+import org.apache.hc.core5.http2.impl.nio.HttpProtocolNegotiator;
+import org.apache.hc.core5.http2.impl.nio.ServerH2PrefaceHandler;
 import org.apache.hc.core5.http2.impl.nio.ServerH2StreamMultiplexerFactory;
-import org.apache.hc.core5.http2.impl.nio.ServerHttpProtocolNegotiator;
+import org.apache.hc.core5.http2.impl.nio.ServerH2UpgradeHandler;
+import org.apache.hc.core5.http2.impl.nio.ServerHttp1UpgradeHandler;
+import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
 import org.apache.hc.core5.reactor.IOEventHandler;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
@@ -48,7 +54,7 @@ import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
 import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
 import org.apache.hc.core5.util.Args;
 
-class InternalServerH2EventHandlerFactory implements IOEventHandlerFactory {
+class InternalServerProtocolNegotiationStarter implements IOEventHandlerFactory {
 
     private final HttpProcessor httpProcessor;
     private final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory;
@@ -60,7 +66,7 @@ class InternalServerH2EventHandlerFactory implements IOEventHandlerFactory {
     private final SSLSessionInitializer sslSessionInitializer;
     private final SSLSessionVerifier sslSessionVerifier;
 
-    public InternalServerH2EventHandlerFactory(
+    public InternalServerProtocolNegotiationStarter(
             final HttpProcessor httpProcessor,
             final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory,
             final HttpVersionPolicy versionPolicy,
@@ -98,11 +104,19 @@ class InternalServerH2EventHandlerFactory implements IOEventHandlerFactory {
                 h2Config,
                 charCodingConfig,
                 LoggingH2StreamListener.INSTANCE);
-        return new ServerHttpProtocolNegotiator(
-                        ioSession,
-                        http1StreamHandlerFactory,
-                        http2StreamHandlerFactory,
-                        versionPolicy);
+        ioSession.registerProtocol(ApplicationProtocol.HTTP_1_1.id, new ServerHttp1UpgradeHandler(http1StreamHandlerFactory));
+        ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ServerH2UpgradeHandler(http2StreamHandlerFactory));
+
+        switch (versionPolicy) {
+            case FORCE_HTTP_2:
+                return new ServerH2PrefaceHandler(ioSession, http2StreamHandlerFactory);
+            case FORCE_HTTP_1:
+                return new ServerHttp1IOEventHandler(http1StreamHandlerFactory.create(
+                        sslContext != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id,
+                        ioSession));
+            default:
+                return new HttpProtocolNegotiator(ioSession, null);
+        }
     }
 
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1IOEventHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1IOEventHandler.java
index 0ed0a6e..7454a35 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1IOEventHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1IOEventHandler.java
@@ -27,6 +27,8 @@
 
 package org.apache.hc.core5.http.impl.nio;
 
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.ProtocolVersion;
 import org.apache.hc.core5.net.InetAddressUtils;
 
 /**
@@ -54,5 +56,11 @@ public class ClientHttp1IOEventHandler extends AbstractHttp1IOEventHandler {
         return buf.toString();
     }
 
+    @Override
+    public ProtocolVersion getProtocolVersion() {
+        final ProtocolVersion protocolVersion = super.getProtocolVersion();
+        return protocolVersion != null ? protocolVersion : HttpVersion.HTTP_1_1;
+    }
+
 }
 
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1IOEventHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1IOEventHandler.java
index 2398e3d..28b3052 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1IOEventHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1IOEventHandler.java
@@ -27,6 +27,8 @@
 
 package org.apache.hc.core5.http.impl.nio;
 
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.ProtocolVersion;
 import org.apache.hc.core5.net.InetAddressUtils;
 
 /**
@@ -54,4 +56,10 @@ public class ServerHttp1IOEventHandler extends AbstractHttp1IOEventHandler {
         return buf.toString();
     }
 
+    @Override
+    public ProtocolVersion getProtocolVersion() {
+        final ProtocolVersion protocolVersion = super.getProtocolVersion();
+        return protocolVersion != null ? protocolVersion : HttpVersion.HTTP_1_1;
+    }
+
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java b/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java
index b069cc3..f28da03 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/reactor/InternalDataChannel.java
@@ -68,6 +68,7 @@ final class InternalDataChannel extends InternalChannel implements ProtocolIOSes
     private final Queue<InternalDataChannel> closedSessions;
     private final AtomicReference<SSLIOSession> tlsSessionRef;
     private final AtomicReference<IOSession> currentSessionRef;
+    private final AtomicReference<IOEventHandler> eventHandlerRef;
     private final ConcurrentMap<String, ProtocolUpgradeHandler> protocolUpgradeHandlerMap;
     private final AtomicBoolean closed;
 
@@ -85,6 +86,7 @@ final class InternalDataChannel extends InternalChannel implements ProtocolIOSes
         this.tlsSessionRef = new AtomicReference<>();
         this.currentSessionRef = new AtomicReference<>(
                 ioSessionDecorator != null ? ioSessionDecorator.decorate(ioSession) : ioSession);
+        this.eventHandlerRef = new AtomicReference<>();
         this.protocolUpgradeHandlerMap = new ConcurrentHashMap<>();
         this.closed = new AtomicBoolean(false);
     }
@@ -101,14 +103,14 @@ final class InternalDataChannel extends InternalChannel implements ProtocolIOSes
 
     @Override
     public IOEventHandler getHandler() {
-        final IOSession currentSession = currentSessionRef.get();
-        return currentSession.getHandler();
+        return eventHandlerRef.get();
     }
 
     @Override
     public void upgrade(final IOEventHandler handler) {
         final IOSession currentSession = currentSessionRef.get();
         currentSession.upgrade(handler);
+        eventHandlerRef.set(handler);
     }
 
     private IOEventHandler ensureHandler(final IOSession session) {