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.io.ByteArrayInputStream;
30  import java.io.IOException;
31  import java.io.InterruptedIOException;
32  
33  import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
34  import org.apache.hc.client5.http.HttpRoute;
35  import org.apache.hc.client5.http.UserTokenHandler;
36  import org.apache.hc.client5.http.classic.ExecChain;
37  import org.apache.hc.client5.http.classic.ExecRuntime;
38  import org.apache.hc.client5.http.classic.methods.HttpGet;
39  import org.apache.hc.client5.http.entity.EntityBuilder;
40  import org.apache.hc.client5.http.impl.ConnectionShutdownException;
41  import org.apache.hc.client5.http.io.HttpClientConnectionManager;
42  import org.apache.hc.client5.http.protocol.HttpClientContext;
43  import org.apache.hc.core5.http.ClassicHttpRequest;
44  import org.apache.hc.core5.http.ClassicHttpResponse;
45  import org.apache.hc.core5.http.ConnectionReuseStrategy;
46  import org.apache.hc.core5.http.HttpEntity;
47  import org.apache.hc.core5.http.HttpException;
48  import org.apache.hc.core5.http.HttpHost;
49  import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
50  import org.apache.hc.core5.http.protocol.HttpProcessor;
51  import org.apache.hc.core5.util.TimeValue;
52  import org.junit.jupiter.api.Assertions;
53  import org.junit.jupiter.api.BeforeEach;
54  import org.junit.jupiter.api.Test;
55  import org.mockito.Mock;
56  import org.mockito.Mockito;
57  import org.mockito.MockitoAnnotations;
58  
59  public class TestMainClientExec {
60  
61      @Mock
62      private HttpClientConnectionManager connectionManager;
63      @Mock
64      private HttpProcessor httpProcessor;
65      @Mock
66      private ConnectionReuseStrategy reuseStrategy;
67      @Mock
68      private ConnectionKeepAliveStrategy keepAliveStrategy;
69      @Mock
70      private UserTokenHandler userTokenHandler;
71      @Mock
72      private ExecRuntime execRuntime;
73  
74      private MainClientExec mainClientExec;
75      private HttpHost target;
76  
77      @BeforeEach
78      public void setup() throws Exception {
79          MockitoAnnotations.openMocks(this);
80          mainClientExec = new MainClientExec(connectionManager, httpProcessor, reuseStrategy, keepAliveStrategy, userTokenHandler);
81          target = new HttpHost("foo", 80);
82      }
83  
84      @Test
85      public void testFundamentals() throws Exception {
86          final HttpRoute route = new HttpRoute(target);
87          final ClassicHttpRequest request = new HttpGet("/test");
88          final HttpClientContext context = HttpClientContext.create();
89  
90          final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
91          final HttpEntity responseEntity = EntityBuilder.create()
92                  .setStream(new ByteArrayInputStream(new byte[]{}))
93                  .build();
94          response.setEntity(responseEntity);
95  
96          Mockito.when(execRuntime.execute(
97                  Mockito.anyString(),
98                  Mockito.same(request),
99                  Mockito.any(),
100                 Mockito.any())).thenReturn(response);
101 
102         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
103         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
104 
105         Mockito.verify(httpProcessor).process(request, null, context);
106         Mockito.verify(execRuntime).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
107         Mockito.verify(httpProcessor).process(response, responseEntity, context);
108 
109         Assertions.assertEquals(route, context.getHttpRoute());
110         Assertions.assertSame(request, context.getRequest());
111         Assertions.assertSame(response, context.getResponse());
112     }
113 
114     @Test
115     public void testExecRequestNonPersistentConnection() throws Exception {
116         final HttpRoute route = new HttpRoute(target);
117         final HttpClientContext context = HttpClientContext.create();
118         final ClassicHttpRequest request = new HttpGet("http://bar/test");
119         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
120         response.setEntity(EntityBuilder.create()
121                 .setStream(new ByteArrayInputStream(new byte[]{}))
122                 .build());
123 
124         Mockito.when(execRuntime.execute(
125                 Mockito.anyString(),
126                 Mockito.same(request),
127                 Mockito.any(),
128                 Mockito.any())).thenReturn(response);
129         Mockito.when(reuseStrategy.keepAlive(
130                 Mockito.same(request),
131                 Mockito.same(response),
132                 Mockito.<HttpClientContext>any())).thenReturn(false);
133 
134         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
135         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
136         Mockito.verify(execRuntime).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
137         Mockito.verify(execRuntime, Mockito.times(1)).markConnectionNonReusable();
138         Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
139 
140         Assertions.assertNull(context.getUserToken());
141         Assertions.assertNotNull(finalResponse);
142         Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse);
143     }
144 
145     @Test
146     public void testExecRequestNonPersistentConnectionNoResponseEntity() throws Exception {
147         final HttpRoute route = new HttpRoute(target);
148         final HttpClientContext context = HttpClientContext.create();
149         final ClassicHttpRequest request = new HttpGet("http://bar/test");
150         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
151         response.setEntity(null);
152 
153         Mockito.when(execRuntime.execute(
154                 Mockito.anyString(),
155                 Mockito.same(request),
156                 Mockito.any(),
157                 Mockito.any())).thenReturn(response);
158         Mockito.when(reuseStrategy.keepAlive(
159                 Mockito.same(request),
160                 Mockito.same(response),
161                 Mockito.<HttpClientContext>any())).thenReturn(false);
162 
163         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
164         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
165 
166         Mockito.verify(execRuntime).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
167         Mockito.verify(execRuntime).markConnectionNonReusable();
168         Mockito.verify(execRuntime).releaseEndpoint();
169 
170         Assertions.assertNotNull(finalResponse);
171         Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse);
172     }
173 
174     @Test
175     public void testExecRequestPersistentConnection() throws Exception {
176         final HttpRoute route = new HttpRoute(target);
177         final HttpClientContext context = HttpClientContext.create();
178         final ClassicHttpRequest request = new HttpGet("http://bar/test");
179         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
180         // The entity is streaming
181         response.setEntity(EntityBuilder.create()
182                 .setStream(new ByteArrayInputStream(new byte[]{}))
183                 .build());
184 
185 
186         Mockito.when(execRuntime.execute(
187                 Mockito.anyString(),
188                 Mockito.same(request),
189                 Mockito.any(),
190                 Mockito.any())).thenReturn(response);
191         Mockito.when(reuseStrategy.keepAlive(
192                 Mockito.same(request),
193                 Mockito.same(response),
194                 Mockito.<HttpClientContext>any())).thenReturn(true);
195         Mockito.when(keepAliveStrategy.getKeepAliveDuration(
196                 Mockito.same(response),
197                 Mockito.<HttpClientContext>any())).thenReturn(TimeValue.ofMilliseconds(678L));
198 
199         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
200         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
201 
202         Mockito.verify(execRuntime).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
203         Mockito.verify(execRuntime).markConnectionReusable(null, TimeValue.ofMilliseconds(678L));
204         Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
205 
206         Assertions.assertNotNull(finalResponse);
207         Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse);
208     }
209 
210     @Test
211     public void testExecRequestPersistentConnectionNoResponseEntity() throws Exception {
212         final HttpRoute route = new HttpRoute(target);
213         final HttpClientContext context = HttpClientContext.create();
214         final ClassicHttpRequest request = new HttpGet("http://bar/test");
215         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
216 
217         Mockito.when(execRuntime.execute(
218                 Mockito.anyString(),
219                 Mockito.same(request),
220                 Mockito.any(),
221                 Mockito.any())).thenReturn(response);
222         Mockito.when(reuseStrategy.keepAlive(
223                 Mockito.same(request),
224                 Mockito.same(response),
225                 Mockito.<HttpClientContext>any())).thenReturn(true);
226         Mockito.when(keepAliveStrategy.getKeepAliveDuration(
227                 Mockito.same(response),
228                 Mockito.<HttpClientContext>any())).thenReturn(TimeValue.ofMilliseconds(678L));
229 
230         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
231         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
232 
233         Mockito.verify(execRuntime).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
234         Mockito.verify(execRuntime).releaseEndpoint();
235 
236         Assertions.assertNotNull(finalResponse);
237         Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse);
238     }
239 
240     @Test
241     public void testExecRequestConnectionRelease() throws Exception {
242         final HttpRoute route = new HttpRoute(target);
243         final HttpClientContext context = HttpClientContext.create();
244         final ClassicHttpRequest request = new HttpGet("http://bar/test");
245         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
246         // The entity is streaming
247         response.setEntity(EntityBuilder.create()
248                 .setStream(new ByteArrayInputStream(new byte[]{}))
249                 .build());
250 
251         Mockito.when(execRuntime.execute(
252                 Mockito.anyString(),
253                 Mockito.same(request),
254                 Mockito.any(),
255                 Mockito.any())).thenReturn(response);
256         Mockito.when(reuseStrategy.keepAlive(
257                 Mockito.same(request),
258                 Mockito.same(response),
259                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
260 
261         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
262         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
263         Mockito.verify(execRuntime, Mockito.times(1)).execute(Mockito.eq("test"), Mockito.same(request), Mockito.any(), Mockito.any());
264         Mockito.verify(execRuntime, Mockito.never()).disconnectEndpoint();
265         Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
266 
267         Assertions.assertNotNull(finalResponse);
268         Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse);
269         finalResponse.close();
270 
271         Mockito.verify(execRuntime).disconnectEndpoint();
272         Mockito.verify(execRuntime).discardEndpoint();
273     }
274 
275     @Test
276     public void testExecConnectionShutDown() throws Exception {
277         final HttpRoute route = new HttpRoute(target);
278         final HttpClientContext context = HttpClientContext.create();
279         final ClassicHttpRequest request = new HttpGet("http://bar/test");
280 
281         Mockito.when(execRuntime.execute(
282                 Mockito.anyString(),
283                 Mockito.same(request),
284                 Mockito.any(),
285                 Mockito.any())).thenThrow(new ConnectionShutdownException());
286 
287         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
288         Assertions.assertThrows(InterruptedIOException.class, () ->
289                 mainClientExec.execute(request, scope, null));
290         Mockito.verify(execRuntime).discardEndpoint();
291     }
292 
293     @Test
294     public void testExecRuntimeException() throws Exception {
295         final HttpRoute route = new HttpRoute(target);
296         final HttpClientContext context = HttpClientContext.create();
297         final ClassicHttpRequest request = new HttpGet("http://bar/test");
298 
299         Mockito.when(execRuntime.execute(
300                 Mockito.anyString(),
301                 Mockito.same(request),
302                 Mockito.any(),
303                 Mockito.any())).thenThrow(new RuntimeException("Ka-boom"));
304 
305         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
306         Assertions.assertThrows(RuntimeException.class, () ->
307                 mainClientExec.execute(request, scope, null));
308         Mockito.verify(execRuntime).discardEndpoint();
309     }
310 
311     @Test
312     public void testExecHttpException() throws Exception {
313         final HttpRoute route = new HttpRoute(target);
314         final HttpClientContext context = HttpClientContext.create();
315         final ClassicHttpRequest request = new HttpGet("http://bar/test");
316 
317         Mockito.when(execRuntime.execute(
318                 Mockito.anyString(),
319                 Mockito.same(request),
320                 Mockito.any(),
321                 Mockito.any())).thenThrow(new HttpException("Ka-boom"));
322 
323         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
324         Assertions.assertThrows(HttpException.class, () ->
325                 mainClientExec.execute(request, scope, null));
326         Mockito.verify(execRuntime).discardEndpoint();
327     }
328 
329     @Test
330     public void testExecIOException() throws Exception {
331         final HttpRoute route = new HttpRoute(target);
332         final HttpClientContext context = HttpClientContext.create();
333         final ClassicHttpRequest request = new HttpGet("http://bar/test");
334 
335         Mockito.when(execRuntime.execute(
336                 Mockito.anyString(),
337                 Mockito.same(request),
338                 Mockito.any(),
339                 Mockito.any())).thenThrow(new IOException("Ka-boom"));
340 
341         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
342         Assertions.assertThrows(IOException.class, () ->
343                 mainClientExec.execute(request, scope, null));
344         Mockito.verify(execRuntime).discardEndpoint();
345     }
346 
347 }