Skip to content

Commit fe1e095

Browse files
committed
Regression: connection managers fail to take into account per route connection config when closing expired connections
1 parent 233a5bd commit fe1e095

File tree

4 files changed

+65
-11
lines changed

4 files changed

+65
-11
lines changed

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestConnectionManagement.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@
5858
import org.apache.hc.core5.util.TimeValue;
5959
import org.apache.hc.core5.util.Timeout;
6060
import org.junit.jupiter.api.Assertions;
61-
import org.junit.jupiter.api.Disabled;
6261
import org.junit.jupiter.api.Test;
6362
import org.junit.jupiter.api.extension.RegisterExtension;
6463

@@ -276,7 +275,7 @@ public void testCloseExpiredIdleConnections() throws Exception {
276275
connManager.close();
277276
}
278277

279-
@Test @Disabled
278+
@Test
280279
public void testCloseExpiredTTLConnections() throws Exception {
281280
final ClassicTestServer server = startServer();
282281
server.registerHandler("/random/*", new RandomHandler());
@@ -294,6 +293,7 @@ public void testCloseExpiredTTLConnections() throws Exception {
294293
);
295294

296295
final PoolingHttpClientConnectionManager connManager = testResources.connManager();
296+
connManager.setMaxTotal(1);
297297

298298
final HttpRoute route = new HttpRoute(target, null, false);
299299
final HttpContext context = new BasicHttpContext();

httpclient5/src/main/java/org/apache/hc/client5/http/config/ConnectionConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ protected ConnectionConfig clone() throws CloneNotSupportedException {
107107
public String toString() {
108108
final StringBuilder builder = new StringBuilder();
109109
builder.append("[");
110-
builder.append(", connectTimeout=").append(connectTimeout);
110+
builder.append("connectTimeout=").append(connectTimeout);
111111
builder.append(", socketTimeout=").append(socketTimeout);
112112
builder.append(", validateAfterInactivity=").append(validateAfterInactivity);
113113
builder.append(", timeToLive=").append(timeToLive);

httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/PoolingHttpClientConnectionManager.java

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,19 +188,33 @@ protected PoolingHttpClientConnectionManager(
188188
this.connectionOperator = Args.notNull(httpClientConnectionOperator, "Connection operator");
189189
switch (poolConcurrencyPolicy != null ? poolConcurrencyPolicy : PoolConcurrencyPolicy.STRICT) {
190190
case STRICT:
191-
this.pool = new StrictConnPool<>(
191+
this.pool = new StrictConnPool<HttpRoute, ManagedHttpClientConnection>(
192192
DEFAULT_MAX_CONNECTIONS_PER_ROUTE,
193193
DEFAULT_MAX_TOTAL_CONNECTIONS,
194194
timeToLive,
195195
poolReusePolicy,
196-
null);
196+
null) {
197+
198+
@Override
199+
public void closeExpired() {
200+
enumAvailable(e -> closeIfExpired(e));
201+
}
202+
203+
};
197204
break;
198205
case LAX:
199-
this.pool = new LaxConnPool<>(
206+
this.pool = new LaxConnPool<HttpRoute, ManagedHttpClientConnection>(
200207
DEFAULT_MAX_CONNECTIONS_PER_ROUTE,
201208
timeToLive,
202209
poolReusePolicy,
203-
null);
210+
null) {
211+
212+
@Override
213+
public void closeExpired() {
214+
enumAvailable(e -> closeIfExpired(e));
215+
}
216+
217+
};
204218
break;
205219
default:
206220
throw new IllegalArgumentException("Unexpected PoolConcurrencyPolicy value: " + poolConcurrencyPolicy);
@@ -570,6 +584,19 @@ public void setTlsConfigResolver(final Resolver<HttpHost, TlsConfig> tlsConfigRe
570584
this.tlsConfigResolver = tlsConfigResolver;
571585
}
572586

587+
void closeIfExpired(final PoolEntry<HttpRoute, ManagedHttpClientConnection> entry) {
588+
final long now = System.currentTimeMillis();
589+
if (entry.getExpiryDeadline().isBefore(now)) {
590+
entry.discardConnection(CloseMode.GRACEFUL);
591+
} else {
592+
final ConnectionConfig connectionConfig = resolveConnectionConfig(entry.getRoute());
593+
final TimeValue timeToLive = connectionConfig.getTimeToLive();
594+
if (timeToLive != null && Deadline.calculate(entry.getCreated(), timeToLive).isBefore(now)) {
595+
entry.discardConnection(CloseMode.GRACEFUL);
596+
}
597+
}
598+
}
599+
573600
/**
574601
* @deprecated Use custom {@link #setConnectionConfigResolver(Resolver)}
575602
*/

httpclient5/src/main/java/org/apache/hc/client5/http/impl/nio/PoolingAsyncClientConnectionManager.java

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,19 +172,33 @@ protected PoolingAsyncClientConnectionManager(
172172
this.connectionOperator = Args.notNull(connectionOperator, "Connection operator");
173173
switch (poolConcurrencyPolicy != null ? poolConcurrencyPolicy : PoolConcurrencyPolicy.STRICT) {
174174
case STRICT:
175-
this.pool = new StrictConnPool<>(
175+
this.pool = new StrictConnPool<HttpRoute, ManagedAsyncClientConnection>(
176176
DEFAULT_MAX_CONNECTIONS_PER_ROUTE,
177177
DEFAULT_MAX_TOTAL_CONNECTIONS,
178178
timeToLive,
179179
poolReusePolicy,
180-
null);
180+
null) {
181+
182+
@Override
183+
public void closeExpired() {
184+
enumAvailable(e -> closeIfExpired(e));
185+
}
186+
187+
};
181188
break;
182189
case LAX:
183-
this.pool = new LaxConnPool<>(
190+
this.pool = new LaxConnPool<HttpRoute, ManagedAsyncClientConnection>(
184191
DEFAULT_MAX_CONNECTIONS_PER_ROUTE,
185192
timeToLive,
186193
poolReusePolicy,
187-
null);
194+
null) {
195+
196+
@Override
197+
public void closeExpired() {
198+
enumAvailable(e -> closeIfExpired(e));
199+
}
200+
201+
};
188202
break;
189203
default:
190204
throw new IllegalArgumentException("Unexpected PoolConcurrencyPolicy value: " + poolConcurrencyPolicy);
@@ -619,6 +633,19 @@ public void setTlsConfigResolver(final Resolver<HttpHost, TlsConfig> tlsConfigRe
619633
this.tlsConfigResolver = tlsConfigResolver;
620634
}
621635

636+
void closeIfExpired(final PoolEntry<HttpRoute, ManagedAsyncClientConnection > entry) {
637+
final long now = System.currentTimeMillis();
638+
if (entry.getExpiryDeadline().isBefore(now)) {
639+
entry.discardConnection(CloseMode.GRACEFUL);
640+
} else {
641+
final ConnectionConfig connectionConfig = resolveConnectionConfig(entry.getRoute());
642+
final TimeValue timeToLive = connectionConfig.getTimeToLive();
643+
if (timeToLive != null && Deadline.calculate(entry.getCreated(), timeToLive).isBefore(now)) {
644+
entry.discardConnection(CloseMode.GRACEFUL);
645+
}
646+
}
647+
}
648+
622649
/**
623650
* @deprecated Use custom {@link #setConnectionConfigResolver(Resolver)}
624651
*/

0 commit comments

Comments
 (0)