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 15:55:57 UTC

[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

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

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

commit f9aa9553994de5993813188f8a421e06535b6084
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Wed Feb 2 13:09:52 2022 +0100

    Protocol negotiators now keep track the negotiated HTTP protocol version and can report it to the application layer; improved HTTP protocol negotiation
---
 ...a => ClientHttpProtocolNegotiationStarter.java} | 27 +++++++++-----
 .../impl/nio/ClientHttpProtocolNegotiator.java     |  5 ++-
 .../nio/ClientHttpProtocolNegotiatorFactory.java   |  3 ++
 .../impl/nio/H2OnlyClientProtocolNegotiator.java   | 11 +++++-
 .../nio/H2OnlyServerHttpProtocolNegotiator.java    | 10 ++++-
 .../http2/impl/nio/ProtocolNegotiatorBase.java     |  7 +++-
 ...a => ServerHttpProtocolNegotiationStarter.java} | 43 +++++++++++++---------
 .../impl/nio/ServerHttpProtocolNegotiator.java     |  9 ++---
 .../nio/ServerHttpProtocolNegotiatorFactory.java   |  3 ++
 .../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} | 19 ++++++----
 ... InternalServerProtocolNegotiationStarter.java} | 22 +++++++----
 .../http/impl/nio/ClientHttp1IOEventHandler.java   |  8 ++++
 .../http/impl/nio/ServerHttp1IOEventHandler.java   |  8 ++++
 .../hc/core5/reactor/InternalDataChannel.java      |  6 ++-
 18 files changed, 133 insertions(+), 64 deletions(-)

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 78%
copy from httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiatorFactory.java
copy to httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiationStarter.java
index e257ae9..06d0998 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
@@ -32,7 +32,9 @@ import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.HttpHost;
 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.reactor.EndpointParameters;
@@ -43,13 +45,15 @@ import org.apache.hc.core5.util.Asserts;
 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;
@@ -57,7 +61,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,
@@ -71,7 +75,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;
@@ -90,11 +94,14 @@ public class ClientHttpProtocolNegotiatorFactory implements IOEventHandlerFactor
                 endpointPolicy = (HttpVersionPolicy) params.getAttachment();
             }
         }
-        return new ClientHttpProtocolNegotiator(
-                ioSession,
-                http1StreamHandlerFactory,
-                http2StreamHandlerFactory,
-                endpointPolicy);
+        switch (endpointPolicy) {
+            case FORCE_HTTP_2:
+                return new H2OnlyClientProtocolNegotiator(ioSession, http2StreamHandlerFactory, false);
+            case FORCE_HTTP_1:
+                return new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession));
+            default:
+                return new ClientHttpProtocolNegotiator(ioSession, http1StreamHandlerFactory, http2StreamHandlerFactory, HttpVersionPolicy.NEGOTIATE);
+        }
     }
 
 }
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 a1a7f4b..4840450 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
@@ -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.http.impl.nio.ClientHttp1IOEventHandler;
 import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory;
@@ -94,7 +95,7 @@ public class ClientHttpProtocolNegotiator extends ProtocolNegotiatorBase {
 
     private void startHttp1() throws IOException {
         final ByteBuffer data = inBuf != null ? inBuf.data() : null;
-        startProtocol(new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession)), data);
+        startProtocol(HttpVersion.HTTP_1_1, new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession)), data);
         if (inBuf != null) {
             inBuf.clear();
         }
