src/java.net.http/share/classes/jdk/internal/net/http/ConnectionPool.java
branchhttp-client-branch
changeset 56538 9bdcfc7d2b9c
parent 56451 9585061fdb04
child 56795 03ece2518428
equal deleted inserted replaced
56537:5665667a080c 56538:9bdcfc7d2b9c
    51  */
    51  */
    52 final class ConnectionPool {
    52 final class ConnectionPool {
    53 
    53 
    54     static final long KEEP_ALIVE = Utils.getIntegerNetProperty(
    54     static final long KEEP_ALIVE = Utils.getIntegerNetProperty(
    55             "jdk.httpclient.keepalive.timeout", 1200); // seconds
    55             "jdk.httpclient.keepalive.timeout", 1200); // seconds
       
    56     static final long MAX_POOL_SIZE = Utils.getIntegerNetProperty(
       
    57             "jdk.httpclient.connectionPoolSize", 0); // unbounded
    56     final Logger debug = Utils.getDebugLogger(this::dbgString, Utils.DEBUG);
    58     final Logger debug = Utils.getDebugLogger(this::dbgString, Utils.DEBUG);
    57 
    59 
    58     // Pools of idle connections
    60     // Pools of idle connections
    59 
    61 
    60     private final HashMap<CacheKey,LinkedList<HttpConnection>> plainPool;
    62     private final HashMap<CacheKey,LinkedList<HttpConnection>> plainPool;
   158         // since we don't want to trigger the cleanup if the connection
   160         // since we don't want to trigger the cleanup if the connection
   159         // is not in the pool.
   161         // is not in the pool.
   160         CleanupTrigger cleanup = registerCleanupTrigger(conn);
   162         CleanupTrigger cleanup = registerCleanupTrigger(conn);
   161 
   163 
   162         // it's possible that cleanup may have been called.
   164         // it's possible that cleanup may have been called.
       
   165         HttpConnection toClose = null;
   163         synchronized(this) {
   166         synchronized(this) {
   164             if (cleanup.isDone()) {
   167             if (cleanup.isDone()) {
   165                 return;
   168                 return;
   166             } else if (stopped) {
   169             } else if (stopped) {
   167                 conn.close();
   170                 conn.close();
   168                 return;
   171                 return;
   169             }
   172             }
       
   173             if (MAX_POOL_SIZE > 0 && expiryList.size() >= MAX_POOL_SIZE) {
       
   174                 toClose = expiryList.removeOldest();
       
   175                 if (toClose != null) removeFromPool(toClose);
       
   176             }
   170             if (conn instanceof PlainHttpConnection) {
   177             if (conn instanceof PlainHttpConnection) {
   171                 putConnection(conn, plainPool);
   178                 putConnection(conn, plainPool);
   172             } else {
   179             } else {
   173                 assert conn.isSecure();
   180                 assert conn.isSecure();
   174                 putConnection(conn, sslPool);
   181                 putConnection(conn, sslPool);
   175             }
   182             }
   176             expiryList.add(conn, now, keepAlive);
   183             expiryList.add(conn, now, keepAlive);
       
   184         }
       
   185         if (toClose != null) {
       
   186             if (debug.on()) {
       
   187                 debug.log("Maximum pool size reached: removing oldest connection %s",
       
   188                           toClose.dbgString());
       
   189             }
       
   190             close(toClose);
   177         }
   191         }
   178         //System.out.println("Return to pool: " + conn);
   192         //System.out.println("Return to pool: " + conn);
   179     }
   193     }
   180 
   194 
   181     private CleanupTrigger registerCleanupTrigger(HttpConnection conn) {
   195     private CleanupTrigger registerCleanupTrigger(HttpConnection conn) {
   312      */
   326      */
   313     private static final class ExpiryList {
   327     private static final class ExpiryList {
   314         private final LinkedList<ExpiryEntry> list = new LinkedList<>();
   328         private final LinkedList<ExpiryEntry> list = new LinkedList<>();
   315         private volatile boolean mayContainEntries;
   329         private volatile boolean mayContainEntries;
   316 
   330 
       
   331         int size() { return list.size(); }
       
   332 
   317         // A loosely accurate boolean whose value is computed
   333         // A loosely accurate boolean whose value is computed
   318         // at the end of each operation performed on ExpiryList;
   334         // at the end of each operation performed on ExpiryList;
   319         // Does not require synchronizing on the ConnectionPool.
   335         // Does not require synchronizing on the ConnectionPool.
   320         boolean purgeMaybeRequired() {
   336         boolean purgeMaybeRequired() {
   321             return mayContainEntries;
   337             return mayContainEntries;
   325         // should only be called while holding a synchronization
   341         // should only be called while holding a synchronization
   326         // lock on the ConnectionPool
   342         // lock on the ConnectionPool
   327         Optional<Instant> nextExpiryDeadline() {
   343         Optional<Instant> nextExpiryDeadline() {
   328             if (list.isEmpty()) return Optional.empty();
   344             if (list.isEmpty()) return Optional.empty();
   329             else return Optional.of(list.getLast().expiry);
   345             else return Optional.of(list.getLast().expiry);
       
   346         }
       
   347 
       
   348         // should only be called while holding a synchronization
       
   349         // lock on the ConnectionPool
       
   350         HttpConnection removeOldest() {
       
   351             ExpiryEntry entry = list.pollLast();
       
   352             return entry == null ? null : entry.connection;
   330         }
   353         }
   331 
   354 
   332         // should only be called while holding a synchronization
   355         // should only be called while holding a synchronization
   333         // lock on the ConnectionPool
   356         // lock on the ConnectionPool
   334         void add(HttpConnection conn) {
   357         void add(HttpConnection conn) {
   417             list.clear();
   440             list.clear();
   418             mayContainEntries = false;
   441             mayContainEntries = false;
   419         }
   442         }
   420     }
   443     }
   421 
   444 
       
   445     // Remove a connection from the pool.
       
   446     // should only be called while holding a synchronization
       
   447     // lock on the ConnectionPool
       
   448     private void removeFromPool(HttpConnection c) {
       
   449         assert Thread.holdsLock(this);
       
   450         if (c instanceof PlainHttpConnection) {
       
   451             removeFromPool(c, plainPool);
       
   452         } else {
       
   453             assert c.isSecure();
       
   454             removeFromPool(c, sslPool);
       
   455         }
       
   456     }
       
   457 
       
   458     // Used by tests
       
   459     synchronized boolean contains(HttpConnection c) {
       
   460         final CacheKey key = c.cacheKey();
       
   461         List<HttpConnection> list;
       
   462         if ((list = plainPool.get(key)) != null) {
       
   463             if (list.contains(c)) return true;
       
   464         }
       
   465         if ((list = sslPool.get(key)) != null) {
       
   466             if (list.contains(c)) return true;
       
   467         }
       
   468         return false;
       
   469     }
       
   470 
   422     void cleanup(HttpConnection c, Throwable error) {
   471     void cleanup(HttpConnection c, Throwable error) {
   423         if (debug.on())
   472         if (debug.on())
   424             debug.log("%s : ConnectionPool.cleanup(%s)",
   473             debug.log("%s : ConnectionPool.cleanup(%s)",
   425                     String.valueOf(c.getConnectionFlow()), error);
   474                     String.valueOf(c.getConnectionFlow()), error);
   426         synchronized(this) {
   475         synchronized(this) {
   427             if (c instanceof PlainHttpConnection) {
   476             removeFromPool(c);
   428                 removeFromPool(c, plainPool);
       
   429             } else {
       
   430                 assert c.isSecure();
       
   431                 removeFromPool(c, sslPool);
       
   432             }
       
   433             expiryList.remove(c);
   477             expiryList.remove(c);
   434         }
   478         }
   435         c.close();
   479         c.close();
   436     }
   480     }
   437 
   481