--- 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();