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.TimeUnit;
34
35 import org.apache.hc.client5.http.DnsResolver;
36 import org.apache.hc.client5.http.HttpRoute;
37 import org.apache.hc.client5.http.SchemePortResolver;
38 import org.apache.hc.client5.http.config.ConnectionConfig;
39 import org.apache.hc.client5.http.config.TlsConfig;
40 import org.apache.hc.client5.http.io.ConnectionEndpoint;
41 import org.apache.hc.client5.http.io.LeaseRequest;
42 import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
43 import org.apache.hc.client5.http.protocol.HttpClientContext;
44 import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
45 import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
46 import org.apache.hc.core5.http.HttpHost;
47 import org.apache.hc.core5.http.config.Lookup;
48 import org.apache.hc.core5.http.io.HttpConnectionFactory;
49 import org.apache.hc.core5.http.io.SocketConfig;
50 import org.apache.hc.core5.io.CloseMode;
51 import org.apache.hc.core5.util.TimeValue;
52 import org.apache.hc.core5.util.Timeout;
53 import org.junit.jupiter.api.Assertions;
54 import org.junit.jupiter.api.BeforeEach;
55 import org.junit.jupiter.api.Test;
56 import org.mockito.Mock;
57 import org.mockito.Mockito;
58 import org.mockito.MockitoAnnotations;
59
60 @SuppressWarnings({"boxing","static-access"})
61 public class TestBasicHttpClientConnectionManager {
62
63 @Mock
64 private ManagedHttpClientConnection conn;
65 @Mock
66 private HttpConnectionFactory<ManagedHttpClientConnection> connFactory;
67 @Mock
68 private Lookup<ConnectionSocketFactory> socketFactoryRegistry;
69 @Mock
70 private ConnectionSocketFactory plainSocketFactory;
71 @Mock
72 private LayeredConnectionSocketFactory sslSocketFactory;
73 @Mock
74 private Socket socket;
75 @Mock
76 private SchemePortResolver schemePortResolver;
77 @Mock
78 private DnsResolver dnsResolver;
79
80 private BasicHttpClientConnectionManager mgr;
81
82 @BeforeEach
83 public void setup() throws Exception {
84 MockitoAnnotations.openMocks(this);
85 mgr = new BasicHttpClientConnectionManager(
86 socketFactoryRegistry, connFactory, schemePortResolver, dnsResolver);
87 }
88
89 @Test
90 public void testLeaseReleaseNonReusable() throws Exception {
91 final HttpHost target = new HttpHost("localhost", 80);
92 final HttpRoute route = new HttpRoute(target);
93
94 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
95
96 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
97 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
98 Assertions.assertNotNull(endpoint1);
99 Assertions.assertFalse(endpoint1.isConnected());
100
101 mgr.release(endpoint1, null, TimeValue.ofMilliseconds(100));
102
103 Assertions.assertNull(mgr.getRoute());
104 Assertions.assertNull(mgr.getState());
105
106 final LeaseRequest connRequest2 = mgr.lease("some-id", route, null);
107 final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS);
108 Assertions.assertNotNull(conn2);
109 Assertions.assertFalse(conn2.isConnected());
110
111 Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.any());
112 }
113
114 @Test
115 public void testLeaseReleaseReusable() throws Exception {
116 final HttpHost target = new HttpHost("somehost", 80);
117 final HttpRoute route = new HttpRoute(target);
118
119 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
120
121 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
122 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
123 Assertions.assertNotNull(endpoint1);
124
125 Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any());
126
127 Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE);
128
129 mgr.release(endpoint1, null, TimeValue.ofMilliseconds(100));
130
131 Assertions.assertEquals(route, mgr.getRoute());
132 Assertions.assertNull(mgr.getState());
133
134 final LeaseRequest connRequest2 = mgr.lease("some-id", route, null);
135 final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS);
136 Assertions.assertNotNull(conn2);
137 Assertions.assertTrue(conn2.isConnected());
138
139 Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any());
140 }
141
142 @Test
143 public void testLeaseReleaseReusableWithState() throws Exception {
144 final HttpHost target = new HttpHost("somehost", 80);
145 final HttpRoute route = new HttpRoute(target);
146
147 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
148
149 final LeaseRequest connRequest1 = mgr.lease("some-id", route, "some state");
150 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
151 Assertions.assertNotNull(endpoint1);
152
153 Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any());
154
155 Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE);
156
157 mgr.release(endpoint1, "some other state", TimeValue.ofMilliseconds(10000));
158
159 Assertions.assertEquals(route, mgr.getRoute());
160 Assertions.assertEquals("some other state", mgr.getState());
161
162 final LeaseRequest connRequest2 = mgr.lease("some-id", route, "some other state");
163 final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS);
164 Assertions.assertNotNull(conn2);
165 Assertions.assertTrue(conn2.isConnected());
166
167 Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any());
168 }
169
170 @Test
171 public void testLeaseDifferentRoute() throws Exception {
172 final HttpHost target1 = new HttpHost("somehost", 80);
173 final HttpRoute route1 = new HttpRoute(target1);
174
175 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
176
177 final LeaseRequest connRequest1 = mgr.lease("some-id", route1, null);
178 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
179 Assertions.assertNotNull(endpoint1);
180
181 Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any());
182
183 Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE);
184
185 mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND);
186
187 Assertions.assertEquals(route1, mgr.getRoute());
188 Assertions.assertNull(mgr.getState());
189
190 final HttpHost target2 = new HttpHost("otherhost", 80);
191 final HttpRoute route2 = new HttpRoute(target2);
192 final LeaseRequest connRequest2 = mgr.lease("some-id", route2, null);
193 final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS);
194 Assertions.assertNotNull(conn2);
195 Assertions.assertFalse(conn2.isConnected());
196
197 Mockito.verify(conn).close(CloseMode.GRACEFUL);
198 Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.any());
199 }
200
201 @Test
202 public void testLeaseExpired() throws Exception {
203 final HttpHost target = new HttpHost("somehost", 80);
204 final HttpRoute route = new HttpRoute(target);
205
206 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
207
208 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
209 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
210 Assertions.assertNotNull(endpoint1);
211
212 Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any());
213
214 Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE);
215
216 mgr.release(endpoint1, null, TimeValue.ofMilliseconds(10));
217
218 Assertions.assertEquals(route, mgr.getRoute());
219 Assertions.assertNull(mgr.getState());
220
221 Thread.sleep(50);
222
223 final LeaseRequest connRequest2 = mgr.lease("some-id", route, null);
224 final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS);
225 Assertions.assertNotNull(conn2);
226 Assertions.assertFalse(conn2.isConnected());
227
228 Mockito.verify(conn).close(CloseMode.GRACEFUL);
229 Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.any());
230 }
231
232 @Test
233 public void testReleaseInvalidArg() throws Exception {
234 Assertions.assertThrows(NullPointerException.class, () ->
235 mgr.release(null, null, TimeValue.NEG_ONE_MILLISECOND));
236 }
237
238 @Test
239 public void testReleaseAnotherConnection() throws Exception {
240 final ConnectionEndpoint wrongCon = Mockito.mock(ConnectionEndpoint.class);
241 Assertions.assertThrows(IllegalStateException.class, () ->
242 mgr.release(wrongCon, null, TimeValue.NEG_ONE_MILLISECOND));
243 }
244
245 @Test
246 public void testShutdown() throws Exception {
247 final HttpHost target = new HttpHost("somehost", 80);
248 final HttpRoute route = new HttpRoute(target);
249
250 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
251
252 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
253 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
254 Assertions.assertNotNull(endpoint1);
255
256 Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any());
257
258 Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE);
259
260 mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND);
261
262 mgr.close();
263
264 Mockito.verify(conn, Mockito.times(1)).close(CloseMode.GRACEFUL);
265
266 final LeaseRequest connRequest2 = mgr.lease("some-id", route, null);
267 Assertions.assertThrows(IllegalStateException.class, () -> connRequest2.get(Timeout.ZERO_MILLISECONDS));
268
269
270 mgr.closeExpired();
271 mgr.closeIdle(TimeValue.ZERO_MILLISECONDS);
272 mgr.close();
273
274 Mockito.verify(conn, Mockito.times(1)).close(CloseMode.GRACEFUL);
275 }
276
277 @Test
278 public void testCloseExpired() throws Exception {
279 final HttpHost target = new HttpHost("somehost", 80);
280 final HttpRoute route = new HttpRoute(target);
281
282 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
283
284 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
285 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
286 Assertions.assertNotNull(endpoint1);
287
288 Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any());
289
290 Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE);
291
292 mgr.release(endpoint1, null, TimeValue.ofMilliseconds(10));
293
294 Assertions.assertEquals(route, mgr.getRoute());
295 Assertions.assertNull(mgr.getState());
296
297 Thread.sleep(50);
298
299 mgr.closeExpired();
300
301 Mockito.verify(conn).close(CloseMode.GRACEFUL);
302 }
303
304 @Test
305 public void testCloseIdle() throws Exception {
306 final HttpHost target = new HttpHost("somehost", 80);
307 final HttpRoute route = new HttpRoute(target);
308
309 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
310
311 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
312 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
313 Assertions.assertNotNull(endpoint1);
314
315 Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.any());
316
317 Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE);
318
319 mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND);
320
321 Assertions.assertEquals(route, mgr.getRoute());
322 Assertions.assertNull(mgr.getState());
323
324 Thread.sleep(100);
325
326 mgr.closeIdle(TimeValue.ofMilliseconds(50));
327
328 Mockito.verify(conn).close(CloseMode.GRACEFUL);
329 }
330
331 @Test
332 public void testAlreadyLeased() throws Exception {
333 final HttpHost target = new HttpHost("somehost", 80);
334 final HttpRoute route = new HttpRoute(target);
335
336 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
337
338 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
339 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
340 Assertions.assertNotNull(endpoint1);
341 mgr.release(endpoint1, null, TimeValue.ofMilliseconds(100));
342
343 mgr.getConnection(route, null);
344 Assertions.assertThrows(IllegalStateException.class, () ->
345 mgr.getConnection(route, null));
346 }
347
348 @Test
349 public void testTargetConnect() throws Exception {
350 final HttpHost target = new HttpHost("https", "somehost", 443);
351 final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
352 final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
353 final HttpRoute route = new HttpRoute(target, local, true);
354
355 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
356
357 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
358 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
359 Assertions.assertNotNull(endpoint1);
360
361 final HttpClientContext context = HttpClientContext.create();
362 final SocketConfig sconfig = SocketConfig.custom().build();
363
364 mgr.setSocketConfig(sconfig);
365
366 final ConnectionConfig connectionConfig = ConnectionConfig.custom()
367 .setConnectTimeout(234, TimeUnit.MILLISECONDS)
368 .build();
369 mgr.setConnectionConfig(connectionConfig);
370 final TlsConfig tlsConfig = TlsConfig.custom()
371 .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
372 .build();
373 mgr.setTlsConfig(tlsConfig);
374
375 Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] {remote});
376 Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
377 Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(plainSocketFactory);
378 Mockito.when(plainSocketFactory.createSocket(Mockito.any(), Mockito.any())).thenReturn(socket);
379 Mockito.when(plainSocketFactory.connectSocket(
380 Mockito.eq(socket),
381 Mockito.any(),
382 Mockito.any(),
383 Mockito.any(),
384 Mockito.any(),
385 Mockito.any(),
386 Mockito.any())).thenReturn(socket);
387
388 mgr.connect(endpoint1, null, context);
389
390 Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost");
391 Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
392 Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(null, context);
393 Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(
394 socket,
395 target,
396 new InetSocketAddress(remote, 8443),
397 new InetSocketAddress(local, 0),
398 Timeout.ofMilliseconds(234),
399 tlsConfig,
400 context);
401
402 mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
403
404 Mockito.verify(dnsResolver, Mockito.times(2)).resolve("somehost");
405 Mockito.verify(schemePortResolver, Mockito.times(2)).resolve(target);
406 Mockito.verify(plainSocketFactory, Mockito.times(2)).createSocket(null, context);
407 Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(
408 socket,
409 target,
410 new InetSocketAddress(remote, 8443),
411 new InetSocketAddress(local, 0),
412 Timeout.ofMilliseconds(123),
413 tlsConfig,
414 context);
415 }
416
417 @Test
418 public void testProxyConnectAndUpgrade() throws Exception {
419 final HttpHost target = new HttpHost("https", "somehost", 443);
420 final HttpHost proxy = new HttpHost("someproxy", 8080);
421 final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
422 final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
423 final HttpRoute route = new HttpRoute(target, local, proxy, true);
424
425 Mockito.when(connFactory.createConnection(Mockito.any())).thenReturn(conn);
426
427 final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
428 final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
429 Assertions.assertNotNull(endpoint1);
430
431 final HttpClientContext context = HttpClientContext.create();
432 final SocketConfig sconfig = SocketConfig.custom().build();
433
434 mgr.setSocketConfig(sconfig);
435
436 final ConnectionConfig connectionConfig = ConnectionConfig.custom()
437 .setConnectTimeout(234, TimeUnit.MILLISECONDS)
438 .build();
439 mgr.setConnectionConfig(connectionConfig);
440 final TlsConfig tlsConfig = TlsConfig.custom()
441 .setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
442 .build();
443 mgr.setTlsConfig(tlsConfig);
444
445 Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote});
446 Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080);
447 Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
448 Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
449 Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslSocketFactory);
450 Mockito.when(plainSocketFactory.createSocket(Mockito.any(), Mockito.any())).thenReturn(socket);
451 Mockito.when(plainSocketFactory.connectSocket(
452 Mockito.eq(socket),
453 Mockito.any(),
454 Mockito.any(),
455 Mockito.any(),
456 Mockito.any(),
457 Mockito.any(),
458 Mockito.any())).thenReturn(socket);
459
460 mgr.connect(endpoint1, null, context);
461
462 Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy");
463 Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy);
464 Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(null, context);
465 Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(
466 socket,
467 proxy,
468 new InetSocketAddress(remote, 8080),
469 new InetSocketAddress(local, 0),
470 Timeout.ofMilliseconds(234),
471 tlsConfig,
472 context);
473
474 Mockito.when(conn.getSocket()).thenReturn(socket);
475
476 mgr.upgrade(endpoint1, context);
477
478 Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
479 Mockito.verify(sslSocketFactory, Mockito.times(1)).createLayeredSocket(
480 socket, "somehost", 8443, tlsConfig, context);
481 }
482
483 }