@@ -102,7 +103,7 @@ public class ClientHttpProtocolNegotiator extends ProtocolNegotiatorBase {
 
     private void startHttp2() throws IOException {
         final ByteBuffer data = inBuf != null ? inBuf.data() : null;
-        startProtocol(new ClientH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), 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/ClientHttpProtocolNegotiatorFactory.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiatorFactory.java
index e257ae9..2ecfde4 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
@@ -46,9 +46,12 @@ import org.apache.hc.core5.util.Timeout;
  * {@link ClientHttpProtocolNegotiator} factory.
  *
  * @since 5.0
+ *
+ * @deprecated Use {@link ClientHttpProtocolNegotiationStarter}
  */
 @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 @Internal
+@Deprecated
 public class ClientHttpProtocolNegotiatorFactory implements IOEventHandlerFactory {
 
     private final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory;
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/H2OnlyClientProtocolNegotiator.java
index a4f7df5..9808bfb 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/H2OnlyClientProtocolNegotiator.java
@@ -34,6 +34,8 @@ 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.ProtocolVersion;
 import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
 import org.apache.hc.core5.reactor.IOSession;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
@@ -102,8 +104,7 @@ public class H2OnlyClientProtocolNegotiator extends ProtocolNegotiatorBase {
         }
         if (!preface.hasRemaining()) {
             session.clearEvent(SelectionKey.OP_WRITE);
-            final ClientH2StreamMultiplexer streamMultiplexer = http2StreamHandlerFactory.create(ioSession);
-            startProtocol(new ClientH2IOEventHandler(streamMultiplexer), null);
+            startProtocol(HttpVersion.HTTP_2, new ClientH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), null);
             preface = null;
         }
     }
@@ -140,6 +141,12 @@ public class H2OnlyClientProtocolNegotiator extends ProtocolNegotiatorBase {
     }
 
     @Override
+    public ProtocolVersion getProtocolVersion() {
+        final ProtocolVersion protocolVersion = super.getProtocolVersion();
+        return protocolVersion != null ? protocolVersion : HttpVersion.HTTP_2;
+    }
+
+    @Override
     public String toString() {
         return getClass().getName();
     }
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/H2OnlyServerHttpProtocolNegotiator.java
index a770f01..6aa2248 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/H2OnlyServerHttpProtocolNegotiator.java
@@ -33,6 +33,8 @@ 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.ProtocolVersion;
 import org.apache.hc.core5.http.impl.nio.BufferedData;
 import org.apache.hc.core5.reactor.IOSession;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
@@ -90,7 +92,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();
@@ -103,6 +105,12 @@ public class H2OnlyServerHttpProtocolNegotiator extends ProtocolNegotiatorBase {
     }
 
     @Override
+    public ProtocolVersion getProtocolVersion() {
+        final ProtocolVersion protocolVersion = super.getProtocolVersion();
+        return protocolVersion != null ? protocolVersion : HttpVersion.HTTP_2;
+    }
+
+    @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/ProtocolNegotiatorBase.java
index 89e787c..ccf1b92 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/ProtocolNegotiatorBase.java
@@ -55,6 +55,7 @@ abstract class ProtocolNegotiatorBase implements HttpConnectionEventHandler {
     private final AtomicReference<HttpConnectionEventHandler> protocolHandlerRef;
     private final FutureCallback<ProtocolIOSession> resultCallback;
     private final AtomicBoolean completed;
+    private final AtomicReference<ProtocolVersion> negotiatedProtocolRef;
 
     ProtocolNegotiatorBase(
             final ProtocolIOSession ioSession,
@@ -63,9 +64,11 @@ abstract class ProtocolNegotiatorBase implements HttpConnectionEventHandler {
         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/ClientHttpProtocolNegotiatorFactory.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiationStarter.java
similarity index 68%
copy from httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientHttpProtocolNegotiatorFactory.java
copy to httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttpProtocolNegotiationStarter.java
index e257ae9..ebc9a0f 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/ServerHttpProtocolNegotiationStarter.java
@@ -30,9 +30,10 @@ package org.apache.hc.core5.http2.impl.nio;
 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.HttpHost;
 import org.apache.hc.core5.http.URIScheme;
-import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory;
+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.reactor.EndpointParameters;
@@ -43,23 +44,25 @@ import org.apache.hc.core5.util.Asserts;
 import org.apache.hc.core5.util.Timeout;
 
 /**
- * {@link ClientHttpProtocolNegotiator} 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 ClientHttpProtocolNegotiatorFactory implements IOEventHandlerFactory {
+public class ServerHttpProtocolNegotiationStarter implements IOEventHandlerFactory {
 
-    private final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory;
-    private final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory;
+    private final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory;
+    private final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory;
     private final HttpVersionPolicy versionPolicy;
     private final TlsStrategy tlsStrategy;
     private final Timeout handshakeTimeout;
 
-    public ClientHttpProtocolNegotiatorFactory(
-            final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory,
-            final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory,
+    public ServerHttpProtocolNegotiationStarter(
+            final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory,
+            final ServerH2StreamMultiplexerFactory http2StreamHandlerFactory,
             final HttpVersionPolicy versionPolicy,
             final TlsStrategy tlsStrategy,
             final Timeout handshakeTimeout) {
@@ -71,16 +74,17 @@ 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;
+        URIScheme uriScheme = URIScheme.HTTP;
         if (attachment instanceof EndpointParameters) {
             final EndpointParameters params = (EndpointParameters) attachment;
             if (URIScheme.HTTPS.same(params.getScheme())) {
                 Asserts.notNull(tlsStrategy, "TLS strategy");
-                final HttpHost host = new HttpHost(params.getScheme(), params.getHostName(), params.getPort());
+                uriScheme = URIScheme.HTTPS;
                 tlsStrategy.upgrade(
                         ioSession,
-                        host,
+                        null,
                         ioSession.getLocalAddress(),
                         ioSession.getRemoteAddress(),
                         params.getAttachment(),
@@ -90,11 +94,14 @@ public class ClientHttpProtocolNegotiatorFactory implements IOEventHandlerFactor
                 endpointPolicy = (HttpVersionPolicy) params.getAttachment();
             }
         }
-        return new ClientHttpProtocolNegotiator(
-                ioSession,
-                http1StreamHandlerFactory,
-                http2StreamHandlerFactory,
-                endpointPolicy);
+        switch (endpointPolicy) {
+            case FORCE_HTTP_2:
+                return new H2OnlyServerHttpProtocolNegotiator(ioSession, http2StreamHandlerFactory);
+            case FORCE_HTTP_1:
+                return new ServerHttp1IOEventHandler(http1StreamHandlerFactory.create(uriScheme.id, ioSession));
+            default:
+                return new ServerHttpProtocolNegotiator(ioSession, http1StreamHandlerFactory, http2StreamHandlerFactory, HttpVersionPolicy.NEGOTIATE);
+        }
     }
 
 }
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 8d9aed2..1211aec 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
@@ -34,10 +34,10 @@ 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.HttpVersion;
 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;
@@ -92,14 +92,13 @@ public class ServerHttpProtocolNegotiator extends ProtocolNegotiatorBase {
     }
 
     private void startHttp1(final TlsDetails tlsDetails, final ByteBuffer data) throws IOException {
-        final ServerHttp1StreamDuplexer http1StreamHandler = http1StreamHandlerFactory.create(
+        startProtocol(HttpVersion.HTTP_1_1, new ServerHttp1IOEventHandler(http1StreamHandlerFactory.create(
                 tlsDetails != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id,
-                ioSession);
-        startProtocol(new ServerHttp1IOEventHandler(http1StreamHandler), data);
+                ioSession)), data);
     }
 
     private void startHttp2(final ByteBuffer data) throws IOException {
-        startProtocol(new ServerH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), data);
+        startProtocol(HttpVersion.HTTP_2, new ServerH2IOEventHandler(http2StreamHandlerFactory.create(ioSession)), data);
     }
 
     private void initialize() throws IOException {
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 e3f432c..60b634d 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
@@ -45,9 +45,12 @@ import org.apache.hc.core5.util.Timeout;
  * {@link ServerHttpProtocolNegotiator} factory.
  *
  * @since 5.0
+ *
+ * @deprecated Use {@link ServerHttpProtocolNegotiationStarter}
  */
 @Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 @Internal
+@Deprecated
 public class ServerHttpProtocolNegotiatorFactory implements IOEventHandlerFactory {
 
     private final ServerHttp1StreamDuplexerFactory http1StreamDuplexerFactory;
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 44adb0d..3dbb4b2 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;
@@ -335,7 +335,7 @@ public class H2RequesterBootstrap {
                 DefaultContentLengthStrategy.INSTANCE,
                 http1StreamListener);
 
-        final IOEventHandlerFactory ioEventHandlerFactory = new ClientHttpProtocolNegotiatorFactory(
+        final IOEventHandlerFactory ioEventHandlerFactory = new ClientHttpProtocolNegotiationStarter(
                 http1StreamHandlerFactory,
                 http2StreamHandlerFactory,
                 actualVersionProtocol,
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 a983611..879b87c 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;
@@ -444,7 +444,7 @@ public class H2ServerBootstrap {
                 DefaultContentLengthStrategy.INSTANCE,
                 http1StreamListener);
 
-        final IOEventHandlerFactory ioEventHandlerFactory = new ServerHttpProtocolNegotiatorFactory(
+        final IOEventHandlerFactory ioEventHandlerFactory = new ServerHttpProtocolNegotiationStarter(
                 http1StreamHandlerFactory,
                 http2StreamHandlerFactory,
                 actualVersionProtocol,
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 7707bfd..8650bb1 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
@@ -102,7 +102,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,
@@ -130,7 +130,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 86%
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..6be8a16 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;
@@ -41,6 +42,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.ClientH2StreamMultiplexerFactory;
 import org.apache.hc.core5.http2.impl.nio.ClientHttpProtocolNegotiator;
+import org.apache.hc.core5.http2.impl.nio.H2OnlyClientProtocolNegotiator;
 import org.apache.hc.core5.reactor.IOEventHandler;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
@@ -48,7 +50,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 +62,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 +99,14 @@ class InternalClientH2EventHandlerFactory implements IOEventHandlerFactory {
                 h2Config,
                 charCodingConfig,
                 LoggingH2StreamListener.INSTANCE);
-        return new ClientHttpProtocolNegotiator(
-                        ioSession,
-                        http1StreamHandlerFactory,
-                        http2StreamHandlerFactory,
-                        versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE);
+        switch (versionPolicy) {
+            case FORCE_HTTP_2:
+                return new H2OnlyClientProtocolNegotiator(ioSession, http2StreamHandlerFactory, false);
+            case FORCE_HTTP_1:
+                return new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession));
+            default:
+                return new ClientHttpProtocolNegotiator(ioSession, http1StreamHandlerFactory, http2StreamHandlerFactory, HttpVersionPolicy.NEGOTIATE);
+        }
    }
 
 }
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 84%
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..868f16f 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,6 +41,7 @@ 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.H2OnlyServerHttpProtocolNegotiator;
 import org.apache.hc.core5.http2.impl.nio.ServerH2StreamMultiplexerFactory;
 import org.apache.hc.core5.http2.impl.nio.ServerHttpProtocolNegotiator;
 import org.apache.hc.core5.reactor.IOEventHandler;
@@ -48,7 +51,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 +63,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 +101,16 @@ class InternalServerH2EventHandlerFactory implements IOEventHandlerFactory {
                 h2Config,
                 charCodingConfig,
                 LoggingH2StreamListener.INSTANCE);
-        return new ServerHttpProtocolNegotiator(
-                        ioSession,
-                        http1StreamHandlerFactory,
-                        http2StreamHandlerFactory,
-                        versionPolicy);
+        switch (versionPolicy) {
+            case FORCE_HTTP_2:
+                return new H2OnlyServerHttpProtocolNegotiator(ioSession, http2StreamHandlerFactory);
+            case FORCE_HTTP_1:
+                return new ServerHttp1IOEventHandler(http1StreamHandlerFactory.create(
+                        sslContext != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id,
+                        ioSession));
+            default:
+                return new ServerHttpProtocolNegotiator(ioSession, http1StreamHandlerFactory, http2StreamHandlerFactory, HttpVersionPolicy.NEGOTIATE);
+        }
     }
 
 }
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 abf93fd..34e31c4 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
@@ -61,6 +61,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 AtomicBoolean closed;
 
     InternalDataChannel(
@@ -77,6 +78,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.closed = new AtomicBoolean(false);
     }
 
@@ -92,14 +94,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) {