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  @SuppressWarnings({"boxing","static-access"}) // test code
60  public class TestMainClientExec {
61  
62      @Mock
63      private HttpClientConnectionManager connectionManager;
64      @Mock
65      private HttpProcessor httpProcessor;
66      @Mock
67      private ConnectionReuseStrategy reuseStrategy;
68      @Mock
69      private ConnectionKeepAliveStrategy keepAliveStrategy;
70      @Mock
71      private UserTokenHandler userTokenHandler;
72      @Mock
73      private ExecRuntime execRuntime;
74  
75      private MainClientExec mainClientExec;
76      private HttpHost target;
77  
78      @BeforeEach
79      public void setup() throws Exception {
80          MockitoAnnotations.openMocks(this);
81          mainClientExec = new MainClientExec(connectionManager, httpProcessor, reuseStrategy, keepAliveStrategy, userTokenHandler);
82          target = new HttpHost("foo", 80);
83      }
84  
85      @Test
86      public void testFundamentals() throws Exception {
87          final HttpRoute route = new HttpRoute(target);
88          final ClassicHttpRequest request = new HttpGet("/test");
89          final HttpClientContext context = HttpClientContext.create();
90  
91          final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
92          final HttpEntity responseEntity = EntityBuilder.create()
93                  .setStream(new ByteArrayInputStream(new byte[]{}))
94                  .build();
95          response.setEntity(responseEntity);
96  
97          Mockito.when(execRuntime.execute(
98                  Mockito.anyString(),
99                  Mockito.same(request),
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("test", request, context);
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 = new HttpClientContext();
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())).thenReturn(response);
128         Mockito.when(reuseStrategy.keepAlive(
129                 Mockito.same(request),
130                 Mockito.same(response),
131                 Mockito.<HttpClientContext>any())).thenReturn(false);
132 
133         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
134         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
135         Mockito.verify(execRuntime).execute("test", request, context);
136         Mockito.verify(execRuntime, Mockito.times(1)).markConnectionNonReusable();
137         Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
138 
139         Assertions.assertNull(context.getUserToken());
140         Assertions.assertNotNull(finalResponse);
141         Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse);
142     }
143 
144     @Test
145     public void testExecRequestNonPersistentConnectionNoResponseEntity() throws Exception {
146         final HttpRoute route = new HttpRoute(target);
147         final HttpClientContext context = new HttpClientContext();
148         final ClassicHttpRequest request = new HttpGet("http://bar/test");
149         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
150         response.setEntity(null);
151 
152         Mockito.when(execRuntime.execute(
153                 Mockito.anyString(),
154                 Mockito.same(request),
155                 Mockito.any())).thenReturn(response);
156         Mockito.when(reuseStrategy.keepAlive(
157                 Mockito.same(request),
158                 Mockito.same(response),
159                 Mockito.<HttpClientContext>any())).thenReturn(false);
160 
161         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
162         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
163 
164         Mockito.verify(execRuntime).execute("test", request, context);
165         Mockito.verify(execRuntime).markConnectionNonReusable();
166         Mockito.verify(execRuntime).releaseEndpoint();
167 
168         Assertions.assertNotNull(finalResponse);
169         Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse);
170     }
171 
172     @Test
173     public void testExecRequestPersistentConnection() throws Exception {
174         final HttpRoute route = new HttpRoute(target);
175         final HttpClientContext context = new HttpClientContext();
176         final ClassicHttpRequest request = new HttpGet("http://bar/test");
177         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
178         // The entity is streaming
179         response.setEntity(EntityBuilder.create()
180                 .setStream(new ByteArrayInputStream(new byte[]{}))
181                 .build());
182 
183 
184         Mockito.when(execRuntime.execute(
185                 Mockito.anyString(),
186                 Mockito.same(request),
187                 Mockito.any())).thenReturn(response);
188         Mockito.when(reuseStrategy.keepAlive(
189                 Mockito.same(request),
190                 Mockito.same(response),
191                 Mockito.<HttpClientContext>any())).thenReturn(true);
192         Mockito.when(keepAliveStrategy.getKeepAliveDuration(
193                 Mockito.same(response),
194                 Mockito.<HttpClientContext>any())).thenReturn(TimeValue.ofMilliseconds(678L));
195 
196         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
197         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
198 
199         Mockito.verify(execRuntime).execute("test", request, context);
200         Mockito.verify(execRuntime).markConnectionReusable(null, TimeValue.ofMilliseconds(678L));
201         Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
202 
203         Assertions.assertNotNull(finalResponse);
204         Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse);
205     }
206 
207     @Test
208     public void testExecRequestPersistentConnectionNoResponseEntity() throws Exception {
209         final HttpRoute route = new HttpRoute(target);
210         final HttpClientContext context = new HttpClientContext();
211         final ClassicHttpRequest request = new HttpGet("http://bar/test");
212         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
213 
214         Mockito.when(execRuntime.execute(
215                 Mockito.anyString(),
216                 Mockito.same(request),
217                 Mockito.any())).thenReturn(response);
218         Mockito.when(reuseStrategy.keepAlive(
219                 Mockito.same(request),
220                 Mockito.same(response),
221                 Mockito.<HttpClientContext>any())).thenReturn(true);
222         Mockito.when(keepAliveStrategy.getKeepAliveDuration(
223                 Mockito.same(response),
224                 Mockito.<HttpClientContext>any())).thenReturn(TimeValue.ofMilliseconds(678L));
225 
226         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
227         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
228 
229         Mockito.verify(execRuntime).execute("test", request, context);
230         Mockito.verify(execRuntime).releaseEndpoint();
231 
232         Assertions.assertNotNull(finalResponse);
233         Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse);
234     }
235 
236     @Test
237     public void testExecRequestConnectionRelease() throws Exception {
238         final HttpRoute route = new HttpRoute(target);
239         final HttpClientContext context = new HttpClientContext();
240         final ClassicHttpRequest request = new HttpGet("http://bar/test");
241         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
242         // The entity is streaming
243         response.setEntity(EntityBuilder.create()
244                 .setStream(new ByteArrayInputStream(new byte[]{}))
245                 .build());
246 
247         Mockito.when(execRuntime.execute(
248                 Mockito.anyString(),
249                 Mockito.same(request),
250                 Mockito.any())).thenReturn(response);
251         Mockito.when(reuseStrategy.keepAlive(
252                 Mockito.same(request),
253                 Mockito.same(response),
254                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
255 
256         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
257         final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
258         Mockito.verify(execRuntime, Mockito.times(1)).execute("test", request, context);
259         Mockito.verify(execRuntime, Mockito.never()).disconnectEndpoint();
260         Mockito.verify(execRuntime, Mockito.never()).releaseEndpoint();
261 
262         Assertions.assertNotNull(finalResponse);
263         Assertions.assertTrue(finalResponse instanceof CloseableHttpResponse);
264         finalResponse.close();
265 
266         Mockito.verify(execRuntime).disconnectEndpoint();
267         Mockito.verify(execRuntime).discardEndpoint();
268     }
269 
270     @Test
271     public void testExecConnectionShutDown() throws Exception {
272         final HttpRoute route = new HttpRoute(target);
273         final HttpClientContext context = new HttpClientContext();
274         final ClassicHttpRequest request = new HttpGet("http://bar/test");
275 
276         Mockito.when(execRuntime.execute(
277                 Mockito.anyString(),
278                 Mockito.same(request),
279                 Mockito.any())).thenThrow(new ConnectionShutdownException());
280 
281         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
282         Assertions.assertThrows(InterruptedIOException.class, () ->
283                 mainClientExec.execute(request, scope, null));
284         Mockito.verify(execRuntime).discardEndpoint();
285     }
286 
287     @Test
288     public void testExecRuntimeException() throws Exception {
289         final HttpRoute route = new HttpRoute(target);
290         final HttpClientContext context = new HttpClientContext();
291         final ClassicHttpRequest request = new HttpGet("http://bar/test");
292 
293         Mockito.when(execRuntime.execute(
294                 Mockito.anyString(),
295                 Mockito.same(request),
296                 Mockito.any())).thenThrow(new RuntimeException("Ka-boom"));
297 
298         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
299         Assertions.assertThrows(RuntimeException.class, () ->
300                 mainClientExec.execute(request, scope, null));
301         Mockito.verify(execRuntime).discardEndpoint();
302     }
303 
304     @Test
305     public void testExecHttpException() throws Exception {
306         final HttpRoute route = new HttpRoute(target);
307         final HttpClientContext context = new HttpClientContext();
308         final ClassicHttpRequest request = new HttpGet("http://bar/test");
309 
310         Mockito.when(execRuntime.execute(
311                 Mockito.anyString(),
312                 Mockito.same(request),
313                 Mockito.any())).thenThrow(new HttpException("Ka-boom"));
314 
315         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
316         Assertions.assertThrows(HttpException.class, () ->
317                 mainClientExec.execute(request, scope, null));
318         Mockito.verify(execRuntime).discardEndpoint();
319     }
320 
321     @Test
322     public void testExecIOException() throws Exception {
323         final HttpRoute route = new HttpRoute(target);
324         final HttpClientContext context = new HttpClientContext();
325         final ClassicHttpRequest request = new HttpGet("http://bar/test");
326 
327         Mockito.when(execRuntime.execute(
328                 Mockito.anyString(),
329                 Mockito.same(request),
330                 Mockito.any())).thenThrow(new IOException("Ka-boom"));
331 
332         final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, execRuntime, context);
333         Assertions.assertThrows(IOException.class, () ->
334                 mainClientExec.execute(request, scope, null));
335         Mockito.verify(execRuntime).discardEndpoint();
336     }
337 
338 }