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.core5.http.impl.io;
29  
30  import java.io.IOException;
31  import java.util.List;
32  
33  import org.apache.hc.core5.http.ClassicHttpRequest;
34  import org.apache.hc.core5.http.ClassicHttpResponse;
35  import org.apache.hc.core5.http.HeaderElements;
36  import org.apache.hc.core5.http.HttpEntity;
37  import org.apache.hc.core5.http.HttpHeaders;
38  import org.apache.hc.core5.http.HttpResponse;
39  import org.apache.hc.core5.http.Method;
40  import org.apache.hc.core5.http.io.HttpClientConnection;
41  import org.apache.hc.core5.http.io.HttpResponseInformationCallback;
42  import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
43  import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
44  import org.apache.hc.core5.http.protocol.HttpCoreContext;
45  import org.apache.hc.core5.http.protocol.HttpProcessor;
46  import org.apache.hc.core5.util.Timeout;
47  import org.junit.jupiter.api.Assertions;
48  import org.junit.jupiter.api.Test;
49  import org.mockito.ArgumentCaptor;
50  import org.mockito.ArgumentMatchers;
51  import org.mockito.Mockito;
52  
53  public class TestHttpRequestExecutor {
54  
55      @Test
56      public void testInvalidInput() throws Exception {
57          final HttpCoreContext context = HttpCoreContext.create();
58          final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/");
59          final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
60          final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
61          final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
62          Assertions.assertThrows(NullPointerException.class, () -> {
63              final HttpRequestExecutor executor = new HttpRequestExecutor();
64              executor.execute(null, conn, context);
65          });
66          Assertions.assertThrows(NullPointerException.class, () -> {
67              final HttpRequestExecutor executor = new HttpRequestExecutor();
68              executor.execute(request, null, context);
69          });
70          Assertions.assertThrows(NullPointerException.class, () -> {
71              final HttpRequestExecutor executor = new HttpRequestExecutor();
72              executor.execute(request, conn, null);
73          });
74  
75          Assertions.assertThrows(NullPointerException.class, () -> {
76              final HttpRequestExecutor executor = new HttpRequestExecutor();
77              executor.preProcess(null, httprocessor, context);
78          });
79          Assertions.assertThrows(NullPointerException.class, () -> {
80              final HttpRequestExecutor executor = new HttpRequestExecutor();
81              executor.preProcess(request, null, context);
82          });
83          Assertions.assertThrows(NullPointerException.class, () -> {
84              final HttpRequestExecutor executor = new HttpRequestExecutor();
85              executor.preProcess(request, httprocessor, null);
86          });
87          Assertions.assertThrows(NullPointerException.class, () -> {
88              final HttpRequestExecutor executor = new HttpRequestExecutor();
89              executor.postProcess(null, httprocessor, context);
90          });
91          Assertions.assertThrows(NullPointerException.class, () -> {
92              final HttpRequestExecutor executor = new HttpRequestExecutor();
93              executor.postProcess(response, null, context);
94          });
95          Assertions.assertThrows(NullPointerException.class, () -> {
96              final HttpRequestExecutor executor = new HttpRequestExecutor();
97              executor.postProcess(response, httprocessor, null);
98          });
99      }
100 
101     @Test
102     public void testBasicExecution() throws Exception {
103         final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
104         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
105         final HttpRequestExecutor executor = new HttpRequestExecutor();
106 
107         final HttpCoreContext context = HttpCoreContext.create();
108         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/");
109 
110         executor.preProcess(request, httprocessor, context);
111         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
112 
113         Mockito.when(conn.receiveResponseHeader()).thenReturn(
114                 new BasicClassicHttpResponse(200, "OK"));
115 
116         final ClassicHttpResponse response = executor.execute(request, conn, context);
117         Mockito.verify(conn).sendRequestHeader(request);
118         Mockito.verify(conn).flush();
119         Mockito.verify(conn).receiveResponseHeader();
120         Mockito.verify(conn).receiveResponseEntity(response);
121 
122         executor.postProcess(response, httprocessor, context);
123         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
124 
125         Assertions.assertSame(request, context.getRequest());
126         Assertions.assertSame(response, context.getResponse());
127     }
128 
129     @Test
130     public void testExecutionSkipIntermediateResponses() throws Exception {
131         final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
132         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
133         final HttpRequestExecutor executor = new HttpRequestExecutor();
134 
135         final HttpCoreContext context = HttpCoreContext.create();
136         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/");
137 
138         executor.preProcess(request, httprocessor, context);
139         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
140 
141         Mockito.when(conn.receiveResponseHeader()).thenReturn(
142                 new BasicClassicHttpResponse(100, "Continue"),
143                 new BasicClassicHttpResponse(110, "Huh?"),
144                 new BasicClassicHttpResponse(111, "Huh?"),
145                 new BasicClassicHttpResponse(200, "OK"));
146 
147         final HttpResponseInformationCallback callback = Mockito.mock(HttpResponseInformationCallback.class);
148 
149         final ClassicHttpResponse response = executor.execute(request, conn, callback, context);
150         Mockito.verify(conn).sendRequestHeader(request);
151         Mockito.verify(conn).flush();
152         Mockito.verify(conn, Mockito.times(4)).receiveResponseHeader();
153         Mockito.verify(conn, Mockito.times(1)).receiveResponseEntity(response);
154 
155         final ArgumentCaptor<HttpResponse> responseCaptor = ArgumentCaptor.forClass(HttpResponse.class);
156         Mockito.verify(callback, Mockito.times(2)).execute(responseCaptor.capture(), ArgumentMatchers.eq(conn), ArgumentMatchers.eq(context));
157         final List<HttpResponse> infos = responseCaptor.getAllValues();
158         Assertions.assertNotNull(infos);
159         Assertions.assertEquals(2, infos.size());
160         final HttpResponse info1 = infos.get(0);
161         Assertions.assertNotNull(info1);
162         Assertions.assertEquals(110, info1.getCode());
163         final HttpResponse info2 = infos.get(1);
164         Assertions.assertNotNull(info2);
165         Assertions.assertEquals(111, info2.getCode());
166 
167         executor.postProcess(response, httprocessor, context);
168         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
169 
170         Assertions.assertSame(request, context.getRequest());
171         Assertions.assertSame(response, context.getResponse());
172     }
173 
174     @Test
175     public void testExecutionNoResponseBody() throws Exception {
176         final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
177         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
178         final HttpRequestExecutor executor = new HttpRequestExecutor();
179 
180         final HttpCoreContext context = HttpCoreContext.create();
181         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/");
182 
183         executor.preProcess(request, httprocessor, context);
184         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
185 
186         Mockito.when(conn.receiveResponseHeader()).thenReturn(
187                 new BasicClassicHttpResponse(204, "OK"));
188 
189         final ClassicHttpResponse response = executor.execute(request, conn, context);
190         Mockito.verify(conn).sendRequestHeader(request);
191         Mockito.verify(conn).flush();
192         Mockito.verify(conn).receiveResponseHeader();
193         Mockito.verify(conn, Mockito.never()).receiveResponseEntity(response);
194 
195         executor.postProcess(response, httprocessor, context);
196         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
197     }
198 
199     @Test
200     public void testExecutionHead() throws Exception {
201         final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
202         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
203         final HttpRequestExecutor executor = new HttpRequestExecutor();
204 
205         final HttpCoreContext context = HttpCoreContext.create();
206         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.HEAD, "/");
207 
208         executor.preProcess(request, httprocessor, context);
209         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
210 
211         Mockito.when(conn.receiveResponseHeader()).thenReturn(
212                 new BasicClassicHttpResponse(200, "OK"));
213 
214         final ClassicHttpResponse response = executor.execute(request, conn, context);
215         Mockito.verify(conn).sendRequestHeader(request);
216         Mockito.verify(conn).flush();
217         Mockito.verify(conn).receiveResponseHeader();
218         Mockito.verify(conn, Mockito.never()).receiveResponseEntity(response);
219 
220         executor.postProcess(response, httprocessor, context);
221         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
222     }
223 
224     @Test
225     public void testExecutionEntityEnclosingRequest() throws Exception {
226         final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
227         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
228         final HttpRequestExecutor executor = new HttpRequestExecutor();
229 
230         final HttpCoreContext context = HttpCoreContext.create();
231         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/");
232         final HttpEntity entity = Mockito.mock(HttpEntity.class);
233         request.setEntity(entity);
234 
235         executor.preProcess(request, httprocessor, context);
236         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
237 
238         Mockito.when(conn.receiveResponseHeader()).thenReturn(
239                 new BasicClassicHttpResponse(200, "OK"));
240 
241         final ClassicHttpResponse response = executor.execute(request, conn, context);
242         Mockito.verify(conn).sendRequestHeader(request);
243         Mockito.verify(conn).sendRequestEntity(request);
244         Mockito.verify(conn).flush();
245         Mockito.verify(conn).receiveResponseHeader();
246         Mockito.verify(conn).receiveResponseEntity(response);
247 
248         executor.postProcess(response, httprocessor, context);
249         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
250     }
251 
252     @Test
253     public void testExecutionEntityEnclosingRequestWithExpectContinueSuccess() throws Exception {
254         final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
255         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
256         final HttpRequestExecutor executor = new HttpRequestExecutor();
257 
258         final HttpCoreContext context = HttpCoreContext.create();
259         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/");
260         request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
261         final HttpEntity entity = Mockito.mock(HttpEntity.class);
262         request.setEntity(entity);
263 
264         executor.preProcess(request, httprocessor, context);
265         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
266 
267         Mockito.when(conn.receiveResponseHeader()).thenReturn(
268                 new BasicClassicHttpResponse(100, "Continue"),
269                 new BasicClassicHttpResponse(200, "OK"));
270         Mockito.when(conn.isDataAvailable(ArgumentMatchers.any(Timeout.class))).thenReturn(Boolean.TRUE);
271 
272         final ClassicHttpResponse response = executor.execute(request, conn, context);
273         Mockito.verify(conn).sendRequestHeader(request);
274         Mockito.verify(conn).sendRequestEntity(request);
275         Mockito.verify(conn, Mockito.times(2)).flush();
276         Mockito.verify(conn).isDataAvailable(Timeout.ofMilliseconds(3000));
277         Mockito.verify(conn, Mockito.times(2)).receiveResponseHeader();
278         Mockito.verify(conn).receiveResponseEntity(response);
279 
280         executor.postProcess(response, httprocessor, context);
281         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
282     }
283 
284     @Test
285     public void testExecutionEntityEnclosingRequestWithExpectContinueFailure() throws Exception {
286         final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
287         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
288         final HttpRequestExecutor executor = new HttpRequestExecutor();
289 
290         final HttpCoreContext context = HttpCoreContext.create();
291         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/");
292         request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
293         final HttpEntity entity = Mockito.mock(HttpEntity.class);
294         request.setEntity(entity);
295 
296         executor.preProcess(request, httprocessor, context);
297         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
298 
299         Mockito.when(conn.receiveResponseHeader()).thenReturn(
300                 new BasicClassicHttpResponse(402, "OK"));
301         Mockito.when(conn.isDataAvailable(ArgumentMatchers.any(Timeout.class))).thenReturn(Boolean.TRUE);
302 
303         final ClassicHttpResponse response = executor.execute(request, conn, context);
304         Mockito.verify(conn).sendRequestHeader(request);
305         Mockito.verify(conn, Mockito.never()).sendRequestEntity(request);
306         Mockito.verify(conn).terminateRequest(request);
307         Mockito.verify(conn, Mockito.times(2)).flush();
308         Mockito.verify(conn).isDataAvailable(Timeout.ofMilliseconds(3000));
309         Mockito.verify(conn).receiveResponseHeader();
310         Mockito.verify(conn).receiveResponseEntity(response);
311 
312         executor.postProcess(response, httprocessor, context);
313         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
314     }
315 
316     @Test
317     public void testExecutionEntityEnclosingRequestWithExpectContinueMultiple1xxResponses() throws Exception {
318         final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
319         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
320         final HttpRequestExecutor executor = new HttpRequestExecutor();
321 
322         final HttpCoreContext context = HttpCoreContext.create();
323         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/");
324         request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
325         final HttpEntity entity = Mockito.mock(HttpEntity.class);
326         request.setEntity(entity);
327 
328         executor.preProcess(request, httprocessor, context);
329         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
330 
331         Mockito.when(conn.receiveResponseHeader()).thenReturn(
332                 new BasicClassicHttpResponse(110, "Huh?"),
333                 new BasicClassicHttpResponse(100, "Continue"),
334                 new BasicClassicHttpResponse(111, "Huh?"),
335                 new BasicClassicHttpResponse(200, "OK"));
336         Mockito.when(conn.isDataAvailable(ArgumentMatchers.any(Timeout.class))).thenReturn(Boolean.TRUE);
337 
338         final HttpResponseInformationCallback callback = Mockito.mock(HttpResponseInformationCallback.class);
339 
340         final ClassicHttpResponse response = executor.execute(request, conn, callback, context);
341         Mockito.verify(conn).sendRequestHeader(request);
342         Mockito.verify(conn).sendRequestEntity(request);
343         Mockito.verify(conn, Mockito.times(2)).flush();
344         Mockito.verify(conn, Mockito.times(2)).isDataAvailable(Timeout.ofMilliseconds(3000));
345         Mockito.verify(conn, Mockito.times(4)).receiveResponseHeader();
346         Mockito.verify(conn).receiveResponseEntity(response);
347 
348         final ArgumentCaptor<HttpResponse> responseCaptor = ArgumentCaptor.forClass(HttpResponse.class);
349         Mockito.verify(callback, Mockito.times(2)).execute(responseCaptor.capture(), ArgumentMatchers.eq(conn), ArgumentMatchers.eq(context));
350         final List<HttpResponse> infos = responseCaptor.getAllValues();
351         Assertions.assertNotNull(infos);
352         Assertions.assertEquals(2, infos.size());
353         final HttpResponse info1 = infos.get(0);
354         Assertions.assertNotNull(info1);
355         Assertions.assertEquals(110, info1.getCode());
356         final HttpResponse info2 = infos.get(1);
357         Assertions.assertNotNull(info2);
358         Assertions.assertEquals(111, info2.getCode());
359 
360         executor.postProcess(response, httprocessor, context);
361         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
362     }
363 
364     @Test
365     public void testExecutionEntityEnclosingRequestWithExpectContinueNoResponse() throws Exception {
366         final HttpProcessor httprocessor = Mockito.mock(HttpProcessor.class);
367         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
368         final HttpRequestExecutor executor = new HttpRequestExecutor();
369 
370         final HttpCoreContext context = HttpCoreContext.create();
371         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/");
372         request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
373         final HttpEntity entity = Mockito.mock(HttpEntity.class);
374         request.setEntity(entity);
375 
376         executor.preProcess(request, httprocessor, context);
377         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
378 
379         Mockito.when(conn.receiveResponseHeader()).thenReturn(
380                 new BasicClassicHttpResponse(200, "OK"));
381         Mockito.when(conn.isDataAvailable(ArgumentMatchers.any(Timeout.class))).thenReturn(Boolean.FALSE);
382 
383         final ClassicHttpResponse response = executor.execute(request, conn, context);
384         Mockito.verify(conn).sendRequestHeader(request);
385         Mockito.verify(conn).sendRequestEntity(request);
386         Mockito.verify(conn, Mockito.times(2)).flush();
387         Mockito.verify(conn).isDataAvailable(Timeout.ofMilliseconds(3000));
388         Mockito.verify(conn).receiveResponseHeader();
389         Mockito.verify(conn).receiveResponseEntity(response);
390 
391         executor.postProcess(response, httprocessor, context);
392         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
393     }
394 
395     @Test
396     public void testExecutionIOException() throws Exception {
397         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
398         final HttpRequestExecutor executor = new HttpRequestExecutor();
399 
400         final HttpCoreContext context = HttpCoreContext.create();
401         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/");
402 
403         Mockito.doThrow(new IOException("Oopsie")).when(conn).sendRequestHeader(request);
404         Assertions.assertThrows(IOException.class, () -> executor.execute(request, conn, context));
405         Mockito.verify(conn).close();
406     }
407 
408     @Test
409     public void testExecutionRuntimeException() throws Exception {
410         final HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
411         final HttpRequestExecutor executor = new HttpRequestExecutor();
412 
413         final HttpCoreContext context = HttpCoreContext.create();
414         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/");
415 
416         Mockito.doThrow(new RuntimeException("Oopsie")).when(conn).receiveResponseHeader();
417         Assertions.assertThrows(RuntimeException.class, () -> executor.execute(request, conn, context));
418         Mockito.verify(conn).close();
419     }
420 
421 }