View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
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  
34  import org.apache.hc.client5.http.DnsResolver;
35  import org.apache.hc.client5.http.HttpRoute;
36  import org.apache.hc.client5.http.SchemePortResolver;
37  import org.apache.hc.client5.http.io.ConnectionEndpoint;
38  import org.apache.hc.client5.http.io.LeaseRequest;
39  import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
40  import org.apache.hc.client5.http.protocol.HttpClientContext;
41  import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
42  import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
43  import org.apache.hc.core5.http.HttpHost;
44  import org.apache.hc.core5.http.config.Lookup;
45  import org.apache.hc.core5.http.io.HttpConnectionFactory;
46  import org.apache.hc.core5.http.io.SocketConfig;
47  import org.apache.hc.core5.http.protocol.HttpContext;
48  import org.apache.hc.core5.io.CloseMode;
49  import org.apache.hc.core5.util.TimeValue;
50  import org.apache.hc.core5.util.Timeout;
51  import org.junit.Assert;
52  import org.junit.Before;
53  import org.junit.Test;
54  import org.mockito.Mock;
55  import org.mockito.Mockito;
56  import org.mockito.MockitoAnnotations;
57  
58  @SuppressWarnings({"boxing","static-access"}) // test code
59  public class TestBasicHttpClientConnectionManager {
60  
61      @Mock
62      private ManagedHttpClientConnection conn;
63      @Mock
64      private HttpConnectionFactory<ManagedHttpClientConnection> connFactory;
65      @Mock
66      private Lookup<ConnectionSocketFactory> socketFactoryRegistry;
67      @Mock
68      private ConnectionSocketFactory plainSocketFactory;
69      @Mock
70      private LayeredConnectionSocketFactory sslSocketFactory;
71      @Mock
72      private Socket socket;
73      @Mock
74      private SchemePortResolver schemePortResolver;
75      @Mock
76      private DnsResolver dnsResolver;
77  
78      private BasicHttpClientConnectionManager mgr;
79  
80      @Before
81      public void setup() throws Exception {
82          MockitoAnnotations.initMocks(this);
83          mgr = new BasicHttpClientConnectionManager(
84                  socketFactoryRegistry, connFactory, schemePortResolver, dnsResolver);
85      }
86  
87      @Test
88      public void testLeaseReleaseNonReusable() throws Exception {
89          final HttpHost target = new HttpHost("localhost", 80);
90          final HttpRoute route = new HttpRoute(target);
91  
92          Mockito.when(connFactory.createConnection(Mockito.<Socket>any())).thenReturn(conn);
93  
94          final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
95          final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
96          Assert.assertNotNull(endpoint1);
97          Assert.assertFalse(endpoint1.isConnected());
98  
99          mgr.release(endpoint1, null, TimeValue.ofMilliseconds(100));
100 
101         Assert.assertNull(mgr.getRoute());
102         Assert.assertNull(mgr.getState());
103 
104         final LeaseRequest connRequest2 = mgr.lease("some-id", route, null);
105         final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS);
106         Assert.assertNotNull(conn2);
107         Assert.assertFalse(conn2.isConnected());
108 
109         Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.<Socket>any());
110     }
111 
112     @Test
113     public void testLeaseReleaseReusable() throws Exception {
114         final HttpHost target = new HttpHost("somehost", 80);
115         final HttpRoute route = new HttpRoute(target);
116 
117         Mockito.when(connFactory.createConnection(Mockito.<Socket>any())).thenReturn(conn);
118 
119         final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
120         final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
121         Assert.assertNotNull(endpoint1);
122 
123         Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>any());
124 
125         Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE);
126 
127         mgr.release(endpoint1, null, TimeValue.ofMilliseconds(100));
128 
129         Assert.assertEquals(route, mgr.getRoute());
130         Assert.assertEquals(null, mgr.getState());
131 
132         final LeaseRequest connRequest2 = mgr.lease("some-id", route, null);
133         final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS);
134         Assert.assertNotNull(conn2);
135         Assert.assertTrue(conn2.isConnected());
136 
137         Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>any());
138     }
139 
140     @Test
141     public void testLeaseReleaseReusableWithState() throws Exception {
142         final HttpHost target = new HttpHost("somehost", 80);
143         final HttpRoute route = new HttpRoute(target);
144 
145         Mockito.when(connFactory.createConnection(Mockito.<Socket>any())).thenReturn(conn);
146 
147         final LeaseRequest connRequest1 = mgr.lease("some-id", route, "some state");
148         final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
149         Assert.assertNotNull(endpoint1);
150 
151         Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>any());
152 
153         Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE);
154 
155         mgr.release(endpoint1, "some other state", TimeValue.ofMilliseconds(10000));
156 
157         Assert.assertEquals(route, mgr.getRoute());
158         Assert.assertEquals("some other state", mgr.getState());
159 
160         final LeaseRequest connRequest2 = mgr.lease("some-id", route, "some other state");
161         final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS);
162         Assert.assertNotNull(conn2);
163         Assert.assertTrue(conn2.isConnected());
164 
165         Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>any());
166     }
167 
168     @Test
169     public void testLeaseDifferentRoute() throws Exception {
170         final HttpHost target1 = new HttpHost("somehost", 80);
171         final HttpRoute route1 = new HttpRoute(target1);
172 
173         Mockito.when(connFactory.createConnection(Mockito.<Socket>any())).thenReturn(conn);
174 
175         final LeaseRequest connRequest1 = mgr.lease("some-id", route1, null);
176         final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
177         Assert.assertNotNull(endpoint1);
178 
179         Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>any());
180 
181         Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE);
182 
183         mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND);
184 
185         Assert.assertEquals(route1, mgr.getRoute());
186         Assert.assertEquals(null, mgr.getState());
187 
188         final HttpHost target2 = new HttpHost("otherhost", 80);
189         final HttpRoute route2 = new HttpRoute(target2);
190         final LeaseRequest connRequest2 = mgr.lease("some-id", route2, null);
191         final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS);
192         Assert.assertNotNull(conn2);
193         Assert.assertFalse(conn2.isConnected());
194 
195         Mockito.verify(conn).close(CloseMode.GRACEFUL);
196         Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.<Socket>any());
197     }
198 
199     @Test
200     public void testLeaseExpired() throws Exception {
201         final HttpHost target = new HttpHost("somehost", 80);
202         final HttpRoute route = new HttpRoute(target);
203 
204         Mockito.when(connFactory.createConnection(Mockito.<Socket>any())).thenReturn(conn);
205 
206         final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
207         final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
208         Assert.assertNotNull(endpoint1);
209 
210         Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>any());
211 
212         Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE);
213 
214         mgr.release(endpoint1, null, TimeValue.ofMilliseconds(10));
215 
216         Assert.assertEquals(route, mgr.getRoute());
217         Assert.assertEquals(null, mgr.getState());
218 
219         Thread.sleep(50);
220 
221         final LeaseRequest connRequest2 = mgr.lease("some-id", route, null);
222         final ConnectionEndpoint conn2 = connRequest2.get(Timeout.ZERO_MILLISECONDS);
223         Assert.assertNotNull(conn2);
224         Assert.assertFalse(conn2.isConnected());
225 
226         Mockito.verify(conn).close(CloseMode.GRACEFUL);
227         Mockito.verify(connFactory, Mockito.times(2)).createConnection(Mockito.<Socket>any());
228     }
229 
230     @Test(expected=NullPointerException.class)
231     public void testReleaseInvalidArg() throws Exception {
232         mgr.release(null, null, TimeValue.NEG_ONE_MILLISECOND);
233     }
234 
235     @Test(expected=IllegalStateException.class)
236     public void testReleaseAnotherConnection() throws Exception {
237         final ConnectionEndpoint wrongCon = Mockito.mock(ConnectionEndpoint.class);
238         mgr.release(wrongCon, null, TimeValue.NEG_ONE_MILLISECOND);
239     }
240 
241     @Test
242     public void testShutdown() throws Exception {
243         final HttpHost target = new HttpHost("somehost", 80);
244         final HttpRoute route = new HttpRoute(target);
245 
246         Mockito.when(connFactory.createConnection(Mockito.<Socket>any())).thenReturn(conn);
247 
248         final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
249         final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
250         Assert.assertNotNull(endpoint1);
251 
252         Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>any());
253 
254         Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE);
255 
256         mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND);
257 
258         mgr.close();
259 
260         Mockito.verify(conn, Mockito.times(1)).close(CloseMode.GRACEFUL);
261 
262         try {
263             final LeaseRequest connRequest2 = mgr.lease("some-id", route, null);
264             connRequest2.get(Timeout.ZERO_MILLISECONDS);
265             Assert.fail("IllegalStateException expected");
266         } catch (final IllegalStateException ex) {
267         }
268 
269         // Should have no effect
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.<Socket>any())).thenReturn(conn);
283 
284         final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
285         final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
286         Assert.assertNotNull(endpoint1);
287 
288         Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>any());
289 
290         Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE);
291 
292         mgr.release(endpoint1, null, TimeValue.ofMilliseconds(10));
293 
294         Assert.assertEquals(route, mgr.getRoute());
295         Assert.assertEquals(null, 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.<Socket>any())).thenReturn(conn);
310 
311         final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
312         final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
313         Assert.assertNotNull(endpoint1);
314 
315         Mockito.verify(connFactory, Mockito.times(1)).createConnection(Mockito.<Socket>any());
316 
317         Mockito.when(conn.isOpen()).thenReturn(Boolean.TRUE, Boolean.FALSE);
318 
319         mgr.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND);
320 
321         Assert.assertEquals(route, mgr.getRoute());
322         Assert.assertEquals(null, 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(expected=IllegalStateException.class)
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.<Socket>any())).thenReturn(conn);
337 
338         final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
339         final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
340         Assert.assertNotNull(endpoint1);
341         mgr.release(endpoint1, null, TimeValue.ofMilliseconds(100));
342 
343         mgr.getConnection(route, null);
344         mgr.getConnection(route, null);
345     }
346 
347     @Test
348     public void testTargetConnect() throws Exception {
349         final HttpHost target = new HttpHost("https", "somehost", 443);
350         final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
351         final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
352         final HttpRoute route = new HttpRoute(target, local, true);
353 
354         Mockito.when(connFactory.createConnection(Mockito.<Socket>any())).thenReturn(conn);
355 
356         final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
357         final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
358         Assert.assertNotNull(endpoint1);
359 
360         final HttpClientContext context = HttpClientContext.create();
361         final SocketConfig sconfig = SocketConfig.custom().build();
362 
363         mgr.setSocketConfig(sconfig);
364 
365         Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] {remote});
366         Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
367         Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(plainSocketFactory);
368         Mockito.when(plainSocketFactory.createSocket(Mockito.<HttpContext>any())).thenReturn(socket);
369         Mockito.when(plainSocketFactory.connectSocket(
370                 Mockito.<TimeValue>any(),
371                 Mockito.eq(socket),
372                 Mockito.<HttpHost>any(),
373                 Mockito.<InetSocketAddress>any(),
374                 Mockito.<InetSocketAddress>any(),
375                 Mockito.<HttpContext>any())).thenReturn(socket);
376 
377         mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
378 
379         Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost");
380         Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
381         Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(context);
382         Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(TimeValue.ofMilliseconds(123), socket, target,
383                 new InetSocketAddress(remote, 8443),
384                 new InetSocketAddress(local, 0), context);
385     }
386 
387     @Test
388     public void testProxyConnectAndUpgrade() throws Exception {
389         final HttpHost target = new HttpHost("https", "somehost", 443);
390         final HttpHost proxy = new HttpHost("someproxy", 8080);
391         final InetAddress remote = InetAddress.getByAddress(new byte[] {10, 0, 0, 1});
392         final InetAddress local = InetAddress.getByAddress(new byte[] {127, 0, 0, 1});
393         final HttpRoute route = new HttpRoute(target, local, proxy, true);
394 
395         Mockito.when(connFactory.createConnection(Mockito.<Socket>any())).thenReturn(conn);
396 
397         final LeaseRequest connRequest1 = mgr.lease("some-id", route, null);
398         final ConnectionEndpoint endpoint1 = connRequest1.get(Timeout.ZERO_MILLISECONDS);
399         Assert.assertNotNull(endpoint1);
400 
401         final HttpClientContext context = HttpClientContext.create();
402         final SocketConfig sconfig = SocketConfig.custom().build();
403 
404         mgr.setSocketConfig(sconfig);
405 
406         Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote});
407         Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080);
408         Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
409         Mockito.when(socketFactoryRegistry.lookup("http")).thenReturn(plainSocketFactory);
410         Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslSocketFactory);
411         Mockito.when(plainSocketFactory.createSocket(Mockito.<HttpContext>any())).thenReturn(socket);
412         Mockito.when(plainSocketFactory.connectSocket(
413                 Mockito.<TimeValue>any(),
414                 Mockito.eq(socket),
415                 Mockito.<HttpHost>any(),
416                 Mockito.<InetSocketAddress>any(),
417                 Mockito.<InetSocketAddress>any(),
418                 Mockito.<HttpContext>any())).thenReturn(socket);
419 
420         mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
421 
422         Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy");
423         Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy);
424         Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(context);
425         Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(TimeValue.ofMilliseconds(123), socket, proxy,
426                 new InetSocketAddress(remote, 8080),
427                 new InetSocketAddress(local, 0), context);
428 
429         Mockito.when(conn.getSocket()).thenReturn(socket);
430 
431         mgr.upgrade(endpoint1, context);
432 
433         Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
434         Mockito.verify(sslSocketFactory, Mockito.times(1)).createLayeredSocket(
435                 socket, "somehost", 8443, context);
436     }
437 
438 }