Blocking connection managers to validate connections after inactivity of more than 2s by default; behavior of async connection managers remains the same
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 6af618f..230c304 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
@@ -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.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.ConnectionShutdownException;
import org.apache.hc.client5.http.io.ConnectionEndpoint;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
@@ -109,6 +110,8 @@
private final AtomicBoolean closed;
+ private volatile TimeValue validateAfterInactivity;
+
private static Registry<ConnectionSocketFactory> getDefaultRegistry() {
return RegistryBuilder.<ConnectionSocketFactory>create()
.register(URIScheme.HTTP.id, PlainConnectionSocketFactory.getSocketFactory())
@@ -138,6 +141,7 @@
this.expiry = Long.MAX_VALUE;
this.socketConfig = SocketConfig.DEFAULT;
this.closed = new AtomicBoolean(false);
+ this.validateAfterInactivity = TimeValue.ofSeconds(2L);
}
public BasicHttpClientConnectionManager(
@@ -228,6 +232,26 @@
}
}
+ 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 (stale) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn));
+ }
+ closeConnection(CloseMode.GRACEFUL);
+ }
+ }
+ }
+
synchronized ManagedHttpClientConnection getConnection(final HttpRoute route, final Object state) throws IOException {
Asserts.check(!this.closed.get(), "Connection manager has been shut down");
if (LOG.isDebugEnabled()) {
@@ -240,6 +264,7 @@
this.route = route;
this.state = state;
checkExpiry();
+ validate();
if (this.conn == null) {
this.conn = this.connFactory.createConnection(null);
} else {
@@ -365,6 +390,27 @@
}
}
+ /**
+ * @see #setValidateAfterInactivity(TimeValue)
+ *
+ * @since 5.1
+ */
+ public TimeValue getValidateAfterInactivity() {
+ return validateAfterInactivity;
+ }
+
+ /**
+ * Defines period of inactivity after which persistent connections must
+ * be re-validated prior to being {@link #lease(String, HttpRoute, Object)} leased} to the consumer.
+ * Negative values passed to this method disable connection validation. This check helps
+ * detect connections that have become stale (half-closed) while kept inactive in the pool.
+ *
+ * @since 5.1
+ */
+ public void setValidateAfterInactivity(final TimeValue validateAfterInactivity) {
+ this.validateAfterInactivity = validateAfterInactivity;
+ }
+
class InternalConnectionEndpoint extends ConnectionEndpoint {
private final HttpRoute route;
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 4c8d6d4..13b3998 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
@@ -95,9 +95,6 @@
* Total time to live (TTL) set at construction time defines maximum life span
* of persistent connections regardless of their expiration setting. No persistent
* connection will be re-used past its TTL value.
- * <p>
- * Please note in contrast to 4.x no stale check is employed by default.
- * @see #setValidateAfterInactivity(TimeValue)
*
* @since 4.3
*/
@@ -206,6 +203,7 @@
}
this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE;
this.closed = new AtomicBoolean(false);
+ this.validateAfterInactivity = TimeValue.ofSeconds(2L);
}
@Internal
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 121127e..2dd506b 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
@@ -212,7 +212,9 @@
schemePortResolver,
dnsResolver,
connectionFactory);
- poolingmgr.setValidateAfterInactivity(this.validateAfterInactivity);
+ if (validateAfterInactivity != null) {
+ poolingmgr.setValidateAfterInactivity(validateAfterInactivity);
+ }
if (defaultSocketConfig != null) {
poolingmgr.setDefaultSocketConfig(defaultSocketConfig);
}
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 cafe7e4..c1dd8ae 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
@@ -201,7 +201,9 @@
timeToLive,
schemePortResolver,
dnsResolver);
- poolingmgr.setValidateAfterInactivity(this.validateAfterInactivity);
+ if (validateAfterInactivity != null) {
+ poolingmgr.setValidateAfterInactivity(validateAfterInactivity);
+ }
if (maxConnTotal > 0) {
poolingmgr.setMaxTotal(maxConnTotal);
}