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/23 17:53:15 UTC

[httpcomponents-client] branch master updated (fff0976 -> ffc8cd7)

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

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


    from fff0976  Make IOReactor IO session decorator configurable.
     new a02455a  Support for connection TTL setting on a per-route basis
     new ffc8cd7  Updated connection and TLS configuration examples

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


Summary of changes:
 .../hc/client5/http/config/ConnectionConfig.java   |  41 +++++++-
 .../apache/hc/client5/http/config/TlsConfig.java   |  18 ++++
 .../impl/io/BasicHttpClientConnectionManager.java  |  64 +++++++++----
 .../io/PoolingHttpClientConnectionManager.java     |  40 +++++---
 .../PoolingHttpClientConnectionManagerBuilder.java |  11 ++-
 .../nio/PoolingAsyncClientConnectionManager.java   |  44 ++++++---
 ...PoolingAsyncClientConnectionManagerBuilder.java |  10 +-
 ...ining.java => AsyncClientConnectionConfig.java} |  95 +++++++++++--------
 .../client5/http/examples/ClientConfiguration.java |   9 ++
 .../http/examples/ClientConnectionConfig.java      | 103 +++++++++++++++++++++
 .../hc/client5/http/examples/ClientCustomSSL.java  |   9 +-
 11 files changed, 344 insertions(+), 100 deletions(-)
 copy httpclient5/src/test/java/org/apache/hc/client5/http/examples/{AsyncClientHttp1Pipelining.java => AsyncClientConnectionConfig.java} (52%)
 create mode 100644 httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientConnectionConfig.java

[httpcomponents-client] 01/02: Support for connection TTL setting on a per-route basis

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

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

commit a02455acb31ad7e676130d46d6a04bd97b76e957
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sat Oct 23 15:57:00 2021 +0200

    Support for connection TTL setting on a per-route basis
---
 .../hc/client5/http/config/ConnectionConfig.java   | 41 ++++++++++++--
 .../impl/io/BasicHttpClientConnectionManager.java  | 64 ++++++++++++++++------
 .../io/PoolingHttpClientConnectionManager.java     | 40 +++++++++-----
 .../PoolingHttpClientConnectionManagerBuilder.java | 11 ++--
 .../nio/PoolingAsyncClientConnectionManager.java   | 44 ++++++++++-----
 ...PoolingAsyncClientConnectionManagerBuilder.java | 10 +++-
 6 files changed, 153 insertions(+), 57 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 38ead6f..126b38d 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
