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.nio;
28
29 import java.util.concurrent.Future;
30 import java.util.concurrent.atomic.AtomicReference;
31
32 import org.apache.hc.core5.concurrent.BasicFuture;
33 import org.apache.hc.core5.concurrent.FutureCallback;
34 import org.apache.hc.core5.http.HttpConnection;
35 import org.apache.hc.core5.http.HttpVersion;
36 import org.apache.hc.core5.io.CloseMode;
37 import org.apache.hc.core5.pool.ManagedConnPool;
38 import org.apache.hc.core5.pool.PoolEntry;
39 import org.apache.hc.core5.util.Timeout;
40 import org.junit.jupiter.api.Assertions;
41 import org.junit.jupiter.api.BeforeEach;
42 import org.junit.jupiter.api.Test;
43 import org.mockito.Mock;
44 import org.mockito.Mockito;
45 import org.mockito.MockitoAnnotations;
46
47 public class H2SharingConnPoolTest {
48
49 static final String DEFAULT_ROUTE = "DEFAULT_ROUTE";
50
51 @Mock
52 ManagedConnPool<String, HttpConnection> connPool;
53 @Mock
54 FutureCallback<PoolEntry<String, HttpConnection>> callback;
55 @Mock
56 HttpConnection connection;
57 H2SharingConnPool<String, HttpConnection> h2SharingPool;
58
59 @BeforeEach
60 void setup() {
61 MockitoAnnotations.openMocks(this);
62 h2SharingPool = new H2SharingConnPool<>(connPool);
63 }
64
65 @Test
66 void testLeaseFutureReturned() throws Exception {
67 Mockito.when(connPool.lease(
68 Mockito.eq(DEFAULT_ROUTE),
69 Mockito.any(),
70 Mockito.any(),
71 Mockito.any())).thenReturn(new BasicFuture<>(null));
72
73 final Future<PoolEntry<String, HttpConnection>> result = h2SharingPool.lease(DEFAULT_ROUTE, null, Timeout.ONE_MILLISECOND, callback);
74 Assertions.assertNotNull(result);
75 Assertions.assertFalse(result.isDone());
76
77 Mockito.verify(connPool).lease(
78 Mockito.eq(DEFAULT_ROUTE),
79 Mockito.eq(null),
80 Mockito.eq(Timeout.ONE_MILLISECOND),
81 Mockito.any());
82 Mockito.verify(callback, Mockito.never()).completed(
83 Mockito.any());
84 }
85
86 @Test
87 void testLeaseExistingConnectionReturned() throws Exception {
88 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
89 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
90 routePool.track(poolEntry);
91
92 final Future<PoolEntry<String, HttpConnection>> future = h2SharingPool.lease(DEFAULT_ROUTE, null, Timeout.ONE_MILLISECOND, callback);
93 Assertions.assertNotNull(future);
94 Assertions.assertSame(poolEntry, future.get());
95
96 Mockito.verify(connPool, Mockito.never()).lease(
97 Mockito.eq(DEFAULT_ROUTE),
98 Mockito.any(),
99 Mockito.any(),
100 Mockito.any());
101 Mockito.verify(callback).completed(
102 Mockito.same(poolEntry));
103 }
104
105 @Test
106 void testLeaseWithStateCacheBypassed() throws Exception {
107 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
108 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
109 routePool.track(poolEntry);
110
111 Mockito.when(connPool.lease(
112 Mockito.eq(DEFAULT_ROUTE),
113 Mockito.any(),
114 Mockito.any(),
115 Mockito.any())).thenReturn(new BasicFuture<>(null));
116
117 final Future<PoolEntry<String, HttpConnection>> result = h2SharingPool.lease(DEFAULT_ROUTE, "stuff", Timeout.ONE_MILLISECOND, callback);
118 Assertions.assertNotNull(result);
119 Assertions.assertFalse(result.isDone());
120
121 Mockito.verify(connPool).lease(
122 Mockito.eq(DEFAULT_ROUTE),
123 Mockito.eq("stuff"),
124 Mockito.eq(Timeout.ONE_MILLISECOND),
125 Mockito.any());
126 Mockito.verify(callback, Mockito.never()).completed(
127 Mockito.any());
128 }
129
130 @Test
131 void testLeaseNewConnectionReturnedAndCached() throws Exception {
132 final AtomicReference<BasicFuture<PoolEntry<String, HttpConnection>>> futureRef = new AtomicReference<>();
133 Mockito.when(connPool.lease(
134 Mockito.eq(DEFAULT_ROUTE),
135 Mockito.any(),
136 Mockito.any(),
137 Mockito.any())).thenAnswer(invocationOnMock -> {
138 final BasicFuture<PoolEntry<String, HttpConnection>> future = new BasicFuture<>(invocationOnMock.getArgument(3));
139 futureRef.set(future);
140 return future;
141 });
142
143 final Future<PoolEntry<String, HttpConnection>> result = h2SharingPool.lease(DEFAULT_ROUTE, null, Timeout.ONE_MILLISECOND, callback);
144 final BasicFuture<PoolEntry<String, HttpConnection>> future = futureRef.get();
145 Assertions.assertNotNull(future);
146
147 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
148 poolEntry.assignConnection(connection);
149 Mockito.when(connection.getProtocolVersion()).thenReturn(HttpVersion.HTTP_2);
150 future.completed(poolEntry);
151
152 Assertions.assertTrue(result.isDone());
153
154 Mockito.verify(connPool).lease(
155 Mockito.eq(DEFAULT_ROUTE),
156 Mockito.eq(null),
157 Mockito.eq(Timeout.ONE_MILLISECOND),
158 Mockito.any());
159 Mockito.verify(callback).completed(
160 Mockito.any());
161
162 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
163 Assertions.assertEquals(1, routePool.getCount(poolEntry));
164 }
165
166 @Test
167 void testLeaseNewConnectionReturnedAndNotCached() throws Exception {
168 final AtomicReference<BasicFuture<PoolEntry<String, HttpConnection>>> futureRef = new AtomicReference<>();
169 Mockito.when(connPool.lease(
170 Mockito.eq(DEFAULT_ROUTE),
171 Mockito.any(),
172 Mockito.any(),
173 Mockito.any())).thenAnswer(invocationOnMock -> {
174 final BasicFuture<PoolEntry<String, HttpConnection>> future = new BasicFuture<>(invocationOnMock.getArgument(3));
175 futureRef.set(future);
176 return future;
177 });
178
179 final Future<PoolEntry<String, HttpConnection>> result = h2SharingPool.lease(DEFAULT_ROUTE, null, Timeout.ONE_MILLISECOND, callback);
180 final BasicFuture<PoolEntry<String, HttpConnection>> future = futureRef.get();
181 Assertions.assertNotNull(future);
182
183 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
184 poolEntry.assignConnection(connection);
185 Mockito.when(connection.getProtocolVersion()).thenReturn(HttpVersion.HTTP_1_1);
186 future.completed(poolEntry);
187
188 Assertions.assertTrue(result.isDone());
189
190 Mockito.verify(connPool).lease(
191 Mockito.eq(DEFAULT_ROUTE),
192 Mockito.eq(null),
193 Mockito.eq(Timeout.ONE_MILLISECOND),
194 Mockito.any());
195 Mockito.verify(callback).completed(
196 Mockito.any());
197
198 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
199 Assertions.assertEquals(0, routePool.getCount(poolEntry));
200 }
201
202 @Test
203 void testLeaseNoConnection() throws Exception {
204 final AtomicReference<BasicFuture<PoolEntry<String, HttpConnection>>> futureRef = new AtomicReference<>();
205 Mockito.when(connPool.lease(
206 Mockito.eq(DEFAULT_ROUTE),
207 Mockito.any(),
208 Mockito.any(),
209 Mockito.any())).thenAnswer(invocationOnMock -> {
210 final BasicFuture<PoolEntry<String, HttpConnection>> future = new BasicFuture<>(invocationOnMock.getArgument(3));
211 futureRef.set(future);
212 return future;
213 });
214
215 final Future<PoolEntry<String, HttpConnection>> result = h2SharingPool.lease(DEFAULT_ROUTE, null, Timeout.ONE_MILLISECOND, callback);
216 final BasicFuture<PoolEntry<String, HttpConnection>> future = futureRef.get();
217 Assertions.assertNotNull(future);
218
219 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
220 poolEntry.discardConnection(CloseMode.IMMEDIATE);
221 future.completed(poolEntry);
222
223 Assertions.assertTrue(result.isDone());
224
225 Mockito.verify(connPool).lease(
226 Mockito.eq(DEFAULT_ROUTE),
227 Mockito.eq(null),
228 Mockito.eq(Timeout.ONE_MILLISECOND),
229 Mockito.any());
230 Mockito.verify(callback).completed(
231 Mockito.any());
232
233 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
234 Assertions.assertEquals(0, routePool.getCount(poolEntry));
235 }
236
237 @Test
238 void testLeaseWithStateNewConnectionReturnedAndNotCached() throws Exception {
239 final AtomicReference<BasicFuture<PoolEntry<String, HttpConnection>>> futureRef = new AtomicReference<>();
240 Mockito.when(connPool.lease(
241 Mockito.eq(DEFAULT_ROUTE),
242 Mockito.any(),
243 Mockito.any(),
244 Mockito.any())).thenAnswer(invocationOnMock -> {
245 final BasicFuture<PoolEntry<String, HttpConnection>> future = new BasicFuture<>(invocationOnMock.getArgument(3));
246 futureRef.set(future);
247 return future;
248 });
249
250 final Future<PoolEntry<String, HttpConnection>> result = h2SharingPool.lease(DEFAULT_ROUTE, "stuff", Timeout.ONE_MILLISECOND, callback);
251 final BasicFuture<PoolEntry<String, HttpConnection>> future = futureRef.get();
252 Assertions.assertNotNull(future);
253
254 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
255 poolEntry.assignConnection(connection);
256 Mockito.when(connection.getProtocolVersion()).thenReturn(HttpVersion.HTTP_2);
257 future.completed(poolEntry);
258
259 Assertions.assertTrue(result.isDone());
260
261 Mockito.verify(connPool).lease(
262 Mockito.eq(DEFAULT_ROUTE),
263 Mockito.eq("stuff"),
264 Mockito.eq(Timeout.ONE_MILLISECOND),
265 Mockito.any());
266 Mockito.verify(callback).completed(
267 Mockito.any());
268
269 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
270 Assertions.assertEquals(0, routePool.getCount(poolEntry));
271 }
272
273 @Test
274 void testReleaseReusableNoCacheReturnedToPool() throws Exception {
275 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
276 poolEntry.assignConnection(connection);
277 Mockito.when(connection.isOpen()).thenReturn(true);
278
279 h2SharingPool.release(poolEntry, true);
280
281 Mockito.verify(connPool).release(
282 Mockito.same(poolEntry),
283 Mockito.eq(true));
284 }
285
286 @Test
287 void testReleaseReusableNotInCacheReturnedToPool() throws Exception {
288 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
289 poolEntry.assignConnection(connection);
290 Mockito.when(connection.isOpen()).thenReturn(true);
291 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
292 routePool.track(poolEntry);
293
294 h2SharingPool.release(poolEntry, true);
295
296 Mockito.verify(connPool).release(
297 Mockito.same(poolEntry),
298 Mockito.eq(true));
299 }
300
301 @Test
302 void testReleaseReusableInCacheNotReturnedToPool() throws Exception {
303 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
304 poolEntry.assignConnection(connection);
305 Mockito.when(connection.isOpen()).thenReturn(true);
306 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
307 routePool.track(poolEntry);
308 routePool.track(poolEntry);
309
310 h2SharingPool.release(poolEntry, true);
311
312 Mockito.verify(connPool, Mockito.never()).release(
313 Mockito.same(poolEntry),
314 Mockito.anyBoolean());
315 }
316
317 @Test
318 void testReleaseNonReusableInCacheReturnedToPool() throws Exception {
319 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
320 poolEntry.assignConnection(connection);
321 Mockito.when(connection.isOpen()).thenReturn(true);
322 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
323 routePool.track(poolEntry);
324 routePool.track(poolEntry);
325
326 h2SharingPool.release(poolEntry, false);
327
328 Mockito.verify(connPool).release(
329 Mockito.same(poolEntry),
330 Mockito.eq(false));
331 }
332
333 @Test
334 void testReleaseReusableAndClosedInCacheReturnedToPool() throws Exception {
335 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
336 poolEntry.assignConnection(connection);
337 Mockito.when(connection.isOpen()).thenReturn(false);
338 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
339 routePool.track(poolEntry);
340 routePool.track(poolEntry);
341
342 h2SharingPool.release(poolEntry, true);
343
344 Mockito.verify(connPool).release(
345 Mockito.same(poolEntry),
346 Mockito.eq(true));
347 }
348
349 @Test
350 void testClose() throws Exception {
351 h2SharingPool.close();
352
353 Mockito.verify(connPool).close();
354 }
355
356 @Test
357 void testCloseMode() throws Exception {
358 h2SharingPool.close(CloseMode.IMMEDIATE);
359
360 Mockito.verify(connPool).close(CloseMode.IMMEDIATE);
361 }
362
363 @Test
364 void testLeasePoolClosed() throws Exception {
365 h2SharingPool.close();
366
367 Assertions.assertThrows(IllegalStateException.class, () -> h2SharingPool.lease(DEFAULT_ROUTE, null, Timeout.ONE_MILLISECOND, callback));
368 }
369
370 @Test
371 void testReleasePoolClosed() throws Exception {
372 final PoolEntry<String, HttpConnection> poolEntry = new PoolEntry<>(DEFAULT_ROUTE);
373 poolEntry.assignConnection(connection);
374 Mockito.when(connection.isOpen()).thenReturn(false);
375 final H2SharingConnPool.PerRoutePool<String, HttpConnection> routePool = h2SharingPool.getPerRoutePool(DEFAULT_ROUTE);
376 routePool.track(poolEntry);
377
378 h2SharingPool.close();
379
380 h2SharingPool.release(poolEntry, true);
381
382 Mockito.verify(connPool).release(
383 Mockito.same(poolEntry),
384 Mockito.eq(true));
385 }
386
387 }