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 efc263c16..4b44db4ce 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 @@ -642,6 +642,9 @@ public void enumAvailable(final Callback> callback) { } releaseSeqNum.incrementAndGet(); servicePendingRequests(RequestServiceStrategy.ALL); + // servicePendingRequests stops at the first request it cannot serve, + // so expired requests queued behind it would stay pending. + validatePendingRequests(); } public void enumLeased(final Callback> callback) { diff --git a/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolPendingRequestExpiry.java b/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolPendingRequestExpiry.java index ef4927e2b..8c40596e4 100644 --- a/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolPendingRequestExpiry.java +++ b/httpcore5/src/test/java/org/apache/hc/core5/pool/TestPoolPendingRequestExpiry.java @@ -26,6 +26,7 @@ */ package org.apache.hc.core5.pool; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -92,6 +93,33 @@ void closeIdleFailsExpiredPendingRequest(final PoolConcurrencyPolicy policy) thr } } + @ParameterizedTest + @EnumSource(PoolConcurrencyPolicy.class) + void closeExpiredFailsExpiredRequestQueuedBehindLiveRequest(final PoolConcurrencyPolicy policy) throws Exception { + final TestingClock clock = new TestingClock(0L); + final ManagedConnPool pool = + PoolTestSupport.createPool(policy, 1, 1, clock); + try { + final PoolEntry leased = + pool.lease("r1", null, Timeout.ofSeconds(1), null).get(1, TimeUnit.SECONDS); + assertNotNull(leased); + final Future> live = + pool.lease("r1", null, Timeout.ofMinutes(1), null); + final Future> expired = + pool.lease("r1", null, Timeout.ofMilliseconds(100), null); + + clock.advanceMillis(10_000L); + pool.closeExpired(); + + final ExecutionException ex = assertThrows(ExecutionException.class, + () -> expired.get(5, TimeUnit.SECONDS)); + assertInstanceOf(TimeoutException.class, ex.getCause()); + assertFalse(live.isDone(), "Request that has not expired must stay pending"); + } finally { + pool.close(CloseMode.IMMEDIATE); + } + } + @ParameterizedTest @EnumSource(PoolConcurrencyPolicy.class) void expiredPendingRequestIsDoneAfterEviction(final PoolConcurrencyPolicy policy) throws Exception {