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:20:59 UTC

[httpcomponents-client] branch HTTPCLIENT-2135 created (now 4a316fd)

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

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


      at 4a316fd  HTTPCLIENT-2135: TLS configuration on a per-host basis

This branch includes the following new commits:

     new 4a316fd  HTTPCLIENT-2135: TLS configuration on a per-host basis

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


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

Posted by ol...@apache.org.
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);
     }
 
 }