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