@@ -49,22 +49,25 @@ public class ConnectionConfig implements Cloneable {
     private final Timeout connectTimeout;
     private final Timeout socketTimeout;
     private final TimeValue validateAfterInactivity;
+    private final TimeValue timeToLive;
 
     /**
      * Intended for CDI compatibility
      */
     protected ConnectionConfig() {
-        this(DEFAULT_CONNECT_TIMEOUT, null, null);
+        this(DEFAULT_CONNECT_TIMEOUT, null, null, null);
     }
 
     ConnectionConfig(
             final Timeout connectTimeout,
             final Timeout socketTimeout,
-            final TimeValue validateAfterInactivity) {
+            final TimeValue validateAfterInactivity,
+            final TimeValue timeToLive) {
         super();
         this.connectTimeout = connectTimeout;
         this.socketTimeout = socketTimeout;
         this.validateAfterInactivity = validateAfterInactivity;
+        this.timeToLive = timeToLive;
     }
 
     /**
@@ -88,6 +91,13 @@ public class ConnectionConfig implements Cloneable {
         return validateAfterInactivity;
     }
 
+    /**
+     * @see Builder#setTimeToLive(TimeValue) (TimeValue)
+     */
+    public TimeValue getTimeToLive() {
+        return timeToLive;
+    }
+
     @Override
     protected ConnectionConfig clone() throws CloneNotSupportedException {
         return (ConnectionConfig) super.clone();
@@ -100,6 +110,7 @@ public class ConnectionConfig implements Cloneable {
         builder.append(", connectTimeout=").append(connectTimeout);
         builder.append(", socketTimeout=").append(socketTimeout);
         builder.append(", validateAfterInactivity=").append(validateAfterInactivity);
+        builder.append(", timeToLive=").append(timeToLive);
         builder.append("]");
         return builder.toString();
     }
@@ -112,7 +123,8 @@ public class ConnectionConfig implements Cloneable {
         return new Builder()
                 .setConnectTimeout(config.getConnectTimeout())
                 .setSocketTimeout(config.getSocketTimeout())
-                .setValidateAfterInactivity(config.getValidateAfterInactivity());
+                .setValidateAfterInactivity(config.getValidateAfterInactivity())
+                .setTimeToLive(config.getTimeToLive());
     }
 
     public static class Builder {
@@ -120,6 +132,7 @@ public class ConnectionConfig implements Cloneable {
         private Timeout socketTimeout;
         private Timeout connectTimeout;
         private TimeValue validateAfterInactivity;
+        private TimeValue timeToLive;
 
         Builder() {
             super();
@@ -190,11 +203,31 @@ public class ConnectionConfig implements Cloneable {
             return this;
         }
 
+        /**
+         * Defines the total span of time connections can be kept alive or execute requests.
+         * <p>
+         * Default: {@code null} (undefined)
+         * </p>
+         */
+        public Builder setTimeToLive(final TimeValue timeToLive) {
+            this.timeToLive = timeToLive;
+            return this;
+        }
+
+        /**
+         * @see #setTimeToLive(TimeValue)
+         */
+        public Builder setTimeToLive(final long timeToLive, final TimeUnit timeUnit) {
+            this.timeToLive = TimeValue.of(timeToLive, timeUnit);
+            return this;
+        }
+
         public ConnectionConfig build() {
             return new ConnectionConfig(
                     connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT,
                     socketTimeout,
-                    validateAfterInactivity);
+                    validateAfterInactivity,
+                    timeToLive);
         }
 
     }
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 a67b3ad..cfa82dc 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
@@ -67,6 +67,7 @@ import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.io.CloseMode;
 import org.apache.hc.core5.util.Args;
 import org.apache.hc.core5.util.Asserts;
+import org.apache.hc.core5.util.Deadline;
 import org.apache.hc.core5.util.LangUtils;
 import org.apache.hc.core5.util.TimeValue;
 import org.apache.hc.core5.util.Timeout;
@@ -105,6 +106,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
     private ManagedHttpClientConnection conn;
     private HttpRoute route;
     private Object state;
+    private long created;
     private long updated;
     private long expiry;
     private boolean leased;
@@ -114,8 +116,6 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
 
     private final AtomicBoolean closed;
 
-    private volatile TimeValue validateAfterInactivity;
-
     private static Registry<ConnectionSocketFactory> getDefaultRegistry() {
         return RegistryBuilder.<ConnectionSocketFactory>create()
                 .register(URIScheme.HTTP.id, PlainConnectionSocketFactory.getSocketFactory())
@@ -147,7 +147,6 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
         this.connectionConfig = ConnectionConfig.DEFAULT;
         this.tlsConfig = TlsConfig.DEFAULT;
         this.closed = new AtomicBoolean(false);
-        this.validateAfterInactivity = TimeValue.ofSeconds(2L);
     }
 
     public BasicHttpClientConnectionManager(
@@ -210,6 +209,13 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
     /**
      * @since 5.2
      */
+    public synchronized TlsConfig getTlsConfig() {
+        return tlsConfig;
+    }
+
+    /**
+     * @since 5.2
+     */
     public synchronized void setTlsConfig(final TlsConfig tlsConfig) {
         this.tlsConfig = tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT;
     }
@@ -260,21 +266,34 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
     }
 
     private void validate() {
-        final TimeValue validateAfterInactivitySnapshot = validateAfterInactivity;
-        if (this.conn != null
-                && TimeValue.isNonNegative(validateAfterInactivitySnapshot)
-                && updated + validateAfterInactivitySnapshot.toMilliseconds() <= System.currentTimeMillis()) {
-            boolean stale;
-            try {
-                stale = conn.isStale();
-            } catch (final IOException ignore) {
-                stale = true;
+        if (this.conn != null) {
+            final TimeValue timeToLive = connectionConfig.getTimeToLive();
+            if (TimeValue.isNonNegative(timeToLive)) {
+                final Deadline deadline = Deadline.calculate(created, timeToLive);
+                if (deadline.isExpired()) {
+                    closeConnection(CloseMode.GRACEFUL);
+                }
             }
-            if (stale) {
-                if (LOG.isDebugEnabled()) {
-                    LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn));
+        }
+        if (this.conn != null) {
+            final TimeValue timeValue = connectionConfig.getValidateAfterInactivity() != null ?
+                    connectionConfig.getValidateAfterInactivity() : TimeValue.ofSeconds(2);
+            if (TimeValue.isNonNegative(timeValue)) {
+                final Deadline deadline = Deadline.calculate(updated, timeValue);
+                if (deadline.isExpired()) {
+                    boolean stale;
+                    try {
+                        stale = conn.isStale();
+                    } catch (final IOException ignore) {
+                        stale = true;
+                    }
+                    if (stale) {
+                        if (LOG.isDebugEnabled()) {
+                            LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn));
+                        }
+                        closeConnection(CloseMode.GRACEFUL);
+                    }
                 }
-                closeConnection(CloseMode.GRACEFUL);
             }
         }
     }
@@ -294,6 +313,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
         validate();
         if (this.conn == null) {
             this.conn = this.connFactory.createConnection(null);
+            this.created = System.currentTimeMillis();
         } else {
             this.conn.activate();
         }
@@ -436,9 +456,12 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
      * @see #setValidateAfterInactivity(TimeValue)
      *
      * @since 5.1
+     *
+     * @deprecated Use {@link #getConnectionConfig()}
      */
+    @Deprecated
     public TimeValue getValidateAfterInactivity() {
-        return validateAfterInactivity;
+        return connectionConfig.getValidateAfterInactivity();
     }
 
     /**
@@ -448,9 +471,14 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
      * detect connections that have become stale (half-closed) while kept inactive in the pool.
      *
      * @since 5.1
+     *
+     * @deprecated Use {@link #setConnectionConfig(ConnectionConfig)}
      */
+    @Deprecated
     public void setValidateAfterInactivity(final TimeValue validateAfterInactivity) {
-        this.validateAfterInactivity = validateAfterInactivity;
+        this.connectionConfig = ConnectionConfig.custom()
+                .setValidateAfterInactivity(validateAfterInactivity)
+                .build();
     }
 
     class InternalConnectionEndpoint extends ConnectionEndpoint {
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 8c415fc..626fe98 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
@@ -76,6 +76,7 @@ import org.apache.hc.core5.pool.PoolStats;
 import org.apache.hc.core5.pool.StrictConnPool;
 import org.apache.hc.core5.util.Args;
 import org.apache.hc.core5.util.Asserts;
+import org.apache.hc.core5.util.Deadline;
 import org.apache.hc.core5.util.Identifiable;
 import org.apache.hc.core5.util.TimeValue;
 import org.apache.hc.core5.util.Timeout;
@@ -303,23 +304,34 @@ public class PoolingHttpClientConnectionManager
                     LOG.debug("{} endpoint leased {}", id, ConnPoolSupport.formatStats(route, state, pool));
                 }
                 final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
-                final TimeValue timeValue = resolveValidateAfterInactivity(connectionConfig);
                 try {
-                    if (TimeValue.isNonNegative(timeValue)) {
-                        final ManagedHttpClientConnection conn = poolEntry.getConnection();
-                        if (conn != null
-                                && poolEntry.getUpdated() + timeValue.toMilliseconds() <= System.currentTimeMillis()) {
-                            boolean stale;
-                            try {
-                                stale = conn.isStale();
-                            } catch (final IOException ignore) {
-                                stale = true;
+                    if (poolEntry.hasConnection()) {
+                        final TimeValue timeToLive = connectionConfig.getTimeToLive();
+                        if (TimeValue.isNonNegative(timeToLive)) {
+                            final Deadline deadline = Deadline.calculate(poolEntry.getCreated(), timeToLive);
+                            if (deadline.isExpired()) {
+                                poolEntry.discardConnection(CloseMode.GRACEFUL);
                             }
-                            if (stale) {
-                                if (LOG.isDebugEnabled()) {
-                                    LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn));
+                        }
+                    }
+                    if (poolEntry.hasConnection()) {
+                        final TimeValue timeValue = resolveValidateAfterInactivity(connectionConfig);
+                        if (TimeValue.isNonNegative(timeValue)) {
+                            final Deadline deadline = Deadline.calculate(poolEntry.getUpdated(), timeValue);
+                            if (deadline.isExpired()) {
+                                final ManagedHttpClientConnection conn = poolEntry.getConnection();
+                                boolean stale;
+                                try {
+                                    stale = conn.isStale();
+                                } catch (final IOException ignore) {
+                                    stale = true;
+                                }
+                                if (stale) {
+                                    if (LOG.isDebugEnabled()) {
+                                        LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn));
+                                    }
+                                    poolEntry.discardConnection(CloseMode.IMMEDIATE);
                                 }
-                                poolEntry.discardConnection(CloseMode.IMMEDIATE);
                             }
                         }
                     }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java
index c150e23..799c6ba 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManagerBuilder.java
@@ -90,8 +90,6 @@ public class PoolingHttpClientConnectionManagerBuilder {
     private int maxConnTotal;
     private int maxConnPerRoute;
 
-    private TimeValue timeToLive;
-
     public static PoolingHttpClientConnectionManagerBuilder create() {
         return new PoolingHttpClientConnectionManagerBuilder();
     }
@@ -229,9 +227,14 @@ public class PoolingHttpClientConnectionManagerBuilder {
 
     /**
      * Sets maximum time to live for persistent connections
+     *
+     * @deprecated Use {@link #setDefaultConnectionConfig(ConnectionConfig)}.
      */
+    @Deprecated
     public final PoolingHttpClientConnectionManagerBuilder setConnectionTimeToLive(final TimeValue timeToLive) {
-        this.timeToLive = timeToLive;
+        setDefaultConnectionConfig(ConnectionConfig.custom()
+                .setTimeToLive(timeToLive)
+                .build());
         return this;
     }
 
@@ -269,7 +272,7 @@ public class PoolingHttpClientConnectionManagerBuilder {
                         .build(),
                 poolConcurrencyPolicy,
                 poolReusePolicy,
-                timeToLive != null ? timeToLive : TimeValue.NEG_ONE_MILLISECOND,
+                null,
                 schemePortResolver,
                 dnsResolver,
                 connectionFactory);
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 3db4ca1..3975859 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
@@ -88,6 +88,7 @@ import org.apache.hc.core5.reactor.ProtocolIOSession;
 import org.apache.hc.core5.reactor.ssl.TlsDetails;
 import org.apache.hc.core5.util.Args;
 import org.apache.hc.core5.util.Asserts;
+import org.apache.hc.core5.util.Deadline;
 import org.apache.hc.core5.util.Identifiable;
 import org.apache.hc.core5.util.TimeValue;
 import org.apache.hc.core5.util.Timeout;
@@ -261,25 +262,40 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
 
                         @Override
                         public void completed(final PoolEntry<HttpRoute, ManagedAsyncClientConnection> poolEntry) {
-                            final ManagedAsyncClientConnection connection = poolEntry.getConnection();
-                            final TimeValue timeValue = connectionConfig != null ? connectionConfig.getValidateAfterInactivity() : null;
-                            if (TimeValue.isNonNegative(timeValue) && connection != null &&
-                                    poolEntry.getUpdated() + timeValue.toMilliseconds() <= System.currentTimeMillis()) {
-                                final ProtocolVersion protocolVersion = connection.getProtocolVersion();
-                                if (protocolVersion != null && protocolVersion.greaterEquals(HttpVersion.HTTP_2_0)) {
-                                    connection.submitCommand(new PingCommand(new BasicPingHandler(result -> {
-                                        if (result == null || !result)  {
+                            if (poolEntry.hasConnection()) {
+                                final TimeValue timeToLive = connectionConfig.getTimeToLive();
+                                if (TimeValue.isNonNegative(timeToLive)) {
+                                    final Deadline deadline = Deadline.calculate(poolEntry.getCreated(), timeToLive);
+                                    if (deadline.isExpired()) {
+                                        poolEntry.discardConnection(CloseMode.GRACEFUL);
+                                    }
+                                }
+                            }
+                            if (poolEntry.hasConnection()) {
+                                final ManagedAsyncClientConnection connection = poolEntry.getConnection();
+                                final TimeValue timeValue = connectionConfig.getValidateAfterInactivity();
+                                if (connection.isOpen() && TimeValue.isNonNegative(timeValue)) {
+                                    final Deadline deadline = Deadline.calculate(poolEntry.getUpdated(), timeValue);
+                                    if (deadline.isExpired()) {
+                                        final ProtocolVersion protocolVersion = connection.getProtocolVersion();
+                                        if (protocolVersion != null && protocolVersion.greaterEquals(HttpVersion.HTTP_2_0)) {
+                                            connection.submitCommand(new PingCommand(new BasicPingHandler(result -> {
+                                                if (result == null || !result)  {
+                                                    if (LOG.isDebugEnabled()) {
+                                                        LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(connection));
+                                                    }
+                                                    poolEntry.discardConnection(CloseMode.GRACEFUL);
+                                                }
+                                                leaseCompleted(poolEntry);
+                                            })), Command.Priority.IMMEDIATE);
+                                            return;
+                                        } else {
                                             if (LOG.isDebugEnabled()) {
-                                                LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(connection));
+                                                LOG.debug("{} connection {} is closed", id, ConnPoolSupport.getId(connection));
                                             }
                                             poolEntry.discardConnection(CloseMode.IMMEDIATE);
                                         }
-                                    })), Command.Priority.IMMEDIATE);
-                                } else {
-                                    if (LOG.isDebugEnabled()) {
-                                        LOG.debug("{} connection {} is closed", id, ConnPoolSupport.getId(connection));
                                     }
-                                    poolEntry.discardConnection(CloseMode.IMMEDIATE);
                                 }
                             }
                             leaseCompleted(poolEntry);
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java
index 941f412..302e5af 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManagerBuilder.java
@@ -87,7 +87,6 @@ public class PoolingAsyncClientConnectionManagerBuilder {
     private Resolver<HttpRoute, SocketConfig> socketConfigResolver;
     private Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
     private Resolver<HttpHost, TlsConfig> tlsConfigResolver;
-    private TimeValue timeToLive;
 
     public static PoolingAsyncClientConnectionManagerBuilder create() {
         return new PoolingAsyncClientConnectionManagerBuilder();
@@ -198,9 +197,14 @@ public class PoolingAsyncClientConnectionManagerBuilder {
 
     /**
      * Sets maximum time to live for persistent connections
+     *
+     * @deprecated Use {@link #setDefaultConnectionConfig(ConnectionConfig)}
      */
+    @Deprecated
     public final PoolingAsyncClientConnectionManagerBuilder setConnectionTimeToLive(final TimeValue timeToLive) {
-        this.timeToLive = timeToLive;
+        setDefaultConnectionConfig(ConnectionConfig.custom()
+                .setTimeToLive(timeToLive)
+                .build());
         return this;
     }
 
@@ -252,7 +256,7 @@ public class PoolingAsyncClientConnectionManagerBuilder {
                         .build(),
                 poolConcurrencyPolicy,
                 poolReusePolicy,
-                timeToLive,
+                null,
                 schemePortResolver,
                 dnsResolver);
         poolingmgr.setConnectionConfigResolver(connectionConfigResolver);

[httpcomponents-client] 02/02: Updated connection and TLS configuration examples

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

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

commit ffc8cd7585f13689170bae1e1b894f649b6581a7
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sat Oct 23 19:48:00 2021 +0200

    Updated connection and TLS configuration examples
---
 .../apache/hc/client5/http/config/TlsConfig.java   |  18 +++
 .../http/examples/AsyncClientConnectionConfig.java | 133 +++++++++++++++++++++
 .../client5/http/examples/ClientConfiguration.java |   9 ++
 .../http/examples/ClientConnectionConfig.java      | 103 ++++++++++++++++
 .../hc/client5/http/examples/ClientCustomSSL.java  |   9 +-
 5 files changed, 270 insertions(+), 2 deletions(-)

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
index 7a014e2..45acdb9 100644
--- 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
@@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.http.ssl.TLS;
 import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.util.Timeout;
 
@@ -167,6 +168,23 @@ public class TlsConfig implements Cloneable {
         }
 
         /**
+         * Determines supported TLS protocols.
+         * <p>
+         * Default: {@code null} (undefined)
+         * </p>
+         */
+        public Builder setSupportedProtocols(final TLS... supportedProtocols) {
+            this.supportedProtocols = new String[supportedProtocols.length];
+            for (int i = 0; i < supportedProtocols.length; i++) {
+                final TLS protocol = supportedProtocols[i];
+                if (protocol != null) {
+                    this.supportedProtocols[i] = protocol.id;
+                }
+            }
+            return this;
+        }
+
+        /**
          * Determines supported cipher suites.
          * <p>
          * Default: {@code null} (undefined)
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientConnectionConfig.java b/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientConnectionConfig.java
new file mode 100644
index 0000000..7f2dd3a
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientConnectionConfig.java
@@ -0,0 +1,133 @@
+/*
+ * ====================================================================
+ * 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.examples;
+
+import java.util.concurrent.Future;
+
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
+import org.apache.hc.client5.http.async.methods.SimpleRequestProducer;
+import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer;
+import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.config.TlsConfig;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.message.StatusLine;
+import org.apache.hc.core5.http.ssl.TLS;
+import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.hc.core5.util.Timeout;
+
+/**
+ * This example demonstrates how to use connection configuration on a per-route or a per-host
+ * basis.
+ */
+public class AsyncClientConnectionConfig {
+
+    public static void main(final String[] args) throws Exception {
+        final PoolingAsyncClientConnectionManager cm = PoolingAsyncClientConnectionManagerBuilder.create()
+                .setConnectionConfigResolver(route -> {
+                    // Use different settings for all secure (TLS) connections
+                    final HttpHost targetHost = route.getTargetHost();
+                    if (route.isSecure()) {
+                        return ConnectionConfig.custom()
+                                .setConnectTimeout(Timeout.ofMinutes(2))
+                                .setSocketTimeout(Timeout.ofMinutes(2))
+                                .setValidateAfterInactivity(TimeValue.ofMinutes(1))
+                                .setTimeToLive(TimeValue.ofHours(1))
+                                .build();
+                    } else {
+                        return ConnectionConfig.custom()
+                                .setConnectTimeout(Timeout.ofMinutes(1))
+                                .setSocketTimeout(Timeout.ofMinutes(1))
+                                .setValidateAfterInactivity(TimeValue.ofSeconds(15))
+                                .setTimeToLive(TimeValue.ofMinutes(15))
+                                .build();
+                    }
+                })
+                .setTlsConfigResolver(host -> {
+                    // Use different settings for specific hosts
+                    if (host.getSchemeName().equalsIgnoreCase("httpbin.org")) {
+                        return TlsConfig.custom()
+                                .setSupportedProtocols(TLS.V_1_3)
+                                .setHandshakeTimeout(Timeout.ofSeconds(10))
+                                .build();
+                    } else {
+                        return TlsConfig.DEFAULT;
+                    }
+                })
+                .build();
+        try (final CloseableHttpAsyncClient client = HttpAsyncClients.custom()
+                .setConnectionManager(cm)
+                .build()) {
+
+            client.start();
+
+            for (final URIScheme uriScheme : URIScheme.values()) {
+                final SimpleHttpRequest request = SimpleRequestBuilder.get()
+                        .setHttpHost(new HttpHost(uriScheme.id, "httpbin.org"))
+                        .setPath("/headers")
+                        .build();
+
+                System.out.println("Executing request " + request);
+                final Future<SimpleHttpResponse> future = client.execute(
+                        SimpleRequestProducer.create(request),
+                        SimpleResponseConsumer.create(),
+                        new FutureCallback<SimpleHttpResponse>() {
+
+                            @Override
+                            public void completed(final SimpleHttpResponse response) {
+                                System.out.println(request + "->" + new StatusLine(response));
+                                System.out.println(response.getBody());
+                            }
+
+                            @Override
+                            public void failed(final Exception ex) {
+                                System.out.println(request + "->" + ex);
+                            }
+
+                            @Override
+                            public void cancelled() {
+                                System.out.println(request + " cancelled");
+                            }
+
+                        });
+                future.get();
+            }
+
+            System.out.println("Shutting down");
+            client.close(CloseMode.GRACEFUL);
+        }
+    }
+
+}
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientConfiguration.java b/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientConfiguration.java
index 42d827e..5c900af 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientConfiguration.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientConfiguration.java
@@ -44,6 +44,7 @@ import org.apache.hc.client5.http.auth.StandardAuthScheme;
 import org.apache.hc.client5.http.classic.methods.HttpGet;
 import org.apache.hc.client5.http.config.ConnectionConfig;
 import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.cookie.BasicCookieStore;
 import org.apache.hc.client5.http.cookie.CookieStore;
 import org.apache.hc.client5.http.cookie.StandardCookieSpec;
@@ -80,6 +81,7 @@ import org.apache.hc.core5.http.io.entity.EntityUtils;
 import org.apache.hc.core5.http.message.BasicHeader;
 import org.apache.hc.core5.http.message.BasicLineParser;
 import org.apache.hc.core5.http.message.LineParser;
+import org.apache.hc.core5.http.ssl.TLS;
 import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
 import org.apache.hc.core5.pool.PoolReusePolicy;
 import org.apache.hc.core5.ssl.SSLContexts;
@@ -183,6 +185,13 @@ public class ClientConfiguration {
                 .setConnectTimeout(Timeout.ofSeconds(30))
                 .setSocketTimeout(Timeout.ofSeconds(30))
                 .setValidateAfterInactivity(TimeValue.ofSeconds(10))
+                .setTimeToLive(TimeValue.ofHours(1))
+                .build());
+
+        // Use TLS v1.3 only
+        connManager.setDefaultTlsConfig(TlsConfig.custom()
+                .setHandshakeTimeout(Timeout.ofSeconds(30))
+                .setSupportedProtocols(TLS.V_1_3)
                 .build());
 
         // Configure total max or per route limits for persistent connections
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientConnectionConfig.java b/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientConnectionConfig.java
new file mode 100644
index 0000000..bcbd209
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientConnectionConfig.java
@@ -0,0 +1,103 @@
+/*
+ * ====================================================================
+ * 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.examples;
+
+import org.apache.hc.client5.http.config.ConnectionConfig;
+import org.apache.hc.client5.http.config.TlsConfig;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
+import org.apache.hc.core5.http.message.StatusLine;
+import org.apache.hc.core5.http.ssl.TLS;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.hc.core5.util.Timeout;
+
+/**
+ * This example demonstrates how to use connection configuration on a per-route or a per-host
+ * basis.
+ */
+public class ClientConnectionConfig {
+
+    public final static void main(final String[] args) throws Exception {
+        final PoolingHttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
+                .setConnectionConfigResolver(route -> {
+                    // Use different settings for all secure (TLS) connections
+                    final HttpHost targetHost = route.getTargetHost();
+                    if (route.isSecure()) {
+                        return ConnectionConfig.custom()
+                                .setConnectTimeout(Timeout.ofMinutes(2))
+                                .setSocketTimeout(Timeout.ofMinutes(2))
+                                .setValidateAfterInactivity(TimeValue.ofMinutes(1))
+                                .setTimeToLive(TimeValue.ofHours(1))
+                                .build();
+                    } else {
+                        return ConnectionConfig.custom()
+                                .setConnectTimeout(Timeout.ofMinutes(1))
+                                .setSocketTimeout(Timeout.ofMinutes(1))
+                                .setValidateAfterInactivity(TimeValue.ofSeconds(15))
+                                .setTimeToLive(TimeValue.ofMinutes(15))
+                                .build();
+                    }
+                })
+                .setTlsConfigResolver(host -> {
+                    // Use different settings for specific hosts
+                    if (host.getSchemeName().equalsIgnoreCase("httpbin.org")) {
+                        return TlsConfig.custom()
+                                .setSupportedProtocols(TLS.V_1_3)
+                                .setHandshakeTimeout(Timeout.ofSeconds(10))
+                                .build();
+                    } else {
+                        return TlsConfig.DEFAULT;
+                    }
+                })
+                .build();
+        try (CloseableHttpClient httpclient = HttpClients.custom()
+                .setConnectionManager(cm)
+                .build()) {
+
+            for (final URIScheme uriScheme : URIScheme.values()) {
+                final ClassicHttpRequest request = ClassicRequestBuilder.get()
+                        .setHttpHost(new HttpHost(uriScheme.id, "httpbin.org"))
+                        .setPath("/headers")
+                        .build();
+                System.out.println("Executing request " + request);
+                try (CloseableHttpResponse response = httpclient.execute(request)) {
+                    System.out.println(request + "->" + new StatusLine(response));
+                    EntityUtils.consume(response.getEntity());
+                }
+            }
+        }
+    }
+
+}
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientCustomSSL.java b/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientCustomSSL.java
index b9c2025..88382d3 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientCustomSSL.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientCustomSSL.java
@@ -32,6 +32,7 @@ import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSession;
 
 import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.config.TlsConfig;
 import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
 import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
 import org.apache.hc.client5.http.impl.classic.HttpClients;
@@ -43,6 +44,7 @@ import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
 import org.apache.hc.core5.http.io.entity.EntityUtils;
 import org.apache.hc.core5.http.ssl.TLS;
 import org.apache.hc.core5.ssl.SSLContexts;
+import org.apache.hc.core5.util.Timeout;
 
 /**
  * This example demonstrates how to create secure connections with a custom SSL
@@ -58,13 +60,16 @@ public class ClientCustomSSL {
                     return "CN=httpbin.org".equalsIgnoreCase(cert.getSubjectDN().getName());
                 })
                 .build();
-        // Allow TLSv1.2 protocol only
         final SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create()
                 .setSslContext(sslcontext)
-                .setTlsVersions(TLS.V_1_2)
                 .build();
+        // Allow TLSv1.3 protocol only
         final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
                 .setSSLSocketFactory(sslSocketFactory)
+                .setDefaultTlsConfig(TlsConfig.custom()
+                        .setHandshakeTimeout(Timeout.ofSeconds(30))
+                        .setSupportedProtocols(TLS.V_1_3)
+                        .build())
                 .build();
         try (CloseableHttpClient httpclient = HttpClients.custom()
                 .setConnectionManager(cm)