diff -r 5665667a080c -r 9bdcfc7d2b9c src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java --- a/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java Mon May 07 14:48:59 2018 -0700 +++ b/src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java Wed May 09 16:45:54 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 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 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();