1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.http.impl.io;
28
29 import java.io.IOException;
30 import java.util.Set;
31 import java.util.concurrent.ExecutionException;
32 import java.util.concurrent.Future;
33 import java.util.concurrent.TimeoutException;
34 import java.util.concurrent.atomic.AtomicBoolean;
35 import java.util.concurrent.atomic.AtomicReference;
36 import java.util.concurrent.locks.ReentrantLock;
37
38 import org.apache.hc.client5.http.DnsResolver;
39 import org.apache.hc.client5.http.EndpointInfo;
40 import org.apache.hc.client5.http.HttpRoute;
41 import org.apache.hc.client5.http.SchemePortResolver;
42 import org.apache.hc.client5.http.config.ConnectionConfig;
43 import org.apache.hc.client5.http.config.TlsConfig;
44 import org.apache.hc.client5.http.impl.ConnPoolSupport;
45 import org.apache.hc.client5.http.impl.ConnectionHolder;
46 import org.apache.hc.client5.http.impl.ConnectionShutdownException;
47 import org.apache.hc.client5.http.impl.PrefixedIncrementingId;
48 import org.apache.hc.client5.http.io.ConnectionEndpoint;
49 import org.apache.hc.client5.http.io.HttpClientConnectionManager;
50 import org.apache.hc.client5.http.io.HttpClientConnectionOperator;
51 import org.apache.hc.client5.http.io.LeaseRequest;
52 import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
53 import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
54 import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
55 import org.apache.hc.core5.annotation.Contract;
56 import org.apache.hc.core5.annotation.Internal;
57 import org.apache.hc.core5.annotation.ThreadingBehavior;
58 import org.apache.hc.core5.function.Resolver;
59 import org.apache.hc.core5.http.ClassicHttpRequest;
60 import org.apache.hc.core5.http.ClassicHttpResponse;
61 import org.apache.hc.core5.http.HttpConnection;
62 import org.apache.hc.core5.http.HttpException;
63 import org.apache.hc.core5.http.HttpHost;
64 import org.apache.hc.core5.http.URIScheme;
65 import org.apache.hc.core5.http.config.Registry;
66 import org.apache.hc.core5.http.config.RegistryBuilder;
67 import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
68 import org.apache.hc.core5.http.io.HttpConnectionFactory;
69 import org.apache.hc.core5.http.io.SocketConfig;
70 import org.apache.hc.core5.http.protocol.HttpContext;
71 import org.apache.hc.core5.io.CloseMode;
72 import org.apache.hc.core5.pool.ConnPoolControl;
73 import org.apache.hc.core5.pool.DefaultDisposalCallback;
74 import org.apache.hc.core5.pool.LaxConnPool;
75 import org.apache.hc.core5.pool.ManagedConnPool;
76 import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
77 import org.apache.hc.core5.pool.PoolEntry;
78 import org.apache.hc.core5.pool.PoolReusePolicy;
79 import org.apache.hc.core5.pool.PoolStats;
80 import org.apache.hc.core5.pool.StrictConnPool;
81 import org.apache.hc.core5.util.Args;
82 import org.apache.hc.core5.util.Deadline;
83 import org.apache.hc.core5.util.Identifiable;
84 import org.apache.hc.core5.util.TimeValue;
85 import org.apache.hc.core5.util.Timeout;
86 import org.slf4j.Logger;
87 import org.slf4j.LoggerFactory;
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
108 public class PoolingHttpClientConnectionManager
109 implements HttpClientConnectionManager, ConnPoolControl<HttpRoute> {
110
111 private static final Logger LOG = LoggerFactory.getLogger(PoolingHttpClientConnectionManager.class);
112
113 public static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 25;
114 public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5;
115
116 private final HttpClientConnectionOperator connectionOperator;
117 private final ManagedConnPool<HttpRoute, ManagedHttpClientConnection> pool;
118 private final HttpConnectionFactory<ManagedHttpClientConnection> connFactory;
119 private final AtomicBoolean closed;
120
121 private volatile Resolver<HttpRoute, SocketConfig> socketConfigResolver;
122 private volatile Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
123 private volatile Resolver<HttpHost, TlsConfig> tlsConfigResolver;
124
125 public PoolingHttpClientConnectionManager() {
126 this(new DefaultHttpClientConnectionOperator(null, null,
127 RegistryBuilder.<TlsSocketStrategy>create()
128 .register(URIScheme.HTTPS.id, DefaultClientTlsStrategy.createDefault())
129 .build()),
130 PoolConcurrencyPolicy.STRICT,
131 PoolReusePolicy.LIFO,
132 TimeValue.NEG_ONE_MILLISECOND,
133 null);
134 }
135
136
137
138
139 @Deprecated
140 public PoolingHttpClientConnectionManager(
141 final Registry<org.apache.hc.client5.http.socket.ConnectionSocketFactory> socketFactoryRegistry) {
142 this(socketFactoryRegistry, null);
143 }
144
145
146
147
148 @Deprecated
149 public PoolingHttpClientConnectionManager(
150 final Registry<org.apache.hc.client5.http.socket.ConnectionSocketFactory> socketFactoryRegistry,
151 final HttpConnectionFactory<ManagedHttpClientConnection> connFactory) {
152 this(socketFactoryRegistry, PoolConcurrencyPolicy.STRICT, TimeValue.NEG_ONE_MILLISECOND, connFactory);
153 }
154
155
156
157
158 @Deprecated
159 public PoolingHttpClientConnectionManager(
160 final Registry<org.apache.hc.client5.http.socket.ConnectionSocketFactory> socketFactoryRegistry,
161 final PoolConcurrencyPolicy poolConcurrencyPolicy,
162 final TimeValue timeToLive,
163 final HttpConnectionFactory<ManagedHttpClientConnection> connFactory) {
164 this(socketFactoryRegistry, poolConcurrencyPolicy, PoolReusePolicy.LIFO, timeToLive, connFactory);
165 }
166
167
168
169
170 @Deprecated
171 public PoolingHttpClientConnectionManager(
172 final Registry<org.apache.hc.client5.http.socket.ConnectionSocketFactory> socketFactoryRegistry,
173 final PoolConcurrencyPolicy poolConcurrencyPolicy,
174 final PoolReusePolicy poolReusePolicy,
175 final TimeValue timeToLive) {
176 this(socketFactoryRegistry, poolConcurrencyPolicy, poolReusePolicy, timeToLive, null);
177 }
178
179
180
181
182 @Deprecated
183 public PoolingHttpClientConnectionManager(
184 final Registry<org.apache.hc.client5.http.socket.ConnectionSocketFactory> socketFactoryRegistry,
185 final PoolConcurrencyPolicy poolConcurrencyPolicy,
186 final PoolReusePolicy poolReusePolicy,
187 final TimeValue timeToLive,
188 final HttpConnectionFactory<ManagedHttpClientConnection> connFactory) {
189 this(socketFactoryRegistry, poolConcurrencyPolicy, poolReusePolicy, timeToLive, null, null, connFactory);
190 }
191
192
193
194
195 @Deprecated
196 public PoolingHttpClientConnectionManager(
197 final Registry<org.apache.hc.client5.http.socket.ConnectionSocketFactory> socketFactoryRegistry,
198 final PoolConcurrencyPolicy poolConcurrencyPolicy,
199 final PoolReusePolicy poolReusePolicy,
200 final TimeValue timeToLive,
201 final SchemePortResolver schemePortResolver,
202 final DnsResolver dnsResolver,
203 final HttpConnectionFactory<ManagedHttpClientConnection> connFactory) {
204 this(new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver),
205 poolConcurrencyPolicy,
206 poolReusePolicy,
207 timeToLive,
208 connFactory);
209 }
210
211 @Internal
212 public PoolingHttpClientConnectionManager(
213 final HttpClientConnectionOperator httpClientConnectionOperator,
214 final PoolConcurrencyPolicy poolConcurrencyPolicy,
215 final PoolReusePolicy poolReusePolicy,
216 final TimeValue timeToLive,
217 final HttpConnectionFactory<ManagedHttpClientConnection> connFactory) {
218 super();
219 this.connectionOperator = Args.notNull(httpClientConnectionOperator, "Connection operator");
220 switch (poolConcurrencyPolicy != null ? poolConcurrencyPolicy : PoolConcurrencyPolicy.STRICT) {
221 case STRICT:
222 this.pool = new StrictConnPool<HttpRoute, ManagedHttpClientConnection>(
223 DEFAULT_MAX_CONNECTIONS_PER_ROUTE,
224 DEFAULT_MAX_TOTAL_CONNECTIONS,
225 timeToLive,
226 poolReusePolicy,
227 new DefaultDisposalCallback<>(),
228 null) {
229
230 @Override
231 public void closeExpired() {
232 enumAvailable(e -> closeIfExpired(e));
233 }
234
235 };
236 break;
237 case LAX:
238 this.pool = new LaxConnPool<HttpRoute, ManagedHttpClientConnection>(
239 DEFAULT_MAX_CONNECTIONS_PER_ROUTE,
240 timeToLive,
241 poolReusePolicy,
242 null) {
243
244 @Override
245 public void closeExpired() {
246 enumAvailable(e -> closeIfExpired(e));
247 }
248
249 };
250 break;
251 default:
252 throw new IllegalArgumentException("Unexpected PoolConcurrencyPolicy value: " + poolConcurrencyPolicy);
253 }
254 this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE;
255 this.closed = new AtomicBoolean(false);
256 }
257
258 @Internal
259 protected PoolingHttpClientConnectionManager(
260 final HttpClientConnectionOperator httpClientConnectionOperator,
261 final ManagedConnPool<HttpRoute, ManagedHttpClientConnection> pool,
262 final HttpConnectionFactory<ManagedHttpClientConnection> connFactory) {
263 super();
264 this.connectionOperator = Args.notNull(httpClientConnectionOperator, "Connection operator");
265 this.pool = Args.notNull(pool, "Connection pool");
266 this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE;
267 this.closed = new AtomicBoolean(false);
268 }
269
270 @Override
271 public void close() {
272 close(CloseMode.GRACEFUL);
273 }
274
275 @Override
276 public void close(final CloseMode closeMode) {
277 if (this.closed.compareAndSet(false, true)) {
278 if (LOG.isDebugEnabled()) {
279 LOG.debug("Shutdown connection pool {}", closeMode);
280 }
281 this.pool.close(closeMode);
282 LOG.debug("Connection pool shut down");
283 }
284 }
285
286 private InternalConnectionEndpoint cast(final ConnectionEndpoint endpoint) {
287 if (endpoint instanceof InternalConnectionEndpoint) {
288 return (InternalConnectionEndpoint) endpoint;
289 }
290 throw new IllegalStateException("Unexpected endpoint class: " + endpoint.getClass());
291 }
292
293 private SocketConfig resolveSocketConfig(final HttpRoute route) {
294 final Resolver<HttpRoute, SocketConfig> resolver = this.socketConfigResolver;
295 final SocketConfig socketConfig = resolver != null ? resolver.resolve(route) : null;
296 return socketConfig != null ? socketConfig : SocketConfig.DEFAULT;
297 }
298
299 private ConnectionConfig resolveConnectionConfig(final HttpRoute route) {
300 final Resolver<HttpRoute, ConnectionConfig> resolver = this.connectionConfigResolver;
301 final ConnectionConfig connectionConfig = resolver != null ? resolver.resolve(route) : null;
302 return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
303 }
304
305 private TlsConfig resolveTlsConfig(final HttpHost host) {
306 final Resolver<HttpHost, TlsConfig> resolver = this.tlsConfigResolver;
307 final TlsConfig tlsConfig = resolver != null ? resolver.resolve(host) : null;
308 return tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT;
309 }
310
311 private TimeValue resolveValidateAfterInactivity(final ConnectionConfig connectionConfig) {
312 final TimeValue timeValue = connectionConfig.getValidateAfterInactivity();
313 return timeValue != null ? timeValue : TimeValue.ofSeconds(2);
314 }
315
316 public LeaseRequest lease(final String id, final HttpRoute route, final Object state) {
317 return lease(id, route, Timeout.DISABLED, state);
318 }
319
320 @Override
321 public LeaseRequest lease(
322 final String id,
323 final HttpRoute route,
324 final Timeout requestTimeout,
325 final Object state) {
326 Args.notNull(route, "HTTP route");
327 if (LOG.isDebugEnabled()) {
328 LOG.debug("{} endpoint lease request ({}) {}", id, requestTimeout, ConnPoolSupport.formatStats(route, state, pool));
329 }
330 final Future<PoolEntry<HttpRoute, ManagedHttpClientConnection>> leaseFuture = this.pool.lease(route, state, requestTimeout, null);
331 return new LeaseRequest() {
332
333
334 private final ReentrantLock lock = new ReentrantLock();
335 private volatile ConnectionEndpoint endpoint;
336
337 @Override
338 public ConnectionEndpoint get(
339 final Timeout timeout) throws InterruptedException, ExecutionException, TimeoutException {
340 lock.lock();
341 try {
342 Args.notNull(timeout, "Operation timeout");
343 if (this.endpoint != null) {
344 return this.endpoint;
345 }
346 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry;
347 try {
348 poolEntry = leaseFuture.get(timeout.getDuration(), timeout.getTimeUnit());
349 } catch (final TimeoutException ex) {
350 leaseFuture.cancel(true);
351 throw ex;
352 }
353 if (LOG.isDebugEnabled()) {
354 LOG.debug("{} endpoint leased {}", id, ConnPoolSupport.formatStats(route, state, pool));
355 }
356 final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
357 try {
358 if (poolEntry.hasConnection()) {
359 final TimeValue timeToLive = connectionConfig.getTimeToLive();
360 if (TimeValue.isNonNegative(timeToLive)) {
361 if (timeToLive.getDuration() == 0
362 || Deadline.calculate(poolEntry.getCreated(), timeToLive).isExpired()) {
363 poolEntry.discardConnection(CloseMode.GRACEFUL);
364 }
365 }
366 }
367 if (poolEntry.hasConnection()) {
368 final TimeValue timeValue = resolveValidateAfterInactivity(connectionConfig);
369 if (TimeValue.isNonNegative(timeValue)) {
370 if (timeValue.getDuration() == 0
371 || Deadline.calculate(poolEntry.getUpdated(), timeValue).isExpired()) {
372 final ManagedHttpClientConnection conn = poolEntry.getConnection();
373 boolean stale;
374 try {
375 stale = conn.isStale();
376 } catch (final IOException ignore) {
377 stale = true;
378 }
379 if (stale) {
380 if (LOG.isDebugEnabled()) {
381 LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(conn));
382 }
383 poolEntry.discardConnection(CloseMode.IMMEDIATE);
384 }
385 }
386 }
387 }
388 final ManagedHttpClientConnection conn = poolEntry.getConnection();
389 if (conn != null) {
390 conn.activate();
391 if (connectionConfig.getSocketTimeout() != null) {
392 conn.setSocketTimeout(connectionConfig.getSocketTimeout());
393 }
394 } else {
395 poolEntry.assignConnection(connFactory.createConnection(null));
396 }
397 this.endpoint = new InternalConnectionEndpoint(poolEntry);
398 if (LOG.isDebugEnabled()) {
399 LOG.debug("{} acquired {}", id, ConnPoolSupport.getId(endpoint));
400 }
401 return this.endpoint;
402 } catch (final Exception ex) {
403 if (LOG.isDebugEnabled()) {
404 LOG.debug("{} endpoint lease failed", id);
405 }
406 pool.release(poolEntry, false);
407 throw new ExecutionException(ex.getMessage(), ex);
408 }
409 } finally {
410 lock.unlock();
411 }
412 }
413
414 @Override
415 public boolean cancel() {
416 return leaseFuture.cancel(true);
417 }
418
419 };
420
421 }
422
423 @Override
424 public void release(final ConnectionEndpoint endpoint, final Object state, final TimeValue keepAlive) {
425 Args.notNull(endpoint, "Managed endpoint");
426 final PoolEntry<HttpRoute, ManagedHttpClientConnection> entry = cast(endpoint).detach();
427 if (entry == null) {
428 return;
429 }
430 if (LOG.isDebugEnabled()) {
431 LOG.debug("{} releasing endpoint", ConnPoolSupport.getId(endpoint));
432 }
433
434 if (this.isClosed()) {
435 return;
436 }
437
438 final ManagedHttpClientConnection conn = entry.getConnection();
439 if (conn != null && keepAlive == null) {
440 conn.close(CloseMode.GRACEFUL);
441 }
442 boolean reusable = conn != null && conn.isOpen() && conn.isConsistent();
443 try {
444 if (reusable) {
445 entry.updateState(state);
446 entry.updateExpiry(keepAlive);
447 conn.passivate();
448 if (LOG.isDebugEnabled()) {
449 final String s;
450 if (TimeValue.isPositive(keepAlive)) {
451 s = "for " + keepAlive;
452 } else {
453 s = "indefinitely";
454 }
455 LOG.debug("{} connection {} can be kept alive {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(conn), s);
456 }
457 } else {
458 if (LOG.isDebugEnabled()) {
459 if (conn != null && !conn.isConsistent()) {
460 LOG.debug("{} connection is in an inconsistent state and cannot be kept alive", ConnPoolSupport.getId(endpoint));
461 } else {
462 LOG.debug("{} connection is not kept alive", ConnPoolSupport.getId(endpoint));
463 }
464 }
465 }
466 } catch (final RuntimeException ex) {
467 reusable = false;
468 throw ex;
469 } finally {
470 this.pool.release(entry, reusable);
471 if (LOG.isDebugEnabled()) {
472 LOG.debug("{} connection released {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.formatStats(entry.getRoute(), entry.getState(), pool));
473 }
474 }
475 }
476
477 @Override
478 public void connect(final ConnectionEndpoint endpoint, final TimeValue timeout, final HttpContext context) throws IOException {
479 Args.notNull(endpoint, "Managed endpoint");
480 final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
481 if (internalEndpoint.isConnected()) {
482 return;
483 }
484 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = internalEndpoint.getPoolEntry();
485 if (!poolEntry.hasConnection()) {
486 poolEntry.assignConnection(connFactory.createConnection(null));
487 }
488 final HttpRoute route = poolEntry.getRoute();
489 final HttpHost firstHop = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost();
490 final SocketConfig socketConfig = resolveSocketConfig(route);
491 final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
492 final Timeout connectTimeout = timeout != null ? Timeout.of(timeout.getDuration(), timeout.getTimeUnit()) : connectionConfig.getConnectTimeout();
493 if (LOG.isDebugEnabled()) {
494 LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), firstHop, connectTimeout);
495 }
496 final ManagedHttpClientConnection conn = poolEntry.getConnection();
497 this.connectionOperator.connect(
498 conn,
499 firstHop,
500 route.getTargetName(),
501 route.getLocalSocketAddress(),
502 connectTimeout,
503 socketConfig,
504 route.isTunnelled() ? null : resolveTlsConfig(route.getTargetHost()),
505 context);
506 if (LOG.isDebugEnabled()) {
507 LOG.debug("{} connected {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(conn));
508 }
509 final Timeout socketTimeout = connectionConfig.getSocketTimeout();
510 if (socketTimeout != null) {
511 conn.setSocketTimeout(socketTimeout);
512 }
513 }
514
515 @Override
516 public void upgrade(final ConnectionEndpoint endpoint, final HttpContext context) throws IOException {
517 Args.notNull(endpoint, "Managed endpoint");
518 final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
519 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = internalEndpoint.getValidatedPoolEntry();
520 final HttpRoute route = poolEntry.getRoute();
521 final HttpHost target = route.getTargetHost();
522 final TlsConfig tlsConfig = resolveTlsConfig(target);
523 this.connectionOperator.upgrade(
524 poolEntry.getConnection(),
525 target,
526 route.getTargetName(),
527 tlsConfig,
528 context);
529 }
530
531 @Override
532 public void closeIdle(final TimeValue idleTime) {
533 Args.notNull(idleTime, "Idle time");
534 if (LOG.isDebugEnabled()) {
535 LOG.debug("Closing connections idle longer than {}", idleTime);
536 }
537 if (isClosed()) {
538 return;
539 }
540 this.pool.closeIdle(idleTime);
541 }
542
543 @Override
544 public void closeExpired() {
545 if (isClosed()) {
546 return;
547 }
548 LOG.debug("Closing expired connections");
549 this.pool.closeExpired();
550 }
551
552 @Override
553 public Set<HttpRoute> getRoutes() {
554 return this.pool.getRoutes();
555 }
556
557 @Override
558 public int getMaxTotal() {
559 return this.pool.getMaxTotal();
560 }
561
562 @Override
563 public void setMaxTotal(final int max) {
564 this.pool.setMaxTotal(max);
565 }
566
567 @Override
568 public int getDefaultMaxPerRoute() {
569 return this.pool.getDefaultMaxPerRoute();
570 }
571
572 @Override
573 public void setDefaultMaxPerRoute(final int max) {
574 this.pool.setDefaultMaxPerRoute(max);
575 }
576
577 @Override
578 public int getMaxPerRoute(final HttpRoute route) {
579 return this.pool.getMaxPerRoute(route);
580 }
581
582 @Override
583 public void setMaxPerRoute(final HttpRoute route, final int max) {
584 this.pool.setMaxPerRoute(route, max);
585 }
586
587 @Override
588 public PoolStats getTotalStats() {
589 return this.pool.getTotalStats();
590 }
591
592 @Override
593 public PoolStats getStats(final HttpRoute route) {
594 return this.pool.getStats(route);
595 }
596
597
598
599
600 public void setDefaultSocketConfig(final SocketConfig config) {
601 this.socketConfigResolver = route -> config;
602 }
603
604
605
606
607
608
609 public void setSocketConfigResolver(final Resolver<HttpRoute, SocketConfig> socketConfigResolver) {
610 this.socketConfigResolver = socketConfigResolver;
611 }
612
613
614
615
616
617
618 public void setDefaultConnectionConfig(final ConnectionConfig config) {
619 this.connectionConfigResolver = route -> config;
620 }
621
622
623
624
625
626
627 public void setConnectionConfigResolver(final Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver) {
628 this.connectionConfigResolver = connectionConfigResolver;
629 }
630
631
632
633
634
635
636 public void setDefaultTlsConfig(final TlsConfig config) {
637 this.tlsConfigResolver = host -> config;
638 }
639
640
641
642
643
644
645 public void setTlsConfigResolver(final Resolver<HttpHost, TlsConfig> tlsConfigResolver) {
646 this.tlsConfigResolver = tlsConfigResolver;
647 }
648
649 void closeIfExpired(final PoolEntry<HttpRoute, ManagedHttpClientConnection> entry) {
650 final long now = System.currentTimeMillis();
651 if (entry.getExpiryDeadline().isBefore(now)) {
652 entry.discardConnection(CloseMode.GRACEFUL);
653 } else {
654 final ConnectionConfig connectionConfig = resolveConnectionConfig(entry.getRoute());
655 final TimeValue timeToLive = connectionConfig.getTimeToLive();
656 if (timeToLive != null && Deadline.calculate(entry.getCreated(), timeToLive).isBefore(now)) {
657 entry.discardConnection(CloseMode.GRACEFUL);
658 }
659 }
660 }
661
662
663
664
665 @Deprecated
666 public SocketConfig getDefaultSocketConfig() {
667 return SocketConfig.DEFAULT;
668 }
669
670
671
672
673
674
675 @Deprecated
676 public TimeValue getValidateAfterInactivity() {
677 return ConnectionConfig.DEFAULT.getValidateAfterInactivity();
678 }
679
680
681
682
683
684
685
686
687
688
689
690 @Deprecated
691 public void setValidateAfterInactivity(final TimeValue validateAfterInactivity) {
692 setDefaultConnectionConfig(ConnectionConfig.custom()
693 .setValidateAfterInactivity(validateAfterInactivity)
694 .build());
695 }
696
697 private static final PrefixedIncrementingId INCREMENTING_ID = new PrefixedIncrementingId("ep-");
698
699 static class InternalConnectionEndpoint extends ConnectionEndpoint implements ConnectionHolder, Identifiable {
700
701 private final AtomicReference<PoolEntry<HttpRoute, ManagedHttpClientConnection>> poolEntryRef;
702 private final String id;
703
704 InternalConnectionEndpoint(
705 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry) {
706 this.poolEntryRef = new AtomicReference<>(poolEntry);
707 this.id = INCREMENTING_ID.getNextId();
708 }
709
710 @Override
711 public String getId() {
712 return id;
713 }
714
715 PoolEntry<HttpRoute, ManagedHttpClientConnection> getPoolEntry() {
716 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = poolEntryRef.get();
717 if (poolEntry == null) {
718 throw new ConnectionShutdownException();
719 }
720 return poolEntry;
721 }
722
723 PoolEntry<HttpRoute, ManagedHttpClientConnection> getValidatedPoolEntry() {
724 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = getPoolEntry();
725 final ManagedHttpClientConnection connection = poolEntry.getConnection();
726 if (connection == null || !connection.isOpen()) {
727 throw new ConnectionShutdownException();
728 }
729 return poolEntry;
730 }
731
732 PoolEntry<HttpRoute, ManagedHttpClientConnection> detach() {
733 return poolEntryRef.getAndSet(null);
734 }
735
736 @Override
737 public void close(final CloseMode closeMode) {
738 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = poolEntryRef.get();
739 if (poolEntry != null) {
740 poolEntry.discardConnection(closeMode);
741 }
742 }
743
744 @Override
745 public void close() throws IOException {
746 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = poolEntryRef.get();
747 if (poolEntry != null) {
748 poolEntry.discardConnection(CloseMode.GRACEFUL);
749 }
750 }
751
752 @Override
753 public boolean isConnected() {
754 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = getPoolEntry();
755 final ManagedHttpClientConnection connection = poolEntry.getConnection();
756 return connection != null && connection.isOpen();
757 }
758
759 @Override
760 public void setSocketTimeout(final Timeout timeout) {
761 getValidatedPoolEntry().getConnection().setSocketTimeout(timeout);
762 }
763
764
765
766
767 @Deprecated
768 @Override
769 public ClassicHttpResponse execute(
770 final String exchangeId,
771 final ClassicHttpRequest request,
772 final HttpRequestExecutor requestExecutor,
773 final HttpContext context) throws IOException, HttpException {
774 Args.notNull(request, "HTTP request");
775 Args.notNull(requestExecutor, "Request executor");
776 final ManagedHttpClientConnection connection = getValidatedPoolEntry().getConnection();
777 if (LOG.isDebugEnabled()) {
778 LOG.debug("{} executing exchange {} over {}", id, exchangeId, ConnPoolSupport.getId(connection));
779 }
780 return requestExecutor.execute(request, connection, context);
781 }
782
783
784
785
786 @Override
787 public ClassicHttpResponse execute(
788 final String exchangeId,
789 final ClassicHttpRequest request,
790 final RequestExecutor requestExecutor,
791 final HttpContext context) throws IOException, HttpException {
792 Args.notNull(request, "HTTP request");
793 Args.notNull(requestExecutor, "Request executor");
794 final ManagedHttpClientConnection connection = getValidatedPoolEntry().getConnection();
795 if (LOG.isDebugEnabled()) {
796 LOG.debug("{} executing exchange {} over {}", id, exchangeId, ConnPoolSupport.getId(connection));
797 }
798 return requestExecutor.execute(request, connection, context);
799 }
800
801
802
803
804 @Override
805 public EndpointInfo getInfo() {
806 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = poolEntryRef.get();
807 if (poolEntry != null) {
808 final ManagedHttpClientConnection connection = poolEntry.getConnection();
809 if (connection != null && connection.isOpen()) {
810 return new EndpointInfo(connection.getProtocolVersion(), connection.getSSLSession());
811 }
812 }
813 return null;
814 }
815
816 @Override
817 public HttpConnection get() {
818 final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = poolEntryRef.get();
819 return poolEntry != null ? poolEntry.getConnection() : null;
820 }
821
822 }
823
824
825
826
827
828
829
830
831
832 public boolean isClosed() {
833 return this.closed.get();
834 }
835
836
837 }