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/10/01 13:21:00 UTC

[httpcomponents-client] 01/01: HTTPCLIENT-2135: TLS configuration on a per-host basis

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

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

commit 4a316fdaa00150128f6e1abb5f5ff1327683de81
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Fri Oct 1 15:20:11 2021 +0200

    HTTPCLIENT-2135: TLS configuration on a per-host basis
---
 .../hc/client5/http/config/ConnectionConfig.java   |  42 +-----
 .../apache/hc/client5/http/config/TlsConfig.java   | 152 +++++++++++++++++++++
 .../impl/async/InternalHttpAsyncExecRuntime.java   |   3 +-
 .../http/impl/async/MinimalHttpAsyncClient.java    |   3 +-
 .../impl/io/BasicHttpClientConnectionManager.java  |  23 +++-
 .../io/DefaultHttpClientConnectionOperator.java    |  10 +-
 .../io/PoolingHttpClientConnectionManager.java     |  51 +++++--
 .../nio/DefaultAsyncClientConnectionOperator.java  |  22 +--
 .../nio/PoolingAsyncClientConnectionManager.java   |  43 +++++-
 .../http/io/HttpClientConnectionOperator.java      |   8 +-
 .../http/nio/AsyncClientConnectionOperator.java    |  28 ----
 .../http/socket/ConnectionSocketFactory.java       |   4 +-
 .../socket/LayeredConnectionSocketFactory.java     |   5 +-
 .../http/ssl/AbstractClientTlsStrategy.java        |   5 +-
 .../http/ssl/SSLConnectionSocketFactory.java       |  15 +-
 .../io/TestBasicHttpClientConnectionManager.java   |  19 ++-
 .../impl/io/TestHttpClientConnectionOperator.java  |  19 ++-
 .../io/TestPoolingHttpClientConnectionManager.java |  19 ++-
 18 files changed, 315 insertions(+), 156 deletions(-)

diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/config/ConnectionConfig.java b/httpclient5/src/main/java/org/apache/hc/client5/http/config/ConnectionConfig.java
index 34111f1..38ead6f 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/config/ConnectionConfig.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/config/ConnectionConfig.java
@@ -48,25 +48,22 @@ public class ConnectionConfig implements Cloneable {
 
     private final Timeout connectTimeout;
     private final Timeout socketTimeout;
-    private final Timeout handshakeTimeout;
     private final TimeValue validateAfterInactivity;
 
     /**
      * Intended for CDI compatibility
      */
     protected ConnectionConfig() {
-        this(DEFAULT_CONNECT_TIMEOUT, null, null, null);
+        this(DEFAULT_CONNECT_TIMEOUT, null, null);
     }
 
     ConnectionConfig(
             final Timeout connectTimeout,
             final Timeout socketTimeout,
-            final Timeout handshakeTimeout,
             final TimeValue validateAfterInactivity) {
         super();
         this.connectTimeout = connectTimeout;
         this.socketTimeout = socketTimeout;
-        this.handshakeTimeout = handshakeTimeout;
         this.validateAfterInactivity = validateAfterInactivity;
     }
 
@@ -85,13 +82,6 @@ public class ConnectionConfig implements Cloneable {
     }
 
     /**
-     * @see Builder#setHandshakeTimeout(Timeout)
-     */
-    public Timeout getHandshakeTimeout() {
-        return handshakeTimeout;
-    }
-
-    /**
      * @see Builder#setValidateAfterInactivity(TimeValue)
      */
     public TimeValue getValidateAfterInactivity() {
@@ -109,7 +99,6 @@ public class ConnectionConfig implements Cloneable {
         builder.append("[");
         builder.append(", connectTimeout=").append(connectTimeout);
         builder.append(", socketTimeout=").append(socketTimeout);
-        builder.append(", handshakeTimeout=").append(handshakeTimeout);
         builder.append(", validateAfterInactivity=").append(validateAfterInactivity);
         builder.append("]");
         return builder.toString();
@@ -123,7 +112,6 @@ public class ConnectionConfig implements Cloneable {
         return new Builder()
                 .setConnectTimeout(config.getConnectTimeout())
                 .setSocketTimeout(config.getSocketTimeout())
-                .setHandshakeTimeout(config.getHandshakeTimeout())
                 .setValidateAfterInactivity(config.getValidateAfterInactivity());
     }
 
@@ -131,7 +119,6 @@ public class ConnectionConfig implements Cloneable {
 
         private Timeout socketTimeout;
         private Timeout connectTimeout;
-        private Timeout handshakeTimeout;
         private TimeValue validateAfterInactivity;
 
         Builder() {
@@ -162,9 +149,6 @@ public class ConnectionConfig implements Cloneable {
 
         /**
          * Determines the timeout until a new connection is fully established.
-         * This may also include transport security negotiation exchanges
-         * such as {@code SSL} or {@code TLS} protocol negotiation unless
-         * a different timeout value set with {@link #setHandshakeTimeout(Timeout)}.
          * <p>
          * A timeout value of zero is interpreted as an infinite timeout.
          * </p>
@@ -186,29 +170,6 @@ public class ConnectionConfig implements Cloneable {
         }
 
         /**
-         * Determines the timeout used by transport security negotiation exchanges
-         * such as {@code SSL} or {@code TLS} protocol negotiation).
-         * <p>
-         * A timeout value of zero is interpreted as an infinite timeout.
-         * </p>
-         * <p>
-         * Default: {@code null} (undefined)
-         * </p>
-         */
-        public Builder setHandshakeTimeout(final Timeout handshakeTimeout) {
-            this.handshakeTimeout = handshakeTimeout;
-            return this;
-        }
-
-        /**
-         * @see #setHandshakeTimeout(Timeout)
-         */
-        public Builder setHandshakeTimeout(final long handshakeTimeout, final TimeUnit timeUnit) {
-            this.handshakeTimeout = Timeout.of(handshakeTimeout, timeUnit);
-            return this;
-        }
-
-        /**
          * Defines period of inactivity after which persistent connections must
          * be re-validated prior to being leased to the consumer. Negative values passed
          * to this method disable connection validation.
@@ -233,7 +194,6 @@ public class ConnectionConfig implements Cloneable {
             return new ConnectionConfig(
                     connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT,
                     socketTimeout,
-                    handshakeTimeout,
                     validateAfterInactivity);
         }
 
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/config/TlsConfig.java b/httpclient5/src/main/java/org/apache/hc/client5/http/config/TlsConfig.java
new file mode 100644
index 0000000..c0fed65
--- /dev/null
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/config/TlsConfig.java
@@ -0,0 +1,152 @@
+/*
+ * ====================================================================
+ * 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.client5.http.config;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.util.Timeout;
+
+/**
+ * Immutable class encapsulating TLS protocol settings.
+ *
+ * @since 5.2
+ */
+@Contract(threading = ThreadingBehavior.IMMUTABLE)
+public class TlsConfig implements Cloneable {
+
+    public static final TlsConfig DEFAULT = new Builder().build();
+
+    private final Timeout handshakeTimeout;
+    private final HttpVersionPolicy httpVersionPolicy;
+
+    /**
+     * Intended for CDI compatibility
+     */
+    protected TlsConfig() {
+        this(null, null);
+    }
+
+    TlsConfig(
+            final Timeout handshakeTimeout,
+            final HttpVersionPolicy httpVersionPolicy) {
+        super();
+        this.handshakeTimeout = handshakeTimeout;
+        this.httpVersionPolicy = httpVersionPolicy;
+    }
+
+    /**
+     * @see Builder#setHandshakeTimeout(Timeout)
+     */
+    public Timeout getHandshakeTimeout() {
+        return handshakeTimeout;
+    }
+
+    /**
+     * @see Builder#setVersionPolicy(HttpVersionPolicy)
+     */
+    public HttpVersionPolicy getHttpVersionPolicy() {
+        return httpVersionPolicy;
+    }
+
+    @Override
+    protected TlsConfig clone() throws CloneNotSupportedException {
+        return (TlsConfig) super.clone();
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("[");
+        builder.append("handshakeTimeout=").append(handshakeTimeout);
+        builder.append(", httpVersionPolicy=").append(httpVersionPolicy);
+        builder.append("]");
+        return builder.toString();
+    }
+
+    public static TlsConfig.Builder custom() {
+        return new Builder();
+    }
+
+    public static TlsConfig.Builder copy(final TlsConfig config) {
+        return new Builder()
+                .setHandshakeTimeout(config.getHandshakeTimeout())
+                .setVersionPolicy(config.getHttpVersionPolicy());
+    }
+
+    public static class Builder {
+
+        private Timeout handshakeTimeout;
+        private HttpVersionPolicy versionPolicy;
+
+        /**
+         * Determines the timeout used by TLS session negotiation exchanges (session handshake).
+         * <p>
+         * A timeout value of zero is interpreted as an infinite timeout.
+         * </p>
+         * <p>
+         * Default: {@code null} (undefined)
+         * </p>
+         */
+        public Builder setHandshakeTimeout(final Timeout handshakeTimeout) {
+            this.handshakeTimeout = handshakeTimeout;
+            return this;
+        }
+
+        /**
+         * @see #setHandshakeTimeout(Timeout)
+         */
+        public Builder setHandshakeTimeout(final long handshakeTimeout, final TimeUnit timeUnit) {
+            this.handshakeTimeout = Timeout.of(handshakeTimeout, timeUnit);
+            return this;
+        }
+
+        /**
+         * Determines the HTTP protocol policy. By default, connections are expected to use TLS ALPN
+         * extension to negotiate the application protocol to be used by both endpoints.
+         * </p>
+         * <p>
+         * Default: {@link HttpVersionPolicy#NEGOTIATE}
+         * </p>
+         */
+        public Builder setVersionPolicy(final HttpVersionPolicy versionPolicy) {
+            this.versionPolicy = versionPolicy;
+            return this;
+        }
+
+        public TlsConfig build() {
+            return new TlsConfig(
+                    handshakeTimeout,
+                    versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE);
+        }
+
+    }
+
+}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java
index bbc34fa..59e6a9d 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncExecRuntime.java
@@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicReference;
 import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.async.AsyncExecRuntime;
 import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.impl.ConnPoolSupport;
 import org.apache.hc.client5.http.impl.Operations;
 import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
@@ -213,7 +214,7 @@ class InternalHttpAsyncExecRuntime implements AsyncExecRuntime {
                 endpoint,
                 connectionInitiator,
                 connectTimeout,
-                versionPolicy,
+                versionPolicy != null ? TlsConfig.custom().setVersionPolicy(versionPolicy).build() : null,
                 context,
                 new CallbackContribution<AsyncConnectionEndpoint>(callback) {
 
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/MinimalHttpAsyncClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/MinimalHttpAsyncClient.java
index 6a7bb4e..2406cd1 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/MinimalHttpAsyncClient.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/MinimalHttpAsyncClient.java
@@ -39,6 +39,7 @@ import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.config.Configurable;
 import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.impl.ConnPoolSupport;
 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
 import org.apache.hc.client5.http.impl.ExecSupport;
@@ -153,7 +154,7 @@ public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClient
                                     connectionEndpoint,
                                     getConnectionInitiator(),
                                     connectTimeout,
-                                    versionPolicy,
+                                    versionPolicy != null ? TlsConfig.custom().setVersionPolicy(versionPolicy).build() : null,
                                     clientContext,
                                     new FutureCallback<AsyncConnectionEndpoint>() {
 
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java
index bc702b8..a67b3ad 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java
@@ -39,6 +39,7 @@ import org.apache.hc.client5.http.DnsResolver;
 import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.impl.ConnPoolSupport;
 import org.apache.hc.client5.http.impl.ConnectionShutdownException;
 import org.apache.hc.client5.http.io.ConnectionEndpoint;
@@ -109,6 +110,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
     private boolean leased;
     private SocketConfig socketConfig;
     private ConnectionConfig connectionConfig;
+    private TlsConfig tlsConfig;
 
     private final AtomicBoolean closed;
 
@@ -142,6 +144,8 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
         this.id = String.format("ep-%010d", COUNT.getAndIncrement());
         this.expiry = Long.MAX_VALUE;
         this.socketConfig = SocketConfig.DEFAULT;
+        this.connectionConfig = ConnectionConfig.DEFAULT;
+        this.tlsConfig = TlsConfig.DEFAULT;
         this.closed = new AtomicBoolean(false);
         this.validateAfterInactivity = TimeValue.ofSeconds(2L);
     }
@@ -203,6 +207,13 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
         this.connectionConfig = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
     }
 
+    /**
+     * @since 5.2
+     */
+    public synchronized void setTlsConfig(final TlsConfig tlsConfig) {
+        this.tlsConfig = tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT;
+    }
+
     public LeaseRequest lease(final String id, final HttpRoute route, final Object state) {
         return lease(id, route, Timeout.DISABLED, state);
     }
@@ -358,9 +369,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
         } else {
             host = route.getTargetHost();
         }
-        final ConnectionConfig config = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
-        final Timeout connectTimeout = timeout != null ? Timeout.of(timeout.getDuration(), timeout.getTimeUnit()) : config.getConnectTimeout();
-        final Timeout handshakeTimeout = config.getHandshakeTimeout();
+        final Timeout connectTimeout = timeout != null ? Timeout.of(timeout.getDuration(), timeout.getTimeUnit()) : connectionConfig.getConnectTimeout();
         final ManagedHttpClientConnection connection = internalEndpoint.getConnection();
         if (LOG.isDebugEnabled()) {
             LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), host, connectTimeout);
@@ -370,13 +379,13 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
                 host,
                 route.getLocalSocketAddress(),
                 connectTimeout,
-                handshakeTimeout,
-                this.socketConfig,
+                socketConfig,
+                tlsConfig,
                 context);
         if (LOG.isDebugEnabled()) {
             LOG.debug("{} connected {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(conn));
         }
-        final Timeout socketTimeout = config.getSocketTimeout();
+        final Timeout socketTimeout = connectionConfig.getSocketTimeout();
         if (socketTimeout != null) {
             connection.setSocketTimeout(socketTimeout);
         }
@@ -393,7 +402,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
         this.connectionOperator.upgrade(
                 internalEndpoint.getConnection(),
                 internalEndpoint.getRoute().getTargetHost(),
-                config.getHandshakeTimeout(),
+                tlsConfig,
                 context);
     }
 
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java
index c33f230..8f7eef8 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/DefaultHttpClientConnectionOperator.java
@@ -109,7 +109,7 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
             final SocketConfig socketConfig,
             final HttpContext context) throws IOException {
         final Timeout timeout = connectTimeout != null ? Timeout.of(connectTimeout.getDuration(), connectTimeout.getTimeUnit()) : null;
-        connect(conn, host, localAddress, timeout, timeout, socketConfig, context);
+        connect(conn, host, localAddress, timeout, socketConfig, null, context);
     }
 
     @Override
@@ -118,8 +118,8 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
             final HttpHost host,
             final InetSocketAddress localAddress,
             final Timeout connectTimeout,
-            final Timeout handshakeTimeout,
             final SocketConfig socketConfig,
+            final Object attachment,
             final HttpContext context) throws IOException {
         Args.notNull(conn, "Connection");
         Args.notNull(host, "Host");
@@ -178,7 +178,7 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
                         host.getHostName(), host.getPort(), localAddress, remoteAddress, connectTimeout);
             }
             try {
-                sock = sf.connectSocket(sock, host, remoteAddress, localAddress, connectTimeout, handshakeTimeout, context);
+                sock = sf.connectSocket(sock, host, remoteAddress, localAddress, connectTimeout, attachment, context);
                 conn.bind(sock);
                 conn.setSocketTimeout(soTimeout);
                 if (LOG.isDebugEnabled()) {
@@ -215,7 +215,7 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
     public void upgrade(
             final ManagedHttpClientConnection conn,
             final HttpHost host,
-            final Timeout handshakeTimeout,
+            final Object attachment,
             final HttpContext context) throws IOException {
         final HttpClientContext clientContext = HttpClientContext.adapt(context);
         final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(clientContext);
@@ -234,7 +234,7 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
             throw new ConnectionClosedException("Connection is closed");
         }
         final int port = this.schemePortResolver.resolve(host);
-        sock = lsf.createLayeredSocket(sock, host.getHostName(), port, handshakeTimeout, context);
+        sock = lsf.createLayeredSocket(sock, host.getHostName(), port, attachment, context);
         conn.bind(sock);
     }
 
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
index 45fe784..8c415fc 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java
@@ -39,6 +39,7 @@ import org.apache.hc.client5.http.DnsResolver;
 import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.impl.ConnPoolSupport;
 import org.apache.hc.client5.http.impl.ConnectionShutdownException;
 import org.apache.hc.client5.http.io.ConnectionEndpoint;
@@ -115,6 +116,7 @@ public class PoolingHttpClientConnectionManager
 
     private volatile Resolver<HttpRoute, SocketConfig> socketConfigResolver;
     private volatile Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
+    private volatile Resolver<HttpHost, TlsConfig> tlsConfigResolver;
 
     public PoolingHttpClientConnectionManager() {
         this(RegistryBuilder.<ConnectionSocketFactory>create()
@@ -241,16 +243,22 @@ public class PoolingHttpClientConnectionManager
         throw new IllegalStateException("Unexpected endpoint class: " + endpoint.getClass());
     }
 
+    private SocketConfig resolveSocketConfig(final HttpRoute route) {
+        final Resolver<HttpRoute, SocketConfig> resolver = this.socketConfigResolver;
+        final SocketConfig socketConfig = resolver != null ? resolver.resolve(route) : null;
+        return socketConfig != null ? socketConfig : SocketConfig.DEFAULT;
+    }
+
     private ConnectionConfig resolveConnectionConfig(final HttpRoute route) {
         final Resolver<HttpRoute, ConnectionConfig> resolver = this.connectionConfigResolver;
         final ConnectionConfig connectionConfig = resolver != null ? resolver.resolve(route) : null;
         return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
     }
 
-    private SocketConfig resolveSocketConfig(final HttpRoute route) {
-        final Resolver<HttpRoute, SocketConfig> resolver = this.socketConfigResolver;
-        final SocketConfig socketConfig = resolver != null ? resolver.resolve(route) : null;
-        return socketConfig != null ? socketConfig : SocketConfig.DEFAULT;
+    private TlsConfig resolveTlsConfig(final HttpHost host) {
+        final Resolver<HttpHost, TlsConfig> resolver = this.tlsConfigResolver;
+        final TlsConfig tlsConfig = resolver != null ? resolver.resolve(host) : null;
+        return tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT;
     }
 
     private TimeValue resolveValidateAfterInactivity(final ConnectionConfig connectionConfig) {
@@ -401,16 +409,11 @@ public class PoolingHttpClientConnectionManager
             poolEntry.assignConnection(connFactory.createConnection(null));
         }
         final HttpRoute route = poolEntry.getRoute();
-        final HttpHost host;
-        if (route.getProxyHost() != null) {
-            host = route.getProxyHost();
-        } else {
-            host = route.getTargetHost();
-        }
+        final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost();
         final SocketConfig socketConfig = resolveSocketConfig(route);
         final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
+        final TlsConfig tlsConfig = resolveTlsConfig(host);
         final Timeout connectTimeout = timeout != null ? Timeout.of(timeout.getDuration(), timeout.getTimeUnit()) : connectionConfig.getConnectTimeout();
-        final Timeout handshakeTimeout = connectionConfig.getHandshakeTimeout();
         if (LOG.isDebugEnabled()) {
             LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), host, connectTimeout);
         }
@@ -420,8 +423,8 @@ public class PoolingHttpClientConnectionManager
                 host,
                 route.getLocalSocketAddress(),
                 connectTimeout,
-                handshakeTimeout,
                 socketConfig,
+                tlsConfig,
                 context);
         if (LOG.isDebugEnabled()) {
             LOG.debug("{} connected {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(conn));
@@ -438,9 +441,9 @@ public class PoolingHttpClientConnectionManager
         final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
         final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = internalEndpoint.getValidatedPoolEntry();
         final HttpRoute route = poolEntry.getRoute();
-        final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
-        final Timeout handshakeTimeout = connectionConfig.getHandshakeTimeout();
-        this.connectionOperator.upgrade(poolEntry.getConnection(), route.getTargetHost(), handshakeTimeout, context);
+        final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost();
+        final TlsConfig tlsConfig = resolveTlsConfig(host);
+        this.connectionOperator.upgrade(poolEntry.getConnection(), route.getTargetHost(), tlsConfig, context);
     }
 
     @Override
@@ -538,6 +541,24 @@ public class PoolingHttpClientConnectionManager
     }
 
     /**
+     * Sets the same {@link ConnectionConfig} for all hosts
+     *
+     * @since 5.2
+     */
+    public void setDefaultTlsConfig(final TlsConfig config) {
+        this.tlsConfigResolver = (host) -> config;
+    }
+
+    /**
+     * Sets {@link Resolver} of {@link TlsConfig} on a per host basis.
+     *
+     * @since 5.2
+     */
+    public void setTlsConfigResolver(final Resolver<HttpHost, TlsConfig> tlsConfigResolver) {
+        this.tlsConfigResolver = tlsConfigResolver;
+    }
+
+    /**
      * @deprecated Use custom {@link #setConnectionConfigResolver(Resolver)}
      */
     @Deprecated
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/DefaultAsyncClientConnectionOperator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/DefaultAsyncClientConnectionOperator.java
index f9dcb35..130eb6b 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/DefaultAsyncClientConnectionOperator.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/DefaultAsyncClientConnectionOperator.java
@@ -34,6 +34,7 @@ import java.util.concurrent.Future;
 
 import org.apache.hc.client5.http.DnsResolver;
 import org.apache.hc.client5.http.SchemePortResolver;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
 import org.apache.hc.client5.http.nio.AsyncClientConnectionOperator;
 import org.apache.hc.client5.http.nio.ManagedAsyncClientConnection;
@@ -73,7 +74,6 @@ final class DefaultAsyncClientConnectionOperator implements AsyncClientConnectio
             final HttpHost host,
             final SocketAddress localAddress,
             final Timeout connectTimeout,
-            final Timeout handshakeTimeout,
             final Object attachment,
             final FutureCallback<ManagedAsyncClientConnection> callback) {
         Args.notNull(connectionInitiator, "Connection initiator");
@@ -82,13 +82,14 @@ final class DefaultAsyncClientConnectionOperator implements AsyncClientConnectio
         final HttpHost remoteEndpoint = RoutingSupport.normalize(host, schemePortResolver);
         final InetAddress remoteAddress = host.getAddress();
         final TlsStrategy tlsStrategy = tlsStrategyLookup != null ? tlsStrategyLookup.lookup(host.getSchemeName()) : null;
+        final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT;
         final Future<IOSession> sessionFuture = sessionRequester.connect(
                 connectionInitiator,
                 remoteEndpoint,
                 remoteAddress != null ? new InetSocketAddress(remoteAddress, remoteEndpoint.getPort()) : null,
                 localAddress,
                 connectTimeout,
-                attachment,
+                tlsConfig.getHttpVersionPolicy(),
                 new FutureCallback<IOSession>() {
 
                     @Override
@@ -97,6 +98,7 @@ final class DefaultAsyncClientConnectionOperator implements AsyncClientConnectio
                         if (tlsStrategy != null && URIScheme.HTTPS.same(host.getSchemeName())) {
                             try {
                                 final Timeout socketTimeout = connection.getSocketTimeout();
+                                final Timeout handshakeTimeout = tlsConfig.getHandshakeTimeout();
                                 tlsStrategy.upgrade(
                                         connection,
                                         host,
@@ -135,29 +137,17 @@ final class DefaultAsyncClientConnectionOperator implements AsyncClientConnectio
     }
 
     @Override
-    public Future<ManagedAsyncClientConnection> connect(
-            final ConnectionInitiator connectionInitiator,
-            final HttpHost host,
-            final SocketAddress localAddress,
-            final Timeout connectTimeout,
-            final Object attachment,
-            final FutureCallback<ManagedAsyncClientConnection> callback) {
-        return connect(connectionInitiator, host, localAddress, connectTimeout, connectTimeout, attachment, callback);
-    }
-
-    @Override
     public void upgrade(
             final ManagedAsyncClientConnection connection,
             final HttpHost host,
             final Object attachment) {
-        upgrade(connection, host, null, attachment, null);
+        upgrade(connection, host, attachment, null);
     }
 
     @Override
     public void upgrade(
             final ManagedAsyncClientConnection connection,
             final HttpHost host,
-            final Timeout handshakeTimeout,
             final Object attachment,
             final FutureCallback<ManagedAsyncClientConnection> callback) {
         final TlsStrategy tlsStrategy = tlsStrategyLookup != null ? tlsStrategyLookup.lookup(host.getSchemeName()) : null;
@@ -166,7 +156,7 @@ final class DefaultAsyncClientConnectionOperator implements AsyncClientConnectio
                     connection,
                     host,
                     attachment,
-                    handshakeTimeout,
+                    null,
                     new CallbackContribution<TransportSecurityLayer>(callback) {
 
                         @Override
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
index 74c5bc3..d507154 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java
@@ -41,6 +41,7 @@ import org.apache.hc.client5.http.DnsResolver;
 import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.impl.ConnPoolSupport;
 import org.apache.hc.client5.http.impl.ConnectionShutdownException;
 import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
@@ -123,6 +124,7 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
     private final AtomicBoolean closed;
 
     private volatile Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
+    private volatile Resolver<HttpHost, TlsConfig> tlsConfigResolver;
 
     public PoolingAsyncClientConnectionManager() {
         this(RegistryBuilder.<TlsStrategy>create()
@@ -227,6 +229,15 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
         return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
     }
 
+    private TlsConfig resolveTlsConfig(final HttpHost host, final Object attachment) {
+        if (attachment instanceof TlsConfig) {
+            return (TlsConfig) attachment;
+         }
+        final Resolver<HttpHost, TlsConfig> resolver = this.tlsConfigResolver;
+        final TlsConfig tlsConfig = resolver != null ? resolver.resolve(host) : null;
+        return tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT;
+    }
+
     @Override
     public Future<AsyncConnectionEndpoint> lease(
             final String id,
@@ -400,8 +411,8 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
         }
         final InetSocketAddress localAddress = route.getLocalSocketAddress();
         final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
+        final TlsConfig tlsConfig = resolveTlsConfig(host, attachment);
         final Timeout connectTimeout = timeout != null ? timeout : connectionConfig.getConnectTimeout();
-        final Timeout handshakeTimeout = connectionConfig.getHandshakeTimeout();
 
         if (LOG.isDebugEnabled()) {
             LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), host, connectTimeout);
@@ -411,8 +422,9 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
                 host,
                 localAddress,
                 connectTimeout,
-                handshakeTimeout,
-                route.isTunnelled() ? HttpVersionPolicy.FORCE_HTTP_1 : attachment,
+                route.isTunnelled() ? TlsConfig.copy(tlsConfig)
+                        .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1)
+                        .build() : tlsConfig,
                 new FutureCallback<ManagedAsyncClientConnection>() {
 
                     @Override
@@ -457,13 +469,12 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
         final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
         final PoolEntry<HttpRoute, ManagedAsyncClientConnection> poolEntry = internalEndpoint.getValidatedPoolEntry();
         final HttpRoute route = poolEntry.getRoute();
-        final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
-        final Timeout handshakeTimeout = connectionConfig.getHandshakeTimeout();
+        final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost();
+        final TlsConfig tlsConfig = resolveTlsConfig(host, attachment);
         connectionOperator.upgrade(
                 poolEntry.getConnection(),
                 route.getTargetHost(),
-                handshakeTimeout,
-                attachment,
+                attachment != null ? attachment : tlsConfig,
                 new CallbackContribution<ManagedAsyncClientConnection>(callback) {
 
                     @Override
@@ -572,6 +583,24 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
     }
 
     /**
+     * Sets the same {@link ConnectionConfig} for all hosts
+     *
+     * @since 5.2
+     */
+    public void setDefaultTlsConfig(final TlsConfig config) {
+        this.tlsConfigResolver = (host) -> config;
+    }
+
+    /**
+     * Sets {@link Resolver} of {@link TlsConfig} on a per host basis.
+     *
+     * @since 5.2
+     */
+    public void setTlsConfigResolver(final Resolver<HttpHost, TlsConfig> tlsConfigResolver) {
+        this.tlsConfigResolver = tlsConfigResolver;
+    }
+
+    /**
      * @deprecated Use custom {@link #setConnectionConfigResolver(Resolver)}
      */
     @Deprecated
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/io/HttpClientConnectionOperator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/io/HttpClientConnectionOperator.java
index 5308548..f533fef 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/io/HttpClientConnectionOperator.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/io/HttpClientConnectionOperator.java
@@ -73,8 +73,8 @@ public interface HttpClientConnectionOperator {
      * @param host the address of the opposite endpoint.
      * @param localAddress the address of the local endpoint.
      * @param connectTimeout the timeout of the connect operation.
-     * @param handshakeTimeout the handshake timeout, if applicable.
      * @param socketConfig the socket configuration.
+     * @param attachment connect request attachment.
      * @param context the execution context.
      *
      * @since 5.2
@@ -84,8 +84,8 @@ public interface HttpClientConnectionOperator {
             HttpHost host,
             InetSocketAddress localAddress,
             Timeout connectTimeout,
-            Timeout handshakeTimeout,
             SocketConfig socketConfig,
+            Object attachment,
             HttpContext context) throws IOException {
         connect(conn, host, localAddress, connectTimeout, socketConfig, context);
     }
@@ -109,7 +109,7 @@ public interface HttpClientConnectionOperator {
      *
      * @param conn the managed connection.
      * @param host the address of the opposite endpoint with TLS security.
-     * @param handshakeTimeout the handshake timeout, if applicable.
+     * @param attachment connect request attachment.
      * @param context the execution context.
      *
      * @since 5.2
@@ -117,7 +117,7 @@ public interface HttpClientConnectionOperator {
     default void upgrade(
             ManagedHttpClientConnection conn,
             HttpHost host,
-            Timeout handshakeTimeout,
+            Object attachment,
             HttpContext context) throws IOException {
         upgrade(conn, host, context);
     }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/nio/AsyncClientConnectionOperator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/nio/AsyncClientConnectionOperator.java
index ac46eed..f368e79 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/nio/AsyncClientConnectionOperator.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/nio/AsyncClientConnectionOperator.java
@@ -68,32 +68,6 @@ public interface AsyncClientConnectionOperator {
             FutureCallback<ManagedAsyncClientConnection> callback);
 
     /**
-     * Initiates operation to create a connection to the remote endpoint using
-     * the provided {@link ConnectionInitiator}.
-     *
-     * @param connectionInitiator the connection initiator.
-     * @param host the address of the opposite endpoint.
-     * @param localAddress the address of the local endpoint.
-     * @param connectTimeout the timeout of the connect operation.
-     * @param handshakeTimeout the timeout of the protocol handshake.
-     * @param attachment the attachment, which can be any object representing custom parameter
-     *                    of the operation.
-     * @param callback the future result callback.
-     *
-     * @since 5.2
-     */
-    default Future<ManagedAsyncClientConnection> connect(
-            ConnectionInitiator connectionInitiator,
-            HttpHost host,
-            SocketAddress localAddress,
-            Timeout connectTimeout,
-            Timeout handshakeTimeout,
-            Object attachment,
-            FutureCallback<ManagedAsyncClientConnection> callback) {
-        return connect(connectionInitiator, host, localAddress, connectTimeout, handshakeTimeout, attachment, callback);
-    }
-
-    /**
      * Upgrades transport security of the given managed connection
      * by using the TLS security protocol.
      *
@@ -110,7 +84,6 @@ public interface AsyncClientConnectionOperator {
      *
      * @param conn the managed connection.
      * @param host the address of the opposite endpoint with TLS security.
-     * @param handshakeTimeout the timeout of the protocol handshake.
      * @param attachment the attachment, which can be any object representing custom parameter
      *                    of the operation.
      *
@@ -119,7 +92,6 @@ public interface AsyncClientConnectionOperator {
     default void upgrade(
             ManagedAsyncClientConnection conn,
             HttpHost host,
-            Timeout handshakeTimeout,
             Object attachment,
             FutureCallback<ManagedAsyncClientConnection> callback) {
         upgrade(conn, host, attachment);
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/socket/ConnectionSocketFactory.java b/httpclient5/src/main/java/org/apache/hc/client5/http/socket/ConnectionSocketFactory.java
index b7f5dde..f8d2e6f 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/socket/ConnectionSocketFactory.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/socket/ConnectionSocketFactory.java
@@ -91,7 +91,7 @@ public interface ConnectionSocketFactory {
      * @param remoteAddress the resolved remote address to connect to.
      * @param localAddress the local address to bind the socket to, or {@code null} for any.
      * @param connectTimeout connect timeout.
-     * @param handshakeTimeout handshake timeout, if applicable.
+     * @param attachment connect request attachment.
      * @param context the actual HTTP context.
      *
      * @return  the connected socket. The returned object may be different
@@ -108,7 +108,7 @@ public interface ConnectionSocketFactory {
             InetSocketAddress remoteAddress,
             InetSocketAddress localAddress,
             Timeout connectTimeout,
-            Timeout handshakeTimeout,
+            Object attachment,
             HttpContext context) throws IOException {
         return connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
     }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/socket/LayeredConnectionSocketFactory.java b/httpclient5/src/main/java/org/apache/hc/client5/http/socket/LayeredConnectionSocketFactory.java
index 80661fc..36dfa80 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/socket/LayeredConnectionSocketFactory.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/socket/LayeredConnectionSocketFactory.java
@@ -33,7 +33,6 @@ import java.net.Socket;
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.protocol.HttpContext;
-import org.apache.hc.core5.util.Timeout;
 
 /**
  * Extended {@link ConnectionSocketFactory} interface for layered sockets such as SSL/TLS.
@@ -72,7 +71,7 @@ public interface LayeredConnectionSocketFactory extends ConnectionSocketFactory
      * @param target the name of the target host.
      * @param port the port to connect to on the target host.
      * @param context the actual HTTP context.
-     * @param handshakeTimeout handshake timeout, if applicable.
+     * @param attachment connect request attachment.
      *
      * @return Socket a new socket
      *
@@ -84,7 +83,7 @@ public interface LayeredConnectionSocketFactory extends ConnectionSocketFactory
             Socket socket,
             String target,
             int port,
-            Timeout handshakeTimeout,
+            Object attachment,
             HttpContext context) throws IOException {
         return createLayeredSocket(socket, target, port, context);
     }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/AbstractClientTlsStrategy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/AbstractClientTlsStrategy.java
index 93a1f4a..1d35f54 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/AbstractClientTlsStrategy.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/AbstractClientTlsStrategy.java
@@ -38,6 +38,7 @@ import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLSession;
 
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.concurrent.FutureCallback;
@@ -109,8 +110,8 @@ abstract class AbstractClientTlsStrategy implements TlsStrategy {
             final FutureCallback<TransportSecurityLayer> callback) {
         tlsSession.startTls(sslContext, endpoint, sslBufferManagement, (e, sslEngine) -> {
 
-            final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ?
-                    (HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE;
+            final TlsConfig tlsConfig = attachment instanceof TlsConfig ?(TlsConfig) attachment : TlsConfig.DEFAULT;
+            final HttpVersionPolicy versionPolicy = tlsConfig.getHttpVersionPolicy();
 
             final SSLParameters sslParameters = sslEngine.getSSLParameters();
             if (supportedProtocols != null) {
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/SSLConnectionSocketFactory.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/SSLConnectionSocketFactory.java
index a07a389..59a8df5 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/SSLConnectionSocketFactory.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/SSLConnectionSocketFactory.java
@@ -47,6 +47,7 @@ import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
 
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
@@ -212,7 +213,7 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
             final InetSocketAddress remoteAddress,
             final InetSocketAddress localAddress,
             final Timeout connectTimeout,
-            final Timeout handshakeTimeout,
+            final Object attachment,
             final HttpContext context) throws IOException {
         Args.notNull(host, "HTTP host");
         Args.notNull(remoteAddress, "Remote address");
@@ -244,10 +245,10 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
         // Setup SSL layering if necessary
         if (sock instanceof SSLSocket) {
             final SSLSocket sslsock = (SSLSocket) sock;
-            executeHandshake(sslsock, host.getHostName(), handshakeTimeout);
+            executeHandshake(sslsock, host.getHostName(), attachment);
             return sock;
         }
-        return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), handshakeTimeout, context);
+        return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), attachment, context);
     }
 
     @Override
@@ -264,18 +265,19 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
             final Socket socket,
             final String target,
             final int port,
-            final Timeout handshakeTimeout,
+            final Object attachment,
             final HttpContext context) throws IOException {
         final SSLSocket sslsock = (SSLSocket) this.socketFactory.createSocket(
                 socket,
                 target,
                 port,
                 true);
-        executeHandshake(sslsock, target, handshakeTimeout);
+        executeHandshake(sslsock, target, attachment);
         return sslsock;
     }
 
-    private void executeHandshake(final SSLSocket sslsock, final String target, final Timeout handshakeTimeout) throws IOException {
+    private void executeHandshake(final SSLSocket sslsock, final String target, final Object attachment) throws IOException {
+        final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT;
         if (supportedProtocols != null) {
             sslsock.setEnabledProtocols(supportedProtocols);
         } else {
@@ -286,6 +288,7 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
         } else {
             sslsock.setEnabledCipherSuites(TlsCiphers.excludeWeak(sslsock.getEnabledCipherSuites()));
         }
+        final Timeout handshakeTimeout = tlsConfig.getHandshakeTimeout();
         if (handshakeTimeout != null) {
             sslsock.setSoTimeout(handshakeTimeout.toMillisecondsIntBound());
         }
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java
index a6ac973..1733f4e 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestBasicHttpClientConnectionManager.java
@@ -36,6 +36,7 @@ import org.apache.hc.client5.http.DnsResolver;
 import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.io.ConnectionEndpoint;
 import org.apache.hc.client5.http.io.LeaseRequest;
 import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
@@ -365,9 +366,12 @@ public class TestBasicHttpClientConnectionManager {
 
         final ConnectionConfig connectionConfig = ConnectionConfig.custom()
                 .setConnectTimeout(234, TimeUnit.MILLISECONDS)
-                .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
                 .build();
         mgr.setConnectionConfig(connectionConfig);
+        final TlsConfig tlsConfig = TlsConfig.custom()
+                .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
+                .build();
+        mgr.setTlsConfig(tlsConfig);
 
         Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] {remote});
         Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
@@ -393,7 +397,7 @@ public class TestBasicHttpClientConnectionManager {
                 new InetSocketAddress(remote, 8443),
                 new InetSocketAddress(local, 0),
                 Timeout.ofMilliseconds(234),
-                Timeout.ofMilliseconds(345),
+                tlsConfig,
                 context);
 
         mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
@@ -407,7 +411,7 @@ public class TestBasicHttpClientConnectionManager {
                 new InetSocketAddress(remote, 8443),
                 new InetSocketAddress(local, 0),
                 Timeout.ofMilliseconds(123),
-                Timeout.ofMilliseconds(345),
+                tlsConfig,
                 context);
     }
 
@@ -432,9 +436,12 @@ public class TestBasicHttpClientConnectionManager {
 
         final ConnectionConfig connectionConfig = ConnectionConfig.custom()
                 .setConnectTimeout(234, TimeUnit.MILLISECONDS)
-                .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
                 .build();
         mgr.setConnectionConfig(connectionConfig);
+        final TlsConfig tlsConfig = TlsConfig.custom()
+                .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
+                .build();
+        mgr.setTlsConfig(tlsConfig);
 
         Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote});
         Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080);
@@ -462,7 +469,7 @@ public class TestBasicHttpClientConnectionManager {
                 new InetSocketAddress(remote, 8080),
                 new InetSocketAddress(local, 0),
                 Timeout.ofMilliseconds(234),
-                Timeout.ofMilliseconds(345),
+                tlsConfig,
                 context);
 
         Mockito.when(conn.getSocket()).thenReturn(socket);
@@ -471,7 +478,7 @@ public class TestBasicHttpClientConnectionManager {
 
         Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
         Mockito.verify(sslSocketFactory, Mockito.times(1)).createLayeredSocket(
-                socket, "somehost", 8443, Timeout.ofMilliseconds(345), context);
+                socket, "somehost", 8443, tlsConfig, context);
     }
 
 }
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java
index c860bf3..f83c787 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestHttpClientConnectionOperator.java
@@ -39,6 +39,7 @@ import org.apache.hc.client5.http.DnsResolver;
 import org.apache.hc.client5.http.HttpHostConnectException;
 import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.UnsupportedSchemeException;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
 import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
 import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
@@ -107,9 +108,11 @@ public class TestHttpClientConnectionOperator {
             .setTcpNoDelay(true)
             .setSoLinger(50, TimeUnit.MILLISECONDS)
             .build();
+        final TlsConfig tlsConfig = TlsConfig.custom()
+                .build();
         final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
         connectionOperator.connect(conn, host, localAddress,
-                Timeout.ofMilliseconds(123), Timeout.ofMilliseconds(234), socketConfig, context);
+                Timeout.ofMilliseconds(123), socketConfig, tlsConfig, context);
 
         Mockito.verify(socket).setKeepAlive(true);
         Mockito.verify(socket).setReuseAddress(true);
@@ -123,7 +126,7 @@ public class TestHttpClientConnectionOperator {
                 new InetSocketAddress(ip1, 80),
                 localAddress,
                 Timeout.ofMilliseconds(123),
-                Timeout.ofMilliseconds(234),
+                tlsConfig,
                 context);
         Mockito.verify(conn, Mockito.times(2)).bind(socket);
     }
@@ -208,8 +211,10 @@ public class TestHttpClientConnectionOperator {
                 Mockito.any())).thenReturn(socket);
 
         final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
+        final TlsConfig tlsConfig = TlsConfig.custom()
+                .build();
         connectionOperator.connect(conn, host, localAddress,
-                Timeout.ofMilliseconds(123), Timeout.ofMilliseconds(234), SocketConfig.DEFAULT, context);
+                Timeout.ofMilliseconds(123), SocketConfig.DEFAULT, tlsConfig, context);
 
         Mockito.verify(plainSocketFactory).connectSocket(
                 socket,
@@ -217,7 +222,7 @@ public class TestHttpClientConnectionOperator {
                 new InetSocketAddress(ip2, 80),
                 localAddress,
                 Timeout.ofMilliseconds(123),
-                Timeout.ofMilliseconds(234),
+                tlsConfig,
                 context);
         Mockito.verify(conn, Mockito.times(3)).bind(socket);
     }
@@ -242,8 +247,10 @@ public class TestHttpClientConnectionOperator {
                 Mockito.any())).thenReturn(socket);
 
         final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
+        final TlsConfig tlsConfig = TlsConfig.custom()
+                .build();
         connectionOperator.connect(conn, host, localAddress,
-                Timeout.ofMilliseconds(123), Timeout.ofMilliseconds(234), SocketConfig.DEFAULT, context);
+                Timeout.ofMilliseconds(123), SocketConfig.DEFAULT, tlsConfig, context);
 
         Mockito.verify(plainSocketFactory).connectSocket(
                 socket,
@@ -251,7 +258,7 @@ public class TestHttpClientConnectionOperator {
                 new InetSocketAddress(ip, 80),
                 localAddress,
                 Timeout.ofMilliseconds(123),
-                Timeout.ofMilliseconds(234),
+                tlsConfig,
                 context);
         Mockito.verify(dnsResolver, Mockito.never()).resolve(Mockito.anyString());
         Mockito.verify(conn, Mockito.times(2)).bind(socket);
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java
index a24c5c8..f837d28 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/io/TestPoolingHttpClientConnectionManager.java
@@ -38,6 +38,7 @@ import org.apache.hc.client5.http.DnsResolver;
 import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.io.ConnectionEndpoint;
 import org.apache.hc.client5.http.io.LeaseRequest;
 import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
@@ -250,9 +251,12 @@ public class TestPoolingHttpClientConnectionManager {
 
         final ConnectionConfig connectionConfig = ConnectionConfig.custom()
                 .setConnectTimeout(234, TimeUnit.MILLISECONDS)
-                .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
                 .build();
         mgr.setDefaultConnectionConfig(connectionConfig);
+        final TlsConfig tlsConfig = TlsConfig.custom()
+                .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
+                .build();
+        mgr.setDefaultTlsConfig(tlsConfig);
 
         Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[]{remote});
         Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
@@ -278,7 +282,7 @@ public class TestPoolingHttpClientConnectionManager {
                 new InetSocketAddress(remote, 8443),
                 new InetSocketAddress(local, 0),
                 Timeout.ofMilliseconds(234),
-                Timeout.ofMilliseconds(345),
+                tlsConfig,
                 context);
 
         mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
@@ -292,7 +296,7 @@ public class TestPoolingHttpClientConnectionManager {
                 new InetSocketAddress(remote, 8443),
                 new InetSocketAddress(local, 0),
                 Timeout.ofMilliseconds(123),
-                Timeout.ofMilliseconds(345),
+                tlsConfig,
                 context);
     }
 
@@ -330,9 +334,12 @@ public class TestPoolingHttpClientConnectionManager {
 
         final ConnectionConfig connectionConfig = ConnectionConfig.custom()
                 .setConnectTimeout(234, TimeUnit.MILLISECONDS)
-                .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
                 .build();
         mgr.setDefaultConnectionConfig(connectionConfig);
+        final TlsConfig tlsConfig = TlsConfig.custom()
+                .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
+                .build();
+        mgr.setDefaultTlsConfig(tlsConfig);
 
         Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote});
         Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080);
@@ -360,7 +367,7 @@ public class TestPoolingHttpClientConnectionManager {
                 new InetSocketAddress(remote, 8080),
                 new InetSocketAddress(local, 0),
                 Timeout.ofMilliseconds(234),
-                Timeout.ofMilliseconds(345),
+                tlsConfig,
                 context);
 
         Mockito.when(conn.isOpen()).thenReturn(true);
@@ -370,7 +377,7 @@ public class TestPoolingHttpClientConnectionManager {
 
         Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
         Mockito.verify(sslsf, Mockito.times(1)).createLayeredSocket(
-                mockSock, "somehost", 8443, Timeout.ofMilliseconds(345), context);
+                mockSock, "somehost", 8443, tlsConfig, context);
     }
 
 }