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
28 package org.apache.hc.client5.http.impl.io;
29
30 import java.net.InetAddress;
31 import java.net.InetSocketAddress;
32 import java.net.Socket;
33 import java.util.concurrent.Future;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.TimeoutException;
36
37 import org.apache.hc.client5.http.DnsResolver;
38 import org.apache.hc.client5.http.HttpRoute;
39 import org.apache.hc.client5.http.SchemePortResolver;
40 import org.apache.hc.client5.http.config.ConnectionConfig;
41 import org.apache.hc.client5.http.config.TlsConfig;
42 import org.apache.hc.client5.http.io.ConnectionEndpoint;
43 import org.apache.hc.client5.http.io.LeaseRequest;
44 import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
45 import org.apache.hc.client5.http.protocol.HttpClientContext;
46 import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
47 import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
48 import org.apache.hc.core5.http.HttpHost;
49 import org.apache.hc.core5.http.config.Lookup;
50 import org.apache.hc.core5.http.io.SocketConfig;
51 import org.apache.hc.core5.pool.PoolEntry;
52 import org.apache.hc.core5.pool.StrictConnPool;
53 import org.apache.hc.core5.util.TimeValue;
54 import org.apache.hc.core5.util.Timeout;
55 import org.junit.jupiter.api.Assertions;
56 import org.junit.jupiter.api.BeforeEach;
57 import org.junit.jupiter.api.Test;
58 import org.mockito.Mock;
59 import org.mockito.Mockito;
60 import org.mockito.MockitoAnnotations;
61
62
63
64
65 @SuppressWarnings({"boxing","static-access","resource"})
66 public class TestPoolingHttpClientConnectionManager {
67
68 @Mock
69 private ManagedHttpClientConnection conn;
70 @Mock
71 private Lookup<ConnectionSocketFactory> socketFactoryRegistry;
72 @Mock
73 private ConnectionSocketFactory plainSocketFactory;
74 @Mock
75 private ConnectionSocketFactory sslSocketFactory;
76 @Mock
77 private Socket socket;
78 @Mock
79 private SchemePortResolver schemePortResolver;
80 @Mock
81 private DnsResolver dnsResolver;
82 @Mock
83 private Future<PoolEntry<HttpRoute, ManagedHttpClientConnection>> future;
84 @Mock
85 private StrictConnPool<HttpRoute, ManagedHttpClientConnection> pool;
86 private PoolingHttpClientConnectionManager mgr;
87
88 @BeforeEach
89 public void setup() throws Exception {
90 MockitoAnnotations.openMocks(this);
91 mgr = new PoolingHttpClientConnectionManager(
92 new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver), pool, null);
93 }
94
95 @Test
96 public void testLeaseRelease() throws Exception {
97 final HttpHost target = new HttpHost("localhost", 80);
98 final HttpRoute route = new HttpRoute(target);
99
100 final PoolEntry<HttpRoute, ManagedHttpClientConnection> entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND);
101 entry.assignConnection(conn);
102
103 Mockito.when(conn.isOpen()).thenReturn(true);
104 Mockito.when(conn.isConsistent()).thenReturn(true);
105 Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry);
106 Mockito.when(pool.lease(
107 Mockito.eq(route),
108 Mockito.eq(null),
109 Mockito.any(),
110 Mockito.eq(null)))
111 .thenReturn(future);
112
113 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
114 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1));
115 Assertions.assertNotNull(endpoint1);
116 Assertions.assertNotSame(conn, endpoint1);
117
118 mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND);
119
120 Mockito.verify(pool).release(entry, true);
121 }
122
123 @Test
124 public void testReleaseRouteIncomplete() throws Exception {
125 final HttpHost target = new HttpHost("localhost", 80);
126 final HttpRoute route = new HttpRoute(target);
127
128 final PoolEntry<HttpRoute, ManagedHttpClientConnection> entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND);
129
130 Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry);
131 Mockito.when(pool.lease(
132 Mockito.eq(route),
133 Mockito.eq(null),
134 Mockito.any(),
135 Mockito.eq(null)))
136 .thenReturn(future);
137
138 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
139 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1));
140 Assertions.assertNotNull(endpoint1);
141 Assertions.assertNotSame(conn, endpoint1);
142
143 mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND);
144
145 Mockito.verify(pool).release(entry, false);
146 }
147
148 @Test
149 public void testLeaseFutureTimeout() throws Exception {
150 final HttpHost target = new HttpHost("localhost", 80);
151 final HttpRoute route = new HttpRoute(target);
152
153 Mockito.when(future.get(1, TimeUnit.SECONDS)).thenThrow(new TimeoutException());
154 Mockito.when(pool.lease(
155 Mockito.eq(route),
156 Mockito.eq(null),
157 Mockito.any(),
158 Mockito.eq(null)))
159 .thenReturn(future);
160
161 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
162 Assertions.assertThrows(TimeoutException.class, () ->
163 connRequest1.get(Timeout.ofSeconds(1)));
164 }
165
166 @Test
167 public void testReleaseReusable() throws Exception {
168 final HttpHost target = new HttpHost("localhost", 80);
169 final HttpRoute route = new HttpRoute(target);
170
171 final PoolEntry<HttpRoute, ManagedHttpClientConnection> entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND);
172 entry.assignConnection(conn);
173
174 Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry);
175 Mockito.when(pool.lease(
176 Mockito.eq(route),
177 Mockito.eq(null),
178 Mockito.any(),
179 Mockito.eq(null)))
180 .thenReturn(future);
181 Mockito.when(conn.isOpen()).thenReturn(true);
182 Mockito.when(conn.isConsistent()).thenReturn(true);
183
184 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
185 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1));
186 Assertions.assertNotNull(endpoint1);
187 Assertions.assertTrue(endpoint1.isConnected());
188
189 mgr.release(endpoint1, "some state", TimeValue.NEG_ONE_MILLISECOND);
190
191 Mockito.verify(pool).release(entry, true);
192 Assertions.assertEquals("some state", entry.getState());
193 }
194
195 @Test
196 public void testReleaseNonReusable() throws Exception {
197 final HttpHost target = new HttpHost("localhost", 80);
198 final HttpRoute route = new HttpRoute(target);
199
200 final PoolEntry<HttpRoute, ManagedHttpClientConnection> entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND);
201 entry.assignConnection(conn);
202
203 Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry);
204 Mockito.when(pool.lease(
205 Mockito.eq(route),
206 Mockito.eq(null),
207 Mockito.any(),
208 Mockito.eq(null)))
209 .thenReturn(future);
210 Mockito.when(conn.isOpen()).thenReturn(Boolean.FALSE);
211
212 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
213 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1));
214 Assertions.assertNotNull(endpoint1);
215 Assertions.assertFalse(endpoint1.isConnected());
216
217 mgr.release(endpoint1, "some state", TimeValue.NEG_ONE_MILLISECOND);
218
219 Mockito.verify(pool).release(entry, false);
220 Assertions.assertNull(entry.getState());
221 }
222
223 @Test
224 public void testTargetConnect() throws Exception {
225 final HttpHost target = new HttpHost("https", "somehost", 443);
226 final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
227 final InetAddress local = InetAddress.getByAddress(new byte[]{127, 0, 0, 1});
228 final HttpRoute route = new HttpRoute(target, local, true);
229
230 final PoolEntry<HttpRoute, ManagedHttpClientConnection> entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND);
231 entry.assignConnection(conn);
232
233 Mockito.when(conn.isOpen()).thenReturn(false);
234 Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry);
235 Mockito.when(pool.lease(
236 Mockito.eq(route),
237 Mockito.eq(null),
238 Mockito.any(),
239 Mockito.eq(null)))
240 .thenReturn(future);
241
242 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
243 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1));
244 Assertions.assertNotNull(endpoint1);
245
246 final HttpClientContext context = HttpClientContext.create();
247 final SocketConfig sconfig = SocketConfig.custom().build();
248
249 mgr.setDefaultSocketConfig(sconfig);
250
251 final ConnectionConfig connectionConfig = ConnectionConfig.custom()
252 .setConnectTimeout(234, TimeUnit.MILLISECONDS)
253 .build();
254 mgr.setDefaultConnectionConfig(connectionConfig);
255 final TlsConfig tlsConfig = TlsConfig.custom()
256 .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
257 .build();
258 mgr.setDefaultTlsConfig(tlsConfig);
259
260 Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[]{remote});
261 Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
262 Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(plainSocketFactory);
263 Mockito.when(plainSocketFactory.createSocket(Mockito.any(), Mockito.any())).thenReturn(socket);
264 Mockito.when(plainSocketFactory.connectSocket(
265 Mockito.eq(socket),
266 Mockito.any(),
267 Mockito.any(),
268 Mockito.any(),
269 Mockito.any(),
270 Mockito.any(),
271 Mockito.any())).thenReturn(socket);
272
273 mgr.connect(endpoint1, null, context);
274
275 Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost");
276 Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
277 Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(null, context);
278 Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(
279 socket,
280 target,
281 new InetSocketAddress(remote, 8443),
282 new InetSocketAddress(local, 0),
283 Timeout.ofMilliseconds(234),
284 tlsConfig,
285 context);
286
287 mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
288
289 Mockito.verify(dnsResolver, Mockito.times(2)).resolve("somehost");
290 Mockito.verify(schemePortResolver, Mockito.times(2)).resolve(target);
291 Mockito.verify(plainSocketFactory, Mockito.times(2)).createSocket(null, context);
292 Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(
293 socket,
294 target,
295 new InetSocketAddress(remote, 8443),
296 new InetSocketAddress(local, 0),
297 Timeout.ofMilliseconds(123),
298 tlsConfig,
299 context);
300 }
301
302 @Test
303 public void testProxyConnectAndUpgrade() throws Exception {
304 final HttpHost target = new HttpHost("https", "somehost", 443);
305 final HttpHost proxy = new HttpHost("someproxy", 8080);
306 final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
307 final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
308 final HttpRoute route = new HttpRoute(target, local, proxy, true);
309
310 final PoolEntry<HttpRoute, ManagedHttpClientConnection> entry = new PoolEntry<>(route, TimeValue.NEG_ONE_MILLISECOND);
311 entry.assignConnection(conn);
312
313 Mockito.when(conn.isOpen()).thenReturn(false);
314 Mockito.when(future.get(1, TimeUnit.SECONDS)).thenReturn(entry);
315 Mockito.when(pool.lease(
316 Mockito.eq(route),
317 Mockito.eq(null),
318 Mockito.any(),
319 Mockito.eq(null)))
320 .thenReturn(future);
321
322 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
323 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ofSeconds(1));
324 Assertions.assertNotNull(endpoint1);
325
326 final ConnectionSocketFactory plainsf = Mockito.mock(ConnectionSocketFactory.class);
327 final LayeredConnectionSocketFactory sslsf = Mockito.mock(LayeredConnectionSocketFactory.class);
328 final Socket mockSock = Mockito.mock(Socket.class);
329 final HttpClientContext context = HttpClientContext.create();
330 final SocketConfig sconfig = SocketConfig.custom().build();
331
332 mgr.setDefaultSocketConfig(sconfig);
333
334 final ConnectionConfig connectionConfig = ConnectionConfig.custom()
335 .setConnectTimeout(234, TimeUnit.MILLISECONDS)
336 .build();
337 mgr.setDefaultConnectionConfig(connectionConfig);
338 final TlsConfig tlsConfig = TlsConfig.custom()
339 .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
340 .build();
341 mgr.setDefaultTlsConfig(tlsConfig);
342
343 Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote});
344 Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080);
345 Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
346 Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainsf);
347 Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslsf);
348 Mockito.when(plainsf.createSocket(Mockito.any(), Mockito.any())).thenReturn(mockSock);
349 Mockito.when(plainsf.connectSocket(
350 Mockito.eq(mockSock),
351 Mockito.any(),
352 Mockito.any(),
353 Mockito.any(),
354 Mockito.any(),
355 Mockito.any(),
356 Mockito.any())).thenReturn(mockSock);
357
358 mgr.connect(endpoint1, null, context);
359
360 Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy");
361 Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy);
362 Mockito.verify(plainsf, Mockito.times(1)).createSocket(null, context);
363 Mockito.verify(plainsf, Mockito.times(1)).connectSocket(
364 mockSock,
365 proxy,
366 new InetSocketAddress(remote, 8080),
367 new InetSocketAddress(local, 0),
368 Timeout.ofMilliseconds(234),
369 tlsConfig,
370 context);
371
372 Mockito.when(conn.isOpen()).thenReturn(true);
373 Mockito.when(conn.getSocket()).thenReturn(mockSock);
374
375 mgr.upgrade(endpoint1, context);
376
377 Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
378 Mockito.verify(sslsf, Mockito.times(1)).createLayeredSocket(
379 mockSock, "somehost", 8443, tlsConfig, context);
380 }
381
382 }