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 2019/09/09 15:38:12 UTC

[httpcomponents-core] 01/01: HTTPCORE-596: connection pools to use an optional callback to close out disposed connections

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

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

commit eb9d7c4dc6886ce7bbcaaa3af6c8d54c4c22a0ee
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sat Sep 7 13:01:45 2019 +0200

    HTTPCORE-596: connection pools to use an optional callback to close out disposed connections
---
 .../impl/nio/bootstrap/H2RequesterBootstrap.java   |  3 ++
 .../org/apache/hc/core5/http/HttpConnection.java   | 28 ++---------
 .../apache/hc/core5/http/SocketModalCloseable.java | 54 ++++++++++++++++++++
 .../impl/bootstrap/AsyncRequesterBootstrap.java    |  3 ++
 .../http/impl/bootstrap/RequesterBootstrap.java    |  3 ++
 .../hc/core5/pool/DefaultDisposalCallback.java     | 58 ++++++++++++++++++++++
 .../org/apache/hc/core5/pool/DisposalCallback.java | 45 +++++++++++++++++
 .../java/org/apache/hc/core5/pool/LaxConnPool.java | 30 ++++++++---
 .../java/org/apache/hc/core5/pool/PoolEntry.java   | 33 ++++++++++--
 .../org/apache/hc/core5/pool/StrictConnPool.java   | 27 ++++++++--
 .../org/apache/hc/core5/reactor/IOSession.java     |  4 +-
 11 files changed, 247 insertions(+), 41 deletions(-)

diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
index 7a7203b..5a703b0 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
@@ -53,6 +53,7 @@ import org.apache.hc.core5.http2.impl.nio.H2StreamListener;
 import org.apache.hc.core5.http2.nio.support.DefaultAsyncPushConsumerFactory;
 import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy;
 import org.apache.hc.core5.pool.ConnPoolListener;
+import org.apache.hc.core5.pool.DefaultDisposalCallback;
 import org.apache.hc.core5.pool.LaxConnPool;
 import org.apache.hc.core5.pool.ManagedConnPool;
 import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
@@ -299,6 +300,7 @@ public class H2RequesterBootstrap {
                         defaultMaxPerRoute > 0 ? defaultMaxPerRoute : 20,
                         timeToLive,
                         poolReusePolicy,
+                        new DefaultDisposalCallback<IOSession>(),
                         connPoolListener);
                 break;
             case STRICT:
@@ -308,6 +310,7 @@ public class H2RequesterBootstrap {
                         maxTotal > 0 ? maxTotal : 50,
                         timeToLive,
                         poolReusePolicy,
+                        new DefaultDisposalCallback<IOSession>(),
                         connPoolListener);
                 break;
         }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/HttpConnection.java b/httpcore5/src/main/java/org/apache/hc/core5/http/HttpConnection.java
index 8c3eaf1..5b0e330 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/HttpConnection.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/HttpConnection.java
@@ -32,15 +32,12 @@ import java.net.SocketAddress;
 
 import javax.net.ssl.SSLSession;
 
-import org.apache.hc.core5.io.ModalCloseable;
-import org.apache.hc.core5.util.Timeout;
-
 /**
  * A generic HTTP connection, useful on client and server side.
  *
  * @since 4.0
  */
