src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java
changeset 50681 4254bed3c09d
parent 49765 ee6f7a61f3a5
child 56795 03ece2518428
child 58055 734f7711f87c
--- a/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java	Wed Jun 20 17:15:16 2018 +0200
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java	Wed Jun 20 09:05:57 2018 -0700
@@ -53,6 +53,8 @@
 
     static final long KEEP_ALIVE = Utils.getIntegerNetProperty(
             "jdk.httpclient.keepalive.timeout", 1200); // seconds
+    static final long MAX_POOL_SIZE = Utils.getIntegerNetProperty(
+            "jdk.httpclient.connectionPoolSize", 0); // unbounded
     final Logger debug = Utils.getDebugLogger(this::dbgString, Utils.DEBUG);
 
     // Pools of idle connections
@@ -160,6 +162,7 @@
         CleanupTrigger cleanup = registerCleanupTrigger(conn);
 
         // it's possible that cleanup may have been called.
+        HttpConnection toClose = null;
         synchronized(this) {
             if (cleanup.isDone()) {
                 return;
@@ -167,6 +170,10 @@
                 conn.close();
                 return;
             }
+            if (MAX_POOL_SIZE > 0 && expiryList.size() >= MAX_POOL_SIZE) {
+                toClose = expiryList.removeOldest();
+                if (toClose != null) removeFromPool(toClose);
+            }
             if (conn instanceof PlainHttpConnection) {
                 putConnection(conn, plainPool);
             } else {
@@ -175,6 +182,13 @@
             }
             expiryList.add(conn, now, keepAlive);
         }
+        if (toClose != null) {
+            if (debug.on()) {
+                debug.log("Maximum pool size reached: removing oldest connection %s",
+                          toClose.dbgString());
+            }
+            close(toClose);
+        }
         //System.out.println("Return to pool: " + conn);
     }
 
@@ -314,6 +328,8 @@
         private final LinkedList<ExpiryEntry> list = new LinkedList<>();
         private volatile boolean mayContainEntries;
 
+        int size() { return list.size(); }
+
         // A loosely accurate boolean whose value is computed
         // at the end of each operation performed on ExpiryList;
         // Does not require synchronizing on the ConnectionPool.
@@ -331,6 +347,13 @@
 
         // should only be called while holding a synchronization
         // lock on the ConnectionPool
+        HttpConnection removeOldest() {
+            ExpiryEntry entry = list.pollLast();
+            return entry == null ? null : entry.connection;
+        }
+
+        // should only be called while holding a synchronization
+        // lock on the ConnectionPool
         void add(HttpConnection conn) {
             add(conn, Instant.now(), KEEP_ALIVE);
         }
@@ -419,17 +442,38 @@
         }
     }
 
+    // Remove a connection from the pool.
+    // should only be called while holding a synchronization
+    // lock on the ConnectionPool
+    private void removeFromPool(HttpConnection c) {
+        assert Thread.holdsLock(this);
+        if (c instanceof PlainHttpConnection) {
+            removeFromPool(c, plainPool);
+        } else {
+            assert c.isSecure();
+            removeFromPool(c, sslPool);
+        }
+    }
+
+    // Used by tests
+    synchronized boolean contains(HttpConnection c) {
+        final CacheKey key = c.cacheKey();
+        List<HttpConnection> list;
+        if ((list = plainPool.get(key)) != null) {
+            if (list.contains(c)) return true;
+        }
+        if ((list = sslPool.get(key)) != null) {
+            if (list.contains(c)) return true;
+        }
+        return false;
+    }
+
     void cleanup(HttpConnection c, Throwable error) {
         if (debug.on())
             debug.log("%s : ConnectionPool.cleanup(%s)",
                     String.valueOf(c.getConnectionFlow()), error);
         synchronized(this) {
-            if (c instanceof PlainHttpConnection) {
-                removeFromPool(c, plainPool);
-            } else {
-                assert c.isSecure();
-                removeFromPool(c, sslPool);
-            }
+            removeFromPool(c);
             expiryList.remove(c);
         }
         c.close();