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