-public interface HttpConnection extends ModalCloseable {
+public interface HttpConnection extends SocketModalCloseable {
 
     /**
      * Closes this connection gracefully. This method will attempt to flush the internal output
@@ -66,14 +63,6 @@ public interface HttpConnection extends ModalCloseable {
     SocketAddress getLocalAddress();
 
     /**
-     * Returns this connection's protocol version or {@code null} if unknown.
-     *
-     * @return this connection's protocol version or {@code null} if unknown.
-     * @since 5.0
-     */
-    ProtocolVersion getProtocolVersion();
-
-    /**
      * Returns this connection's remote address or {@code null} if it is not connected yet or
      * unconnected.
      *
@@ -84,19 +73,12 @@ public interface HttpConnection extends ModalCloseable {
     SocketAddress getRemoteAddress();
 
     /**
-     * Returns the socket timeout value.
-     *
-     * @return timeout value.
-     */
-    Timeout getSocketTimeout();
-
-    /**
-     * Sets the socket timeout value.
+     * Returns this connection's protocol version or {@code null} if unknown.
      *
-     * @param timeout
-     *            timeout value
+     * @return this connection's protocol version or {@code null} if unknown.
+     * @since 5.0
      */
-    void setSocketTimeout(Timeout timeout);
+    ProtocolVersion getProtocolVersion();
 
     /**
      * Returns this connection's SSL session or {@code null} if TLS has not been activated.
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/SocketModalCloseable.java b/httpcore5/src/main/java/org/apache/hc/core5/http/SocketModalCloseable.java
new file mode 100644
index 0000000..2597ecc
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/SocketModalCloseable.java
@@ -0,0 +1,54 @@
+/*
+ * ====================================================================
+ * 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.core5.http;
+
+import org.apache.hc.core5.io.ModalCloseable;
+import org.apache.hc.core5.util.Timeout;
+
+/**
+ * A generic {@link ModalCloseable} backed by a network socket.
+ *
+ * @since 5.0
+ */
+public interface SocketModalCloseable extends ModalCloseable {
+
+    /**
+     * Returns the socket timeout value.
+     *
+     * @return timeout value.
+     */
+    Timeout getSocketTimeout();
+
+    /**
+     * Sets the socket timeout value.
+     *
+     * @param timeout timeout value
+     */
+    void setSocketTimeout(Timeout timeout);
+
+}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequesterBootstrap.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequesterBootstrap.java
index 5f9c95d..4da60ae 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequesterBootstrap.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncRequesterBootstrap.java
@@ -41,6 +41,7 @@ import org.apache.hc.core5.http.nio.ssl.BasicClientTlsStrategy;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.pool.ConnPoolListener;
+import org.apache.hc.core5.pool.DefaultDisposalCallback;
 import org.apache.hc.core5.pool.LaxConnPool;
 import org.apache.hc.core5.pool.ManagedConnPool;
 import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
@@ -226,6 +227,7 @@ public class AsyncRequesterBootstrap {
                         defaultMaxPerRoute > 0 ? defaultMaxPerRoute : 20,
                         timeToLive,
                         poolReusePolicy,
+                        new DefaultDisposalCallback<IOSession>(),
                         connPoolListener);
                 break;
             case STRICT:
@@ -235,6 +237,7 @@ public class AsyncRequesterBootstrap {
                         maxTotal > 0 ? maxTotal : 50,
                         timeToLive,
                         poolReusePolicy,
+                        new DefaultDisposalCallback<IOSession>(),
                         connPoolListener);
                 break;
         }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/RequesterBootstrap.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/RequesterBootstrap.java
index 929bfdc..fd9268b 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/RequesterBootstrap.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/RequesterBootstrap.java
@@ -49,6 +49,7 @@ import org.apache.hc.core5.http.io.ssl.DefaultTlsSetupHandler;
 import org.apache.hc.core5.http.io.ssl.SSLSessionVerifier;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.pool.ConnPoolListener;
+import org.apache.hc.core5.pool.DefaultDisposalCallback;
 import org.apache.hc.core5.pool.LaxConnPool;
 import org.apache.hc.core5.pool.ManagedConnPool;
 import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
@@ -188,6 +189,7 @@ public class RequesterBootstrap {
                         defaultMaxPerRoute > 0 ? defaultMaxPerRoute : 20,
                         timeToLive,
                         poolReusePolicy,
+                        new DefaultDisposalCallback<HttpClientConnection>(),
                         connPoolListener);
                 break;
             case STRICT:
@@ -197,6 +199,7 @@ public class RequesterBootstrap {
                         maxTotal > 0 ? maxTotal : 50,
                         timeToLive,
                         poolReusePolicy,
+                        new DefaultDisposalCallback<HttpClientConnection>(),
                         connPoolListener);
                 break;
         }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/pool/DefaultDisposalCallback.java b/httpcore5/src/main/java/org/apache/hc/core5/pool/DefaultDisposalCallback.java
new file mode 100644
index 0000000..3c964b8
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/pool/DefaultDisposalCallback.java
@@ -0,0 +1,58 @@
+/*
+ * ====================================================================
+ * 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.core5.pool;
+
+import org.apache.hc.core5.annotation.Internal;
+import org.apache.hc.core5.http.SocketModalCloseable;
+import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.hc.core5.util.Timeout;
+
+/**
+ * Default implementation of {@link DisposalCallback}.
+ *
+ * @param <T> socket based connection type.
+ *
+ * @since 5.0
+ */
+@Internal
+public final class DefaultDisposalCallback<T extends SocketModalCloseable> implements DisposalCallback<T> {
+
+    private final static Timeout DEFAULT_CLOSE_TIMEOUT = Timeout.ofSeconds(1L);
+
+    @Override
+    public void execute(final SocketModalCloseable closeable, final CloseMode closeMode) {
+        final Timeout socketTimeout = closeable.getSocketTimeout();
+        if (socketTimeout == null ||
+                socketTimeout.compareTo(TimeValue.ZERO_MILLISECONDS) <= 0 ||
+                socketTimeout.compareTo(DEFAULT_CLOSE_TIMEOUT) > 0) {
+            closeable.setSocketTimeout(DEFAULT_CLOSE_TIMEOUT);
+        }
+        closeable.close(closeMode);
+    }
+
+}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/pool/DisposalCallback.java b/httpcore5/src/main/java/org/apache/hc/core5/pool/DisposalCallback.java
new file mode 100644
index 0000000..d944037
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/pool/DisposalCallback.java
@@ -0,0 +1,45 @@
+/*
+ * ====================================================================
+ * 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.core5.pool;
+
+import org.apache.hc.core5.annotation.Internal;
+import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.io.ModalCloseable;
+
+/**
+ * Represents a customizable disposal strategy for {@link ModalCloseable} instances.
+ *
+ * @param <T> process or connection type.
+ *
+ * @since 5.0
+ */
+@Internal
+public interface DisposalCallback<T extends ModalCloseable> {
+
+    void execute(final T closeable, CloseMode closeMode);
+
+}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/pool/LaxConnPool.java b/httpcore5/src/main/java/org/apache/hc/core5/pool/LaxConnPool.java
index bab4ce4..4594fe3 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/pool/LaxConnPool.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/pool/LaxConnPool.java
@@ -35,9 +35,9 @@ import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicMarkableReference;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicMarkableReference;
 
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.Experimental;
@@ -69,8 +69,9 @@ import org.apache.hc.core5.util.Timeout;
 public class LaxConnPool<T, C extends ModalCloseable> implements ManagedConnPool<T, C> {
 
     private final TimeValue timeToLive;
-    private final ConnPoolListener<T> connPoolListener;
     private final PoolReusePolicy policy;
+    private final DisposalCallback<C> disposalCallback;
+    private final ConnPoolListener<T> connPoolListener;
     private final ConcurrentMap<T, PerRoutePool<T, C>> routeToPool;
     private final AtomicBoolean isShutDown;
 
@@ -83,19 +84,32 @@ public class LaxConnPool<T, C extends ModalCloseable> implements ManagedConnPool
             final int defaultMaxPerRoute,
             final TimeValue timeToLive,
             final PoolReusePolicy policy,
+            final DisposalCallback<C> disposalCallback,
             final ConnPoolListener<T> connPoolListener) {
         super();
         Args.positive(defaultMaxPerRoute, "Max per route value");
         this.timeToLive = TimeValue.defaultsToNegativeOneMillisecond(timeToLive);
-        this.connPoolListener = connPoolListener;
         this.policy = policy != null ? policy : PoolReusePolicy.LIFO;
+        this.disposalCallback = disposalCallback;
+        this.connPoolListener = connPoolListener;
         this.routeToPool = new ConcurrentHashMap<>();
         this.isShutDown = new AtomicBoolean(false);
         this.defaultMaxPerRoute = defaultMaxPerRoute;
     }
 
+    /**
+     * @since 5.0
+     */
+    public LaxConnPool(
+            final int defaultMaxPerRoute,
+            final TimeValue timeToLive,
+            final PoolReusePolicy policy,
+            final ConnPoolListener<T> connPoolListener) {
+        this(defaultMaxPerRoute, timeToLive, policy, null, connPoolListener);
+    }
+
     public LaxConnPool(final int defaultMaxPerRoute) {
-        this(defaultMaxPerRoute, TimeValue.NEG_ONE_MILLISECONDS, PoolReusePolicy.LIFO, null);
+        this(defaultMaxPerRoute, TimeValue.NEG_ONE_MILLISECONDS, PoolReusePolicy.LIFO, null, null);
     }
 
     public boolean isShutdown() {
@@ -127,6 +141,7 @@ public class LaxConnPool<T, C extends ModalCloseable> implements ManagedConnPool
                     timeToLive,
                     policy,
                     this,
+                    disposalCallback,
                     connPoolListener);
             routePool = routeToPool.putIfAbsent(route, newRoutePool);
             if (routePool == null) {
@@ -344,8 +359,9 @@ public class LaxConnPool<T, C extends ModalCloseable> implements ManagedConnPool
         private final T route;
         private final TimeValue timeToLive;
         private final PoolReusePolicy policy;
-        private final ConnPoolStats<T> connPoolStats;
+        private final DisposalCallback<C> disposalCallback;
         private final ConnPoolListener<T> connPoolListener;
+        private final ConnPoolStats<T> connPoolStats;
         private final ConcurrentMap<PoolEntry<T, C>, Boolean> leased;
         private final Deque<AtomicMarkableReference<PoolEntry<T, C>>> available;
         private final Deque<LeaseRequest<T, C>> pending;
@@ -361,12 +377,14 @@ public class LaxConnPool<T, C extends ModalCloseable> implements ManagedConnPool
                 final TimeValue timeToLive,
                 final PoolReusePolicy policy,
                 final ConnPoolStats<T> connPoolStats,
+                final DisposalCallback<C> disposalCallback,
                 final ConnPoolListener<T> connPoolListener) {
             super();
             this.route = route;
             this.timeToLive = timeToLive;
             this.policy = policy;
             this.connPoolStats = connPoolStats;
+            this.disposalCallback = disposalCallback;
             this.connPoolListener = connPoolListener;
             this.leased = new ConcurrentHashMap<>();
             this.available = new ConcurrentLinkedDeque<>();
@@ -401,7 +419,7 @@ public class LaxConnPool<T, C extends ModalCloseable> implements ManagedConnPool
                 prev = allocated.get();
                 next = (prev<poolmax)? prev+1 : prev;
             } while (!allocated.compareAndSet(prev, next));
-            return (prev < next)? new PoolEntry<T,C>(route, timeToLive) : null;
+            return (prev < next)? new PoolEntry<T,C>(route, timeToLive, disposalCallback) : null;
         }
 
         private void deallocatePoolEntry() {
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/pool/PoolEntry.java b/httpcore5/src/main/java/org/apache/hc/core5/pool/PoolEntry.java
index 60c11d7..8a28266 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/pool/PoolEntry.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/pool/PoolEntry.java
@@ -52,6 +52,7 @@ public final class PoolEntry<T, C extends ModalCloseable> {
     private final T route;
     private final TimeValue timeToLive;
     private final AtomicReference<C> connRef;
+    private final DisposalCallback<C> disposalCallback;
     private final Supplier<Long> currentTimeSupplier;
 
     private volatile Object state;
@@ -60,16 +61,30 @@ public final class PoolEntry<T, C extends ModalCloseable> {
     private volatile Deadline expiryDeadline = Deadline.MIN_VALUE;
     private volatile Deadline validityDeadline = Deadline.MIN_VALUE;
 
-    PoolEntry(final T route, final TimeValue timeToLive, final Supplier<Long> currentTimeSupplier) {
+    PoolEntry(final T route, final TimeValue timeToLive, final DisposalCallback<C> disposalCallback,
+              final Supplier<Long> currentTimeSupplier) {
         super();
         this.route = Args.notNull(route, "Route");
         this.timeToLive = TimeValue.defaultsToNegativeOneMillisecond(timeToLive);
         this.connRef = new AtomicReference<>(null);
+        this.disposalCallback = disposalCallback;
         this.currentTimeSupplier = currentTimeSupplier;
     }
 
-    long getCurrentTime() {
-        return currentTimeSupplier != null ? currentTimeSupplier.get() : System.currentTimeMillis();
+    PoolEntry(final T route, final TimeValue timeToLive, final Supplier<Long> currentTimeSupplier) {
+        this(route, timeToLive, null, currentTimeSupplier);
+    }
+
+    /**
+     * Creates new {@code PoolEntry} instance.
+     *
+     * @param route route to the opposite endpoint.
+     * @param timeToLive maximum time to live. May be zero if the connection
+     *   does not have an expiry deadline.
+     * @param disposalCallback callback invoked before connection disposal.
+     */
+    public PoolEntry(final T route, final TimeValue timeToLive, final DisposalCallback<C> disposalCallback) {
+        this(route, timeToLive, disposalCallback, null);
     }
 
     /**
@@ -80,13 +95,17 @@ public final class PoolEntry<T, C extends ModalCloseable> {
      *   does not have an expiry deadline.
      */
     public PoolEntry(final T route, final TimeValue timeToLive) {
-        this(route, timeToLive, null);
+        this(route, timeToLive, null, null);
     }
 
     public PoolEntry(final T route) {
         this(route, null);
     }
 
+    long getCurrentTime() {
+        return currentTimeSupplier != null ? currentTimeSupplier.get() : System.currentTimeMillis();
+    }
+
     public T getRoute() {
         return this.route;
     }
@@ -148,7 +167,11 @@ public final class PoolEntry<T, C extends ModalCloseable> {
             this.updated = 0;
             this.expiryDeadline = Deadline.MIN_VALUE;
             this.validityDeadline = Deadline.MIN_VALUE;
-            connection.close(closeMode);
+            if (this.disposalCallback != null) {
+                this.disposalCallback.execute(connection, closeMode);
+            } else {
+                connection.close(closeMode);
+            }
         }
     }
 
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java b/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
index 1b9e8b2..c6422d3 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/pool/StrictConnPool.java
@@ -66,8 +66,9 @@ import org.apache.hc.core5.util.Timeout;
 public class StrictConnPool<T, C extends ModalCloseable> implements ManagedConnPool<T, C> {
 
     private final TimeValue timeToLive;
-    private final ConnPoolListener<T> connPoolListener;
     private final PoolReusePolicy policy;
+    private final DisposalCallback<C> disposalCallback;
+    private final ConnPoolListener<T> connPoolListener;
     private final Map<T, PerRoutePool<T, C>> routeToPool;
     private final LinkedList<LeaseRequest<T, C>> leasingRequests;
     private final Set<PoolEntry<T, C>> leased;
@@ -88,13 +89,15 @@ public class StrictConnPool<T, C extends ModalCloseable> implements ManagedConnP
             final int maxTotal,
             final TimeValue timeToLive,
             final PoolReusePolicy policy,
+            final DisposalCallback<C> disposalCallback,
             final ConnPoolListener<T> connPoolListener) {
         super();
         Args.positive(defaultMaxPerRoute, "Max per route value");
         Args.positive(maxTotal, "Max total value");
         this.timeToLive = TimeValue.defaultsToNegativeOneMillisecond(timeToLive);
-        this.connPoolListener = connPoolListener;
         this.policy = policy != null ? policy : PoolReusePolicy.LIFO;
+        this.disposalCallback = disposalCallback;
+        this.connPoolListener = connPoolListener;
         this.routeToPool = new HashMap<>();
         this.leasingRequests = new LinkedList<>();
         this.leased = new HashSet<>();
@@ -107,6 +110,18 @@ public class StrictConnPool<T, C extends ModalCloseable> implements ManagedConnP
         this.maxTotal = maxTotal;
     }
 
+    /**
+     * @since 5.0
+     */
+    public StrictConnPool(
+            final int defaultMaxPerRoute,
+            final int maxTotal,
+            final TimeValue timeToLive,
+            final PoolReusePolicy policy,
+            final ConnPoolListener<T> connPoolListener) {
+        this(defaultMaxPerRoute, maxTotal, timeToLive, policy, null, connPoolListener);
+    }
+
     public StrictConnPool(final int defaultMaxPerRoute, final int maxTotal) {
         this(defaultMaxPerRoute, maxTotal, TimeValue.NEG_ONE_MILLISECONDS, PoolReusePolicy.LIFO, null);
     }
@@ -142,7 +157,7 @@ public class StrictConnPool<T, C extends ModalCloseable> implements ManagedConnP
     private PerRoutePool<T, C> getPool(final T route) {
         PerRoutePool<T, C> pool = this.routeToPool.get(route);
         if (pool == null) {
-            pool = new PerRoutePool<>(route);
+            pool = new PerRoutePool<>(route, this.disposalCallback);
             this.routeToPool.put(route, pool);
         }
         return pool;
@@ -704,10 +719,12 @@ public class StrictConnPool<T, C extends ModalCloseable> implements ManagedConnP
         private final T route;
         private final Set<PoolEntry<T, C>> leased;
         private final LinkedList<PoolEntry<T, C>> available;
+        private final DisposalCallback<C> disposalCallback;
 
-        PerRoutePool(final T route) {
+        PerRoutePool(final T route, final DisposalCallback<C> disposalCallback) {
             super();
             this.route = route;
+            this.disposalCallback = disposalCallback;
             this.leased = new HashSet<>();
             this.available = new LinkedList<>();
         }
@@ -771,7 +788,7 @@ public class StrictConnPool<T, C extends ModalCloseable> implements ManagedConnP
         }
 
         public PoolEntry<T, C> createEntry(final TimeValue timeToLive) {
-            final PoolEntry<T, C> entry = new PoolEntry<>(this.route, timeToLive);
+            final PoolEntry<T, C> entry = new PoolEntry<>(this.route, timeToLive, disposalCallback);
             this.leased.add(entry);
             return entry;
         }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSession.java b/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSession.java
index 30317da..f74060e 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSession.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/reactor/IOSession.java
@@ -31,7 +31,7 @@ import java.net.SocketAddress;
 import java.nio.channels.ByteChannel;
 import java.util.concurrent.locks.Lock;
 
-import org.apache.hc.core5.io.ModalCloseable;
+import org.apache.hc.core5.http.SocketModalCloseable;
 import org.apache.hc.core5.util.Identifiable;
 import org.apache.hc.core5.util.Timeout;
 
@@ -51,7 +51,7 @@ import org.apache.hc.core5.util.Timeout;
  *
  * @since 4.0
  */
-public interface IOSession extends ModalCloseable, Identifiable {
+public interface IOSession extends SocketModalCloseable, Identifiable {
 
     int ACTIVE       = 0;
     int CLOSING      = 1;