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