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.async;
28
29 import java.net.InetSocketAddress;
30 import java.util.concurrent.Future;
31
32 import org.apache.hc.client5.http.HttpRoute;
33 import org.apache.hc.client5.http.config.ConnectionConfig;
34 import org.apache.hc.core5.concurrent.CallbackContribution;
35 import org.apache.hc.core5.concurrent.FutureCallback;
36 import org.apache.hc.core5.function.Callback;
37 import org.apache.hc.core5.function.Resolver;
38 import org.apache.hc.core5.http.HttpHost;
39 import org.apache.hc.core5.http.URIScheme;
40 import org.apache.hc.core5.http.nio.command.ShutdownCommand;
41 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
42 import org.apache.hc.core5.http2.nio.command.PingCommand;
43 import org.apache.hc.core5.http2.nio.support.BasicPingHandler;
44 import org.apache.hc.core5.io.CloseMode;
45 import org.apache.hc.core5.io.ModalCloseable;
46 import org.apache.hc.core5.net.NamedEndpoint;
47 import org.apache.hc.core5.reactor.AbstractIOSessionPool;
48 import org.apache.hc.core5.reactor.Command;
49 import org.apache.hc.core5.reactor.ConnectionInitiator;
50 import org.apache.hc.core5.reactor.IOSession;
51 import org.apache.hc.core5.reactor.ssl.TransportSecurityLayer;
52 import org.apache.hc.core5.util.TimeValue;
53 import org.apache.hc.core5.util.Timeout;
54
55 class InternalH2ConnPool implements ModalCloseable {
56
57 private final SessionPool sessionPool;
58
59 private volatile Resolver<HttpHost, ConnectionConfig> connectionConfigResolver;
60
61 InternalH2ConnPool(final ConnectionInitiator connectionInitiator,
62 final Resolver<HttpHost, InetSocketAddress> addressResolver,
63 final TlsStrategy tlsStrategy) {
64 this.sessionPool = new SessionPool(connectionInitiator, addressResolver, tlsStrategy);
65 }
66
67 @Override
68 public void close(final CloseMode closeMode) {
69 sessionPool.close(closeMode);
70 }
71
72 @Override
73 public void close() {
74 sessionPool.close();
75 }
76
77 private ConnectionConfig resolveConnectionConfig(final HttpHost httpHost) {
78 final Resolver<HttpHost, ConnectionConfig> resolver = this.connectionConfigResolver;
79 final ConnectionConfig connectionConfig = resolver != null ? resolver.resolve(httpHost) : null;
80 return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
81 }
82
83 public Future<IOSession> getSession(
84 final HttpRoute route,
85 final Timeout connectTimeout,
86 final FutureCallback<IOSession> callback) {
87 final ConnectionConfig connectionConfig = resolveConnectionConfig(route.getTargetHost());
88 return sessionPool.getSession(
89 route,
90 connectTimeout != null ? connectTimeout : connectionConfig.getConnectTimeout(),
91 new CallbackContribution<IOSession>(callback) {
92
93 @Override
94 public void completed(final IOSession ioSession) {
95 final Timeout socketTimeout = connectionConfig.getSocketTimeout();
96 if (socketTimeout != null) {
97 ioSession.setSocketTimeout(socketTimeout);
98 }
99 callback.completed(ioSession);
100 }
101
102 });
103 }
104
105 public void closeIdle(final TimeValue idleTime) {
106 sessionPool.closeIdle(idleTime);
107 }
108
109 public void setConnectionConfigResolver(final Resolver<HttpHost, ConnectionConfig> connectionConfigResolver) {
110 this.connectionConfigResolver = connectionConfigResolver;
111 }
112
113 public TimeValue getValidateAfterInactivity() {
114 return sessionPool.validateAfterInactivity;
115 }
116
117 public void setValidateAfterInactivity(final TimeValue timeValue) {
118 sessionPool.validateAfterInactivity = timeValue;
119 }
120
121
122 static class SessionPool extends AbstractIOSessionPool<HttpRoute> {
123
124 private final ConnectionInitiator connectionInitiator;
125 private final Resolver<HttpHost, InetSocketAddress> addressResolver;
126 private final TlsStrategy tlsStrategy;
127
128 private volatile TimeValue validateAfterInactivity = TimeValue.NEG_ONE_MILLISECOND;
129
130 SessionPool(final ConnectionInitiator connectionInitiator,
131 final Resolver<HttpHost, InetSocketAddress> addressResolver,
132 final TlsStrategy tlsStrategy) {
133 this.connectionInitiator = connectionInitiator;
134 this.addressResolver = addressResolver;
135 this.tlsStrategy = tlsStrategy;
136 }
137
138 @Override
139 protected Future<IOSession> connectSession(final HttpRoute route,
140 final Timeout connectTimeout,
141 final FutureCallback<IOSession> callback) {
142 final HttpHost target = route.getTargetHost();
143 final InetSocketAddress localAddress = route.getLocalSocketAddress();
144 final InetSocketAddress remoteAddress = addressResolver.resolve(target);
145 return connectionInitiator.connect(
146 target,
147 remoteAddress,
148 localAddress,
149 connectTimeout,
150 null,
151 new CallbackContribution<IOSession>(callback) {
152
153 @Override
154 public void completed(final IOSession ioSession) {
155 if (tlsStrategy != null
156 && URIScheme.HTTPS.same(target.getSchemeName())
157 && ioSession instanceof TransportSecurityLayer) {
158 final NamedEndpoint tlsName = route.getTargetName() != null ? route.getTargetName() : target;
159 tlsStrategy.upgrade(
160 (TransportSecurityLayer) ioSession,
161 tlsName,
162 null,
163 connectTimeout,
164 new CallbackContribution<TransportSecurityLayer>(callback) {
165
166 @Override
167 public void completed(final TransportSecurityLayer transportSecurityLayer) {
168 callback.completed(ioSession);
169 }
170
171 });
172 ioSession.setSocketTimeout(connectTimeout);
173 } else {
174 callback.completed(ioSession);
175 }
176 }
177
178 });
179 }
180
181 @Override
182 protected void validateSession(final IOSession ioSession,
183 final Callback<Boolean> callback) {
184 if (ioSession.isOpen()) {
185 final TimeValue timeValue = validateAfterInactivity;
186 if (TimeValue.isNonNegative(timeValue)) {
187 final long lastAccessTime = Math.min(ioSession.getLastReadTime(), ioSession.getLastWriteTime());
188 final long deadline = lastAccessTime + timeValue.toMilliseconds();
189 if (deadline <= System.currentTimeMillis()) {
190 final Timeout socketTimeoutMillis = ioSession.getSocketTimeout();
191 ioSession.enqueue(new PingCommand(new BasicPingHandler(result -> {
192 ioSession.setSocketTimeout(socketTimeoutMillis);
193 callback.execute(result);
194 })), Command.Priority.NORMAL);
195 return;
196 }
197 }
198 callback.execute(true);
199 } else {
200 callback.execute(false);
201 }
202 }
203
204 @Override
205 protected void closeSession(final IOSession ioSession,
206 final CloseMode closeMode) {
207 if (closeMode == CloseMode.GRACEFUL) {
208 ioSession.enqueue(ShutdownCommand.GRACEFUL, Command.Priority.NORMAL);
209 } else {
210 ioSession.close(closeMode);
211 }
212 }
213 }
214
215 }