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  package org.apache.hc.client5.http.impl.classic;
28  
29  import java.util.concurrent.ExecutionException;
30  import java.util.concurrent.TimeUnit;
31  import java.util.concurrent.TimeoutException;
32  
33  import org.apache.hc.client5.http.HttpRoute;
34  import org.apache.hc.client5.http.config.RequestConfig;
35  import org.apache.hc.client5.http.io.ConnectionEndpoint;
36  import org.apache.hc.client5.http.io.HttpClientConnectionManager;
37  import org.apache.hc.client5.http.io.LeaseRequest;
38  import org.apache.hc.client5.http.protocol.HttpClientContext;
39  import org.apache.hc.core5.concurrent.CancellableDependency;
40  import org.apache.hc.core5.http.ConnectionRequestTimeoutException;
41  import org.apache.hc.core5.http.HttpHost;
42  import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
43  import org.apache.hc.core5.io.CloseMode;
44  import org.apache.hc.core5.util.TimeValue;
45  import org.apache.hc.core5.util.Timeout;
46  import org.junit.jupiter.api.Assertions;
47  import org.junit.jupiter.api.BeforeEach;
48  import org.junit.jupiter.api.Test;
49  import org.mockito.Mock;
50  import org.mockito.Mockito;
51  import org.mockito.MockitoAnnotations;
52  import org.slf4j.Logger;
53  
54  @SuppressWarnings({"static-access"}) // test code
55  public class TestInternalExecRuntime {
56  
57      @Mock
58      private Logger log;
59      @Mock
60      private HttpClientConnectionManager mgr;
61      @Mock
62      private LeaseRequest leaseRequest;
63      @Mock
64      private HttpRequestExecutor requestExecutor;
65      @Mock
66      private CancellableDependency cancellableDependency;
67      @Mock
68      private ConnectionEndpoint connectionEndpoint;
69  
70      private HttpRoute route;
71      private InternalExecRuntime execRuntime;
72  
73      @BeforeEach
74      public void setup() {
75          MockitoAnnotations.openMocks(this);
76          route = new HttpRoute(new HttpHost("host", 80));
77          execRuntime = new InternalExecRuntime(log, mgr, requestExecutor, cancellableDependency);
78      }
79  
80      @Test
81      public void testAcquireEndpoint() throws Exception {
82          final HttpClientContext context = HttpClientContext.create();
83          @SuppressWarnings("deprecation")
84          final RequestConfig config = RequestConfig.custom()
85                  .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
86                  .setConnectTimeout(123, TimeUnit.MILLISECONDS)
87                  .build();
88          context.setRequestConfig(config);
89          final HttpRoute route = new HttpRoute(new HttpHost("host", 80));
90  
91          Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any()))
92                  .thenReturn(leaseRequest);
93          Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint);
94  
95          execRuntime.acquireEndpoint("some-id", route, null, context);
96  
97          Assertions.assertTrue(execRuntime.isEndpointAcquired());
98          Assertions.assertSame(connectionEndpoint, execRuntime.ensureValid());
99          Assertions.assertFalse(execRuntime.isEndpointConnected());
100         Assertions.assertFalse(execRuntime.isConnectionReusable());
101 
102         Mockito.verify(leaseRequest).get(Timeout.ofMilliseconds(345));
103         Mockito.verify(cancellableDependency, Mockito.times(1)).setDependency(leaseRequest);
104         Mockito.verify(cancellableDependency, Mockito.times(1)).setDependency(execRuntime);
105         Mockito.verify(cancellableDependency, Mockito.times(2)).setDependency(Mockito.any());
106     }
107 
108     @Test
109     public void testAcquireEndpointAlreadyAcquired() throws Exception {
110         final HttpClientContext context = HttpClientContext.create();
111 
112         Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any()))
113                 .thenReturn(leaseRequest);
114         Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint);
115 
116         execRuntime.acquireEndpoint("some-id", route, null, context);
117 
118         Assertions.assertTrue(execRuntime.isEndpointAcquired());
119         Assertions.assertSame(connectionEndpoint, execRuntime.ensureValid());
120 
121         Assertions.assertThrows(IllegalStateException.class, () ->
122                 execRuntime.acquireEndpoint("some-id", route, null, context));
123     }
124 
125     @Test
126     public void testAcquireEndpointLeaseRequestTimeout() throws Exception {
127         final HttpClientContext context = HttpClientContext.create();
128 
129         Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any()))
130                 .thenReturn(leaseRequest);
131         Mockito.when(leaseRequest.get(Mockito.any())).thenThrow(new TimeoutException("timeout"));
132 
133         Assertions.assertThrows(ConnectionRequestTimeoutException.class, () ->
134                 execRuntime.acquireEndpoint("some-id", route, null, context));
135     }
136 
137     @Test
138     public void testAcquireEndpointLeaseRequestFailure() throws Exception {
139         final HttpClientContext context = HttpClientContext.create();
140 
141         Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any()))
142                 .thenReturn(leaseRequest);
143         Mockito.when(leaseRequest.get(Mockito.any())).thenThrow(new ExecutionException(new IllegalStateException()));
144 
145         Assertions.assertThrows(RequestFailedException.class, () ->
146                 execRuntime.acquireEndpoint("some-id", route, null, context));
147     }
148 
149     @Test
150     public void testAbortEndpoint() throws Exception {
151         final HttpClientContext context = HttpClientContext.create();
152         Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any()))
153                 .thenReturn(leaseRequest);
154         Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint);
155 
156         execRuntime.acquireEndpoint("some-id", new HttpRoute(new HttpHost("host", 80)), null, context);
157         Assertions.assertTrue(execRuntime.isEndpointAcquired());
158         execRuntime.discardEndpoint();
159 
160         Assertions.assertFalse(execRuntime.isEndpointAcquired());
161 
162         Mockito.verify(connectionEndpoint).close(CloseMode.IMMEDIATE);
163         Mockito.verify(mgr).release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS);
164 
165         execRuntime.discardEndpoint();
166 
167         Mockito.verify(connectionEndpoint, Mockito.times(1)).close(CloseMode.IMMEDIATE);
168         Mockito.verify(mgr, Mockito.times(1)).release(
169                 Mockito.any(),
170                 Mockito.any(),
171                 Mockito.any());
172     }
173 
174     @Test
175     public void testCancell() throws Exception {
176         final HttpClientContext context = HttpClientContext.create();
177 
178         Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any()))
179                 .thenReturn(leaseRequest);
180         Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint);
181 
182         execRuntime.acquireEndpoint("some-id", route, null, context);
183         Assertions.assertTrue(execRuntime.isEndpointAcquired());
184 
185         Assertions.assertTrue(execRuntime.cancel());
186 
187         Assertions.assertFalse(execRuntime.isEndpointAcquired());
188 
189         Mockito.verify(connectionEndpoint).close(CloseMode.IMMEDIATE);
190         Mockito.verify(mgr).release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS);
191 
192         Assertions.assertFalse(execRuntime.cancel());
193 
194         Mockito.verify(connectionEndpoint, Mockito.times(1)).close(CloseMode.IMMEDIATE);
195         Mockito.verify(mgr, Mockito.times(1)).release(
196                 Mockito.any(),
197                 Mockito.any(),
198                 Mockito.any());
199     }
200 
201     @Test
202     public void testReleaseEndpointReusable() throws Exception {
203         final HttpClientContext context = HttpClientContext.create();
204 
205         Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any()))
206                 .thenReturn(leaseRequest);
207         Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint);
208 
209         execRuntime.acquireEndpoint("some-id", route, null, context);
210         Assertions.assertTrue(execRuntime.isEndpointAcquired());
211 
212         execRuntime.markConnectionReusable("some state", TimeValue.ofMilliseconds(100000));
213 
214         execRuntime.releaseEndpoint();
215 
216         Assertions.assertFalse(execRuntime.isEndpointAcquired());
217 
218         Mockito.verify(connectionEndpoint, Mockito.never()).close();
219         Mockito.verify(mgr).release(connectionEndpoint, "some state", TimeValue.ofMilliseconds(100000));
220 
221         execRuntime.releaseEndpoint();
222 
223         Mockito.verify(mgr, Mockito.times(1)).release(
224                 Mockito.any(),
225                 Mockito.any(),
226                 Mockito.any());
227     }
228 
229     @Test
230     public void testReleaseEndpointNonReusable() throws Exception {
231         final HttpClientContext context = HttpClientContext.create();
232 
233         Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any()))
234                 .thenReturn(leaseRequest);
235         Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint);
236 
237         execRuntime.acquireEndpoint("some-id", route, null, context);
238         Assertions.assertTrue(execRuntime.isEndpointAcquired());
239 
240         execRuntime.markConnectionReusable("some state", TimeValue.ofMilliseconds(100000));
241         execRuntime.markConnectionNonReusable();
242 
243         execRuntime.releaseEndpoint();
244 
245         Assertions.assertFalse(execRuntime.isEndpointAcquired());
246 
247         Mockito.verify(connectionEndpoint, Mockito.times(1)).close(CloseMode.IMMEDIATE);
248         Mockito.verify(mgr).release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS);
249 
250         execRuntime.releaseEndpoint();
251 
252         Mockito.verify(mgr, Mockito.times(1)).release(
253                 Mockito.any(),
254                 Mockito.any(),
255                 Mockito.any());
256     }
257 
258     @Test
259     public void testConnectEndpoint() throws Exception {
260         final HttpClientContext context = HttpClientContext.create();
261         @SuppressWarnings("deprecation")
262         final RequestConfig config = RequestConfig.custom()
263                 .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
264                 .setConnectTimeout(123, TimeUnit.MILLISECONDS)
265                 .build();
266         context.setRequestConfig(config);
267 
268         Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any()))
269                 .thenReturn(leaseRequest);
270         Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint);
271 
272         execRuntime.acquireEndpoint("some-id", route, null, context);
273         Assertions.assertTrue(execRuntime.isEndpointAcquired());
274 
275         Mockito.when(connectionEndpoint.isConnected()).thenReturn(false);
276         Assertions.assertFalse(execRuntime.isEndpointConnected());
277 
278         execRuntime.connectEndpoint(context);
279 
280         Mockito.verify(mgr).connect(connectionEndpoint, Timeout.ofMilliseconds(123), context);
281     }
282 
283     @Test
284     public void testDisonnectEndpoint() throws Exception {
285         final HttpClientContext context = HttpClientContext.create();
286 
287         Mockito.when(mgr.lease(Mockito.eq("some-id"), Mockito.eq(route), Mockito.any(), Mockito.any()))
288                 .thenReturn(leaseRequest);
289         Mockito.when(leaseRequest.get(Mockito.any())).thenReturn(connectionEndpoint);
290 
291         execRuntime.acquireEndpoint("some-id", route, null, context);
292         Assertions.assertTrue(execRuntime.isEndpointAcquired());
293 
294         Mockito.when(connectionEndpoint.isConnected()).thenReturn(true);
295         Assertions.assertTrue(execRuntime.isEndpointConnected());
296 
297         execRuntime.connectEndpoint(context);
298 
299         Mockito.verify(mgr, Mockito.never()).connect(
300                 Mockito.same(connectionEndpoint), Mockito.any(), Mockito.<HttpClientContext>any());
301 
302         execRuntime.disconnectEndpoint();
303 
304         Mockito.verify(connectionEndpoint).close();
305     }
306 
307 }