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 2021/03/11 22:12:29 UTC

[httpcomponents-core] 05/06: TLS upgrade and TLS strategy APIs redesign

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 b5b2f09ea22a7b7b6b1e9cb250f093f4fc31a7a4
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sun Dec 13 11:10:40 2020 +0100

    TLS upgrade and TLS strategy APIs redesign
---
 .../nio/ClientHttpProtocolNegotiatorFactory.java   | 14 ++-------
 .../nio/ServerHttpProtocolNegotiatorFactory.java   | 12 ++------
 .../impl/nio/bootstrap/H2RequesterBootstrap.java   |  3 +-
 .../impl/nio/bootstrap/H2ServerBootstrap.java      |  3 +-
 .../apache/hc/core5/http2/nio/pool/H2ConnPool.java | 15 +++++++---
 .../http2/ssl/ConscryptClientTlsStrategy.java      | 31 +++++++++++++++-----
 .../http2/ssl/ConscryptServerTlsStrategy.java      | 32 +++++++++++++++-----
 .../hc/core5/http2/ssl/H2ClientTlsStrategy.java    | 31 +++++++++++++++-----
 .../hc/core5/http2/ssl/H2ServerTlsStrategy.java    | 32 +++++++++++++++-----
 .../impl/nio/ClientHttp1IOEventHandlerFactory.java | 10 +------
 .../impl/nio/ServerHttp1IOEventHandlerFactory.java | 22 ++++++--------
 .../core5/http/nio/ssl/BasicClientTlsStrategy.java | 20 +++++++++++--
 .../core5/http/nio/ssl/BasicServerTlsStrategy.java | 20 +++++++++++--
 .../apache/hc/core5/http/nio/ssl/TlsStrategy.java  | 32 +++++++++++++++++++-
 .../hc/core5/reactor/InternalDataChannel.java      | 34 ++++++++++++++++++++--
 .../apache/hc/core5/reactor/ssl/SSLIOSession.java  | 16 +++++-----
 .../core5/reactor/ssl/TransportSecurityLayer.java  | 29 ++++++++++++++++++
 17 files changed, 258 insertions(+), 98 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/ClientHttpProtocolNegotiatorFactory.java
index e257ae9..82fe49b 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
@@ -30,7 +30,6 @@ 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.nio.ssl.TlsStrategy;
@@ -39,7 +38,6 @@ import org.apache.hc.core5.reactor.EndpointParameters;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
 import org.apache.hc.core5.util.Args;
-import org.apache.hc.core5.util.Asserts;
 import org.apache.hc.core5.util.Timeout;
 
 /**
@@ -75,16 +73,8 @@ public class ClientHttpProtocolNegotiatorFactory implements IOEventHandlerFactor
         HttpVersionPolicy endpointPolicy = versionPolicy;
         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());
-                tlsStrategy.upgrade(
-                        ioSession,
-                        host,
-                        ioSession.getLocalAddress(),
-                        ioSession.getRemoteAddress(),
-                        params.getAttachment(),
-                        handshakeTimeout);
+            if (tlsStrategy != null && URIScheme.HTTPS.same(params.getScheme())) {
+                tlsStrategy.upgrade(ioSession, params, params.getAttachment(), handshakeTimeout, null);
             }
             if (params.getAttachment() instanceof HttpVersionPolicy) {
                 endpointPolicy = (HttpVersionPolicy) params.getAttachment();
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..9143538 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
@@ -38,7 +38,6 @@ import org.apache.hc.core5.reactor.EndpointParameters;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.ProtocolIOSession;
 import org.apache.hc.core5.util.Args;
-import org.apache.hc.core5.util.Asserts;
 import org.apache.hc.core5.util.Timeout;
 
 /**
@@ -74,15 +73,8 @@ public class ServerHttpProtocolNegotiatorFactory implements IOEventHandlerFactor
         HttpVersionPolicy endpointPolicy = versionPolicy;
         if (attachment instanceof EndpointParameters) {
             final EndpointParameters params = (EndpointParameters) attachment;
-            if (URIScheme.HTTPS.same(params.getScheme())) {
-                Asserts.notNull(tlsStrategy, "TLS strategy");
-                tlsStrategy.upgrade(
-                        ioSession,
-                        null,
-                        ioSession.getLocalAddress(),
-                        ioSession.getRemoteAddress(),
-                        params.getAttachment(),
-                        handshakeTimeout);
+            if (tlsStrategy != null && URIScheme.HTTPS.same(params.getScheme())) {
+                tlsStrategy.upgrade(ioSession, params, params.getAttachment(), handshakeTimeout, null);
             }
             if (params.getAttachment() instanceof HttpVersionPolicy) {
                 endpointPolicy = (HttpVersionPolicy) params.getAttachment();
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 bce7df9..ab94662 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
@@ -321,7 +321,6 @@ public class H2RequesterBootstrap {
                 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 streamListener);
 
-        final HttpVersionPolicy actualVersionProtocol = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
         final TlsStrategy actualTlsStrategy = tlsStrategy != null ? tlsStrategy : new H2ClientTlsStrategy();
 
         final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory(
@@ -338,7 +337,7 @@ public class H2RequesterBootstrap {
         final IOEventHandlerFactory ioEventHandlerFactory = new ClientHttpProtocolNegotiatorFactory(
                 http1StreamHandlerFactory,
                 http2StreamHandlerFactory,
-                actualVersionProtocol,
+                versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
                 actualTlsStrategy,
                 handshakeTimeout);
 
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 5448c9b..884c3a6 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
@@ -401,7 +401,6 @@ public class H2ServerBootstrap {
                 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 h2StreamListener);
 
-        final HttpVersionPolicy actualVersionProtocol = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
         final TlsStrategy actualTlsStrategy = tlsStrategy != null ? tlsStrategy : new H2ServerTlsStrategy();
 
         final ServerHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ServerHttp1StreamDuplexerFactory(
@@ -419,7 +418,7 @@ public class H2ServerBootstrap {
         final IOEventHandlerFactory ioEventHandlerFactory = new ServerHttpProtocolNegotiatorFactory(
                 http1StreamHandlerFactory,
                 http2StreamHandlerFactory,
-                actualVersionProtocol,
+                versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
                 actualTlsStrategy,
                 handshakeTimeout);
 
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/H2ConnPool.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/H2ConnPool.java
index 37107ca..6eac4d0 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/H2ConnPool.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/pool/H2ConnPool.java
@@ -117,13 +117,20 @@ public final class H2ConnPool extends AbstractIOSessionPool<HttpHost> {
                             tlsStrategy.upgrade(
                                     (TransportSecurityLayer) ioSession,
                                     namedEndpoint,
-                                    ioSession.getLocalAddress(),
-                                    ioSession.getRemoteAddress(),
                                     null,
-                                    connectTimeout);
+                                    connectTimeout,
+                                    new CallbackContribution<TransportSecurityLayer>(callback) {
+
+                                        @Override
+                                        public void completed(final TransportSecurityLayer transportSecurityLayer) {
+                                            callback.completed(ioSession);
+                                        }
+
+                                    });
                             ioSession.setSocketTimeout(connectTimeout);
+                        } else {
+                            callback.completed(ioSession);
                         }
-                        callback.completed(ioSession);
                     }
 
                 });
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptClientTlsStrategy.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptClientTlsStrategy.java
index 88cf871..e6ad019 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptClientTlsStrategy.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptClientTlsStrategy.java
@@ -31,9 +31,11 @@ import java.net.SocketAddress;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.URIScheme;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
 import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
 import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
@@ -83,6 +85,27 @@ public class ConscryptClientTlsStrategy implements TlsStrategy {
     }
 
     @Override
+    public void upgrade(
+            final TransportSecurityLayer tlsSession,
+            final NamedEndpoint endpoint,
+            final Object attachment,
+            final Timeout handshakeTimeout,
+            final FutureCallback<TransportSecurityLayer> callback) {
+        tlsSession.startTls(
+                sslContext,
+                endpoint,
+                sslBufferMode,
+                ConscryptSupport.initialize(attachment, initializer),
+                ConscryptSupport.verify(verifier),
+                handshakeTimeout,
+                callback);
+    }
+
+    /**
+     * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)}
+     */
+    @Deprecated
+    @Override
     public boolean upgrade(
             final TransportSecurityLayer tlsSession,
             final HttpHost host,
@@ -92,13 +115,7 @@ public class ConscryptClientTlsStrategy implements TlsStrategy {
             final Timeout handshakeTimeout) {
         final String scheme = host != null ? host.getSchemeName() : null;
         if (URIScheme.HTTPS.same(scheme)) {
-            tlsSession.startTls(
-                    sslContext,
-                    host,
-                    sslBufferMode,
-                    ConscryptSupport.initialize(attachment, initializer),
-                    ConscryptSupport.verify(verifier),
-                    handshakeTimeout);
+            upgrade(tlsSession, host, attachment, handshakeTimeout, null);
             return true;
         }
         return false;
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptServerTlsStrategy.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptServerTlsStrategy.java
index dba69b8..2b6e9e6 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptServerTlsStrategy.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/ConscryptServerTlsStrategy.java
@@ -31,10 +31,12 @@ import java.net.SocketAddress;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.nio.ssl.FixedPortStrategy;
 import org.apache.hc.core5.http.nio.ssl.SecurePortStrategy;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
 import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
 import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
@@ -145,6 +147,27 @@ public class ConscryptServerTlsStrategy implements TlsStrategy {
     }
 
     @Override
+    public void upgrade(
+            final TransportSecurityLayer tlsSession,
+            final NamedEndpoint endpoint,
+            final Object attachment,
+            final Timeout handshakeTimeout,
+            final FutureCallback<TransportSecurityLayer> callback) {
+        tlsSession.startTls(
+                sslContext,
+                endpoint,
+                sslBufferMode,
+                ConscryptSupport.initialize(attachment, initializer),
+                ConscryptSupport.verify(verifier),
+                handshakeTimeout,
+                callback);
+    }
+
+    /**
+     * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)}
+     */
+    @Deprecated
+    @Override
     public boolean upgrade(
             final TransportSecurityLayer tlsSession,
             final HttpHost host,
@@ -153,16 +176,9 @@ public class ConscryptServerTlsStrategy implements TlsStrategy {
             final Object attachment,
             final Timeout handshakeTimeout) {
         if (isApplicable(localAddress)) {
-            tlsSession.startTls(
-                    sslContext,
-                    host,
-                    sslBufferMode,
-                    ConscryptSupport.initialize(attachment, initializer),
-                    ConscryptSupport.verify(verifier),
-                    handshakeTimeout);
+            upgrade(tlsSession, host, attachment, handshakeTimeout, null);
             return true;
         }
         return false;
     }
-
 }
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ClientTlsStrategy.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ClientTlsStrategy.java
index a003d54..a3f0d88 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ClientTlsStrategy.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ClientTlsStrategy.java
@@ -31,9 +31,11 @@ import java.net.SocketAddress;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.URIScheme;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
 import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
 import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
@@ -88,6 +90,27 @@ public class H2ClientTlsStrategy implements TlsStrategy {
     }
 
     @Override
+    public void upgrade(
+            final TransportSecurityLayer tlsSession,
+            final NamedEndpoint endpoint,
+            final Object attachment,
+            final Timeout handshakeTimeout,
+            final FutureCallback<TransportSecurityLayer> callback) {
+        tlsSession.startTls(
+                sslContext,
+                endpoint,
+                sslBufferMode,
+                H2TlsSupport.enforceRequirements(attachment, initializer),
+                verifier,
+                handshakeTimeout,
+                callback);
+    }
+
+    /**
+     * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)}
+     */
+    @Deprecated
+    @Override
     public boolean upgrade(
             final TransportSecurityLayer tlsSession,
             final HttpHost host,
@@ -97,13 +120,7 @@ public class H2ClientTlsStrategy implements TlsStrategy {
             final Timeout handshakeTimeout) {
         final String scheme = host != null ? host.getSchemeName() : null;
         if (URIScheme.HTTPS.same(scheme)) {
-            tlsSession.startTls(
-                    sslContext,
-                    host,
-                    sslBufferMode,
-                    H2TlsSupport.enforceRequirements(attachment, initializer),
-                    verifier,
-                    handshakeTimeout);
+            upgrade(tlsSession, host, attachment, handshakeTimeout, null);
             return true;
         }
         return false;
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ServerTlsStrategy.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ServerTlsStrategy.java
index f4e7f6f..e850edd 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ServerTlsStrategy.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/ssl/H2ServerTlsStrategy.java
@@ -31,10 +31,12 @@ import java.net.SocketAddress;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.nio.ssl.FixedPortStrategy;
 import org.apache.hc.core5.http.nio.ssl.SecurePortStrategy;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
 import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
 import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
@@ -150,6 +152,27 @@ public class H2ServerTlsStrategy implements TlsStrategy {
     }
 
     @Override
+    public void upgrade(
+            final TransportSecurityLayer tlsSession,
+            final NamedEndpoint endpoint,
+            final Object attachment,
+            final Timeout handshakeTimeout,
+            final FutureCallback<TransportSecurityLayer> callback) {
+        tlsSession.startTls(
+                sslContext,
+                endpoint,
+                sslBufferMode,
+                H2TlsSupport.enforceRequirements(attachment, initializer),
+                verifier,
+                handshakeTimeout,
+                callback);
+    }
+
+    /**
+     * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)}
+     */
+    @Deprecated
+    @Override
     public boolean upgrade(
             final TransportSecurityLayer tlsSession,
             final HttpHost host,
@@ -158,16 +181,9 @@ public class H2ServerTlsStrategy implements TlsStrategy {
             final Object attachment,
             final Timeout handshakeTimeout) {
         if (isApplicable(localAddress)) {
-            tlsSession.startTls(
-                    sslContext,
-                    host,
-                    sslBufferMode,
-                    H2TlsSupport.enforceRequirements(attachment, initializer),
-                    verifier,
-                    handshakeTimeout);
+            upgrade(tlsSession, host, attachment, handshakeTimeout, null);
             return true;
         }
         return false;
     }
-
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1IOEventHandlerFactory.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1IOEventHandlerFactory.java
index f33e0c8..0c2643b 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1IOEventHandlerFactory.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ClientHttp1IOEventHandlerFactory.java
@@ -29,7 +29,6 @@ package org.apache.hc.core5.http.impl.nio;
 
 import org.apache.hc.core5.annotation.Contract;
 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.nio.ssl.TlsStrategy;
 import org.apache.hc.core5.reactor.EndpointParameters;
@@ -65,14 +64,7 @@ public class ClientHttp1IOEventHandlerFactory implements IOEventHandlerFactory {
         if (attachment instanceof EndpointParameters) {
             final EndpointParameters params = (EndpointParameters) attachment;
             if (tlsStrategy != null && URIScheme.HTTPS.same(params.getScheme())) {
-                final HttpHost host = new HttpHost(params.getScheme(), params.getHostName(), params.getPort());
-                tlsStrategy.upgrade(
-                        ioSession,
-                        host,
-                        ioSession.getLocalAddress(),
-                        ioSession.getRemoteAddress(),
-                        params.getAttachment(),
-                        handshakeTimeout);
+                tlsStrategy.upgrade(ioSession, params, params.getAttachment(), handshakeTimeout, null);
             }
         }
         return new ClientHttp1IOEventHandler(streamDuplexerFactory.create(ioSession));
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1IOEventHandlerFactory.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1IOEventHandlerFactory.java
index ef3a32f..1ec0016 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1IOEventHandlerFactory.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1IOEventHandlerFactory.java
@@ -68,22 +68,18 @@ public class ServerHttp1IOEventHandlerFactory implements IOEventHandlerFactory {
             if (tlsStrategy != null && URIScheme.HTTPS.same(endpointScheme)) {
                 tlsStrategy.upgrade(
                         ioSession,
-                        null,
-                        ioSession.getLocalAddress(),
-                        ioSession.getRemoteAddress(),
+                        params,
                         params.getAttachment(),
-                        handshakeTimeout);
+                        handshakeTimeout,
+                        null);
             }
         } else {
-            if (tlsStrategy != null) {
-                tlsStrategy.upgrade(
-                        ioSession,
-                        null,
-                        ioSession.getLocalAddress(),
-                        ioSession.getRemoteAddress(),
-                        attachment,
-                        handshakeTimeout);
-            }
+            tlsStrategy.upgrade(
+                    ioSession,
+                    null,
+                    attachment,
+                    handshakeTimeout,
+                    null);
         }
         return new ServerHttp1IOEventHandler(streamDuplexerFactory.create(endpointScheme, ioSession));
     }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/BasicClientTlsStrategy.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/BasicClientTlsStrategy.java
index 2ccc9c2..3386708 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/BasicClientTlsStrategy.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/BasicClientTlsStrategy.java
@@ -31,8 +31,10 @@ import java.net.SocketAddress;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
 import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
 import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
@@ -87,6 +89,21 @@ public class BasicClientTlsStrategy implements TlsStrategy {
     }
 
     @Override
+    public void upgrade(
+            final TransportSecurityLayer tlsSession,
+            final NamedEndpoint endpoint,
+            final Object attachment,
+            final Timeout handshakeTimeout,
+            final FutureCallback<TransportSecurityLayer> callback) {
+        tlsSession.startTls(sslContext, endpoint, sslBufferMode,
+                TlsSupport.enforceStrongSecurity(initializer), verifier, handshakeTimeout, callback);
+    }
+
+    /**
+     * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)}
+     */
+    @Deprecated
+    @Override
     public boolean upgrade(
             final TransportSecurityLayer tlsSession,
             final HttpHost host,
@@ -96,8 +113,7 @@ public class BasicClientTlsStrategy implements TlsStrategy {
             final Timeout handshakeTimeout) {
         final String scheme = host != null ? host.getSchemeName() : null;
         if (URIScheme.HTTPS.same(scheme)) {
-            tlsSession.startTls(sslContext, host, sslBufferMode,
-                    TlsSupport.enforceStrongSecurity(initializer), verifier, handshakeTimeout);
+            upgrade(tlsSession, host, attachment, handshakeTimeout, null);
             return true;
         }
         return false;
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/BasicServerTlsStrategy.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/BasicServerTlsStrategy.java
index 06cd6b4..63c0025 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/BasicServerTlsStrategy.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/BasicServerTlsStrategy.java
@@ -31,7 +31,9 @@ import java.net.SocketAddress;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.reactor.ssl.SSLBufferMode;
 import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
 import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
@@ -149,6 +151,21 @@ public class BasicServerTlsStrategy implements TlsStrategy {
     }
 
     @Override
+    public void upgrade(
+            final TransportSecurityLayer tlsSession,
+            final NamedEndpoint endpoint,
+            final Object attachment,
+            final Timeout handshakeTimeout,
+            final FutureCallback<TransportSecurityLayer> callback) {
+        tlsSession.startTls(sslContext, endpoint, sslBufferMode,
+                TlsSupport.enforceStrongSecurity(initializer), verifier, handshakeTimeout, callback);
+    }
+
+    /**
+     * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)}
+     */
+    @Deprecated
+    @Override
     public boolean upgrade(
             final TransportSecurityLayer tlsSession,
             final HttpHost host,
@@ -157,8 +174,7 @@ public class BasicServerTlsStrategy implements TlsStrategy {
             final Object attachment,
             final Timeout handshakeTimeout) {
         if (isApplicable(localAddress)) {
-            tlsSession.startTls(sslContext, host, sslBufferMode,
-                    TlsSupport.enforceStrongSecurity(initializer), verifier, handshakeTimeout);
+            upgrade(tlsSession, host, attachment, handshakeTimeout, null);
             return true;
         }
         return false;
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/TlsStrategy.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/TlsStrategy.java
index 5ebee69..a5f09c4 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/TlsStrategy.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/ssl/TlsStrategy.java
@@ -29,7 +29,10 @@ package org.apache.hc.core5.http.nio.ssl;
 
 import java.net.SocketAddress;
 
+import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer;
 import org.apache.hc.core5.util.Timeout;
 
@@ -41,7 +44,7 @@ import org.apache.hc.core5.util.Timeout;
 public interface TlsStrategy {
 
     /**
-     * Secures current session layer with TLS security.
+     * Secures current session layer with TLS.
      *
      * @param sessionLayer the session layer
      * @param host the name of the opposite endpoint when given or {@code null} otherwise.
@@ -50,7 +53,10 @@ public interface TlsStrategy {
      * @param attachment arbitrary object passes to the TLS session initialization code.
      * @param handshakeTimeout the timeout to use while performing the TLS handshake; may be {@code null}.
      * @return {@code true} if the session has been upgraded, {@code false} otherwise.
+     *
+     * @deprecated use {@link #upgrade(TransportSecurityLayer, NamedEndpoint, Object, Timeout, FutureCallback)}
      */
+    @Deprecated
     boolean upgrade(
             TransportSecurityLayer sessionLayer,
             HttpHost host,
@@ -59,4 +65,28 @@ public interface TlsStrategy {
             Object attachment,
             Timeout handshakeTimeout);
 
+    /**
+     * Secures current session layer with TLS.
+     *
+     * @param sessionLayer the session layer
+     * @param endpoint the name of the opposite endpoint when applicable or {@code null} otherwise.
+     * @param attachment arbitrary object passes to the TLS session initialization code.
+     * @param handshakeTimeout the timeout to use while performing the TLS handshake; may be {@code null}.
+     * @param callback Operation result callback.
+     *
+     * @since 5.2
+     */
+    default void upgrade(
+            TransportSecurityLayer sessionLayer,
+            NamedEndpoint endpoint,
+            Object attachment,
+            Timeout handshakeTimeout,
+            FutureCallback<TransportSecurityLayer> callback) {
+        upgrade(sessionLayer, new HttpHost(URIScheme.HTTPS.id, endpoint.getHostName(), endpoint.getPort()),
+                null, null, attachment, handshakeTimeout);
+        if (callback != null) {
+            callback.completed(sessionLayer);
+        }
+    }
+
 }
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 33ed80d..e292a69 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
@@ -39,6 +39,7 @@ import java.util.concurrent.locks.Lock;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.function.Decorator;
 import org.apache.hc.core5.io.CloseMode;
 import org.apache.hc.core5.net.NamedEndpoint;
@@ -48,6 +49,7 @@ import org.apache.hc.core5.reactor.ssl.SSLMode;
 import org.apache.hc.core5.reactor.ssl.SSLSessionInitializer;
 import org.apache.hc.core5.reactor.ssl.SSLSessionVerifier;
 import org.apache.hc.core5.reactor.ssl.TlsDetails;
+import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer;
 import org.apache.hc.core5.util.Asserts;
 import org.apache.hc.core5.util.Timeout;
 
@@ -60,6 +62,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<FutureCallback<TransportSecurityLayer>> tlsHandshakeCallbackRef;
     private final AtomicBoolean closed;
 
     InternalDataChannel(
@@ -76,6 +79,7 @@ final class InternalDataChannel extends InternalChannel implements ProtocolIOSes
         this.tlsSessionRef = new AtomicReference<>(null);
         this.currentSessionRef = new AtomicReference<>(
                 ioSessionDecorator != null ? ioSessionDecorator.decorate(ioSession) : ioSession);
+        this.tlsHandshakeCallbackRef = new AtomicReference<>(null);
         this.closed = new AtomicBoolean(false);
     }
 
@@ -167,6 +171,10 @@ final class InternalDataChannel extends InternalChannel implements ProtocolIOSes
         if (handler != null) {
             handler.exception(currentSession, cause);
         }
+        final FutureCallback<?> callback = tlsHandshakeCallbackRef.getAndSet(null);
+        if (callback != null) {
+            callback.failed(cause);
+        }
     }
 
     void onTLSSessionStart(final SSLIOSession sslSession) {
@@ -174,6 +182,10 @@ final class InternalDataChannel extends InternalChannel implements ProtocolIOSes
         if (sessionListener != null) {
             sessionListener.connected(currentSession);
         }
+        final FutureCallback<TransportSecurityLayer> callback = tlsHandshakeCallbackRef.getAndSet(null);
+        if (callback != null) {
+            callback.completed(this);
+        }
     }
 
     void onTLSSessionEnd(final SSLIOSession sslSession) {
@@ -201,6 +213,18 @@ final class InternalDataChannel extends InternalChannel implements ProtocolIOSes
             final SSLSessionInitializer initializer,
             final SSLSessionVerifier verifier,
             final Timeout handshakeTimeout) {
+        startTls(sslContext, endpoint, sslBufferMode, initializer, verifier, handshakeTimeout, null);
+    }
+
+    @Override
+    public void startTls(
+            final SSLContext sslContext,
+            final NamedEndpoint endpoint,
+            final SSLBufferMode sslBufferMode,
+            final SSLSessionInitializer initializer,
+            final SSLSessionVerifier verifier,
+            final Timeout handshakeTimeout,
+            final FutureCallback<TransportSecurityLayer> callback) {
         final SSLIOSession sslioSession = new SSLIOSession(
                 endpoint != null ? endpoint : initialEndpoint,
                 ioSession,
@@ -214,11 +238,17 @@ final class InternalDataChannel extends InternalChannel implements ProtocolIOSes
                 handshakeTimeout);
         if (tlsSessionRef.compareAndSet(null, sslioSession)) {
             currentSessionRef.set(ioSessionDecorator != null ? ioSessionDecorator.decorate(sslioSession) : sslioSession);
+            tlsHandshakeCallbackRef.set(callback);
+        } else {
+            throw new IllegalStateException("TLS already activated");
+        }
+        try {
             if (sessionListener != null) {
                 sessionListener.startTls(sslioSession);
             }
-        } else {
-            throw new IllegalStateException("TLS already activated");
+            sslioSession.beginHandshake(this);
+        } catch (final Exception ex) {
+            onException(ex);
         }
     }
 
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java b/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java
index 90d67a6..4b90d93 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/SSLIOSession.java
@@ -157,16 +157,11 @@ public class SSLIOSession implements IOSession {
 
             @Override
             public void connected(final IOSession protocolSession) throws IOException {
-                if (handshakeStateRef.compareAndSet(TLSHandShakeState.READY, TLSHandShakeState.INITIALIZED)) {
-                    initialize(protocolSession);
-                }
+                beginHandshake(protocolSession);
             }
 
             @Override
             public void inputReady(final IOSession protocolSession, final ByteBuffer src) throws IOException {
-                if (handshakeStateRef.compareAndSet(TLSHandShakeState.READY, TLSHandShakeState.INITIALIZED)) {
-                    initialize(protocolSession);
-                }
                 receiveEncryptedData();
                 doHandshake(protocolSession);
                 decryptData(protocolSession);
@@ -175,9 +170,6 @@ public class SSLIOSession implements IOSession {
 
             @Override
             public void outputReady(final IOSession protocolSession) throws IOException {
-                if (handshakeStateRef.compareAndSet(TLSHandShakeState.READY, TLSHandShakeState.INITIALIZED)) {
-                    initialize(protocolSession);
-                }
                 encryptData(protocolSession);
                 sendEncryptedData();
                 doHandshake(protocolSession);
@@ -228,6 +220,12 @@ public class SSLIOSession implements IOSession {
         return internalEventHandler;
     }
 
+    public void beginHandshake(final IOSession protocolSession) throws IOException {
+        if (handshakeStateRef.compareAndSet(TLSHandShakeState.READY, TLSHandShakeState.INITIALIZED)) {
+            initialize(protocolSession);
+        }
+    }
+
     private void initialize(final IOSession protocolSession) throws IOException {
         // Save the initial socketTimeout of the underlying IOSession, to be restored after the handshake is finished
         this.socketTimeout = this.session.getSocketTimeout();
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/TransportSecurityLayer.java b/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/TransportSecurityLayer.java
index 42de768..4c48aea 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/TransportSecurityLayer.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/reactor/ssl/TransportSecurityLayer.java
@@ -29,6 +29,7 @@ package org.apache.hc.core5.reactor.ssl;
 
 import javax.net.ssl.SSLContext;
 
+import org.apache.hc.core5.concurrent.FutureCallback;
 import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.util.Timeout;
 
@@ -60,6 +61,34 @@ public interface TransportSecurityLayer {
             Timeout handshakeTimeout) throws UnsupportedOperationException;
 
     /**
+     * Starts TLS session over an existing network connection with the given SSL context.
+     * {@link NamedEndpoint} details are applicable for client side connections and
+     * are used for host name verification, when supported by the SSL engine.
+     *
+     * @param sslContext SSL context to be used for this session.
+     * @param endpoint optional endpoint details for outgoing client side connections.
+     * @param sslBufferMode SSL buffer management mode.
+     * @param initializer SSL session initialization callback.
+     * @param verifier SSL session verification callback.
+     * @param handshakeTimeout the timeout to use while performing the TLS handshake; may be {@code null}.
+     *
+     * @since 5.2
+     */
+    default void startTls(
+            SSLContext sslContext,
+            NamedEndpoint endpoint,
+            SSLBufferMode sslBufferMode,
+            SSLSessionInitializer initializer,
+            SSLSessionVerifier verifier,
+            Timeout handshakeTimeout,
+            FutureCallback<TransportSecurityLayer> callback) throws UnsupportedOperationException {
+        startTls(sslContext, endpoint, sslBufferMode, initializer, verifier, handshakeTimeout);
+        if (callback != null) {
+            callback.completed(null);
+        }
+    }
+
+    /**
      * Returns details of a fully established TLS session.
      *
      * @return TLS session details.