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