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.InputStream;
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.ConnectionReuseStrategy;
36  import org.apache.hc.core5.http.HeaderElements;
37  import org.apache.hc.core5.http.HttpHeaders;
38  import org.apache.hc.core5.http.HttpRequestMapper;
39  import org.apache.hc.core5.http.HttpResponseFactory;
40  import org.apache.hc.core5.http.HttpStatus;
41  import org.apache.hc.core5.http.Method;
42  import org.apache.hc.core5.http.MethodNotSupportedException;
43  import org.apache.hc.core5.http.ProtocolException;
44  import org.apache.hc.core5.http.UnsupportedHttpVersionException;
45  import org.apache.hc.core5.http.io.HttpRequestHandler;
46  import org.apache.hc.core5.http.io.HttpServerConnection;
47  import org.apache.hc.core5.http.io.entity.InputStreamEntity;
48  import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
49  import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
50  import org.apache.hc.core5.http.protocol.HttpCoreContext;
51  import org.apache.hc.core5.http.protocol.HttpProcessor;
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.ArgumentCaptor;
56  import org.mockito.ArgumentMatchers;
57  import org.mockito.Mock;
58  import org.mockito.Mockito;
59  import org.mockito.MockitoAnnotations;
60  import org.mockito.Spy;
61  
62  public class TestHttpService {
63  
64      @Mock
65      private HttpProcessor httprocessor;
66      @Mock
67      private ConnectionReuseStrategy connReuseStrategy;
68      @Spy
69      private final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK);
70      @Mock
71      private HttpResponseFactory<ClassicHttpResponse> responseFactory;
72      @Mock
73      private HttpRequestMapper<HttpRequestHandler> handlerResolver;
74      @Mock
75      private HttpRequestHandler requestHandler;
76      @Mock
77      private HttpServerConnection conn;
78  
79      private HttpService httpservice;
80  
81      @BeforeEach
82      public void prepareMocks() {
83          MockitoAnnotations.openMocks(this);
84          httpservice = new HttpService(
85                  httprocessor,
86                  handlerResolver,
87                  connReuseStrategy,
88                  responseFactory);
89      }
90  
91      @Test
92      public void testInvalidInitialization() throws Exception {
93          Assertions.assertThrows(NullPointerException.class, () ->
94                  new HttpService(
95                          null,
96                          handlerResolver,
97                          connReuseStrategy,
98                          responseFactory));
99      }
100 
101     @Test
102     public void testBasicExecution() throws Exception {
103         final HttpCoreContext context = HttpCoreContext.create();
104         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/");
105         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
106         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
107         Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.FALSE);
108 
109         httpservice.handleRequest(conn, context);
110 
111         Assertions.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getCode());
112 
113         Assertions.assertSame(request, context.getRequest());
114         Assertions.assertSame(response, context.getResponse());
115 
116         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
117         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
118         Mockito.verify(conn).sendResponseHeader(response);
119         Mockito.verify(conn).sendResponseEntity(response);
120         Mockito.verify(conn).flush();
121         Mockito.verify(conn).close();
122         Mockito.verify(response).close();
123     }
124 
125     @Test
126     public void testExecutionEntityEnclosingRequest() throws Exception {
127         final HttpCoreContext context = HttpCoreContext.create();
128         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/");
129         final InputStream inStream = Mockito.mock(InputStream.class);
130         final InputStreamEntity entity = new InputStreamEntity(inStream, -1, null);
131         request.setEntity(entity);
132 
133         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
134         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
135         Mockito.when(connReuseStrategy.keepAlive(ArgumentMatchers.eq(request), ArgumentMatchers.argThat(errorResponse -> errorResponse.getCode() == HttpStatus.SC_NOT_IMPLEMENTED), ArgumentMatchers.eq(context))).thenReturn(Boolean.TRUE);
136 
137         httpservice.handleRequest(conn, context);
138         final ArgumentCaptor<ClassicHttpResponse> responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class);
139         Mockito.verify(conn).sendResponseHeader(responseCaptor.capture());
140         final ClassicHttpResponse response = responseCaptor.getValue();
141         Assertions.assertNotNull(response);
142 
143         Assertions.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getCode());
144 
145         Assertions.assertSame(request, context.getRequest());
146 
147         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
148         Mockito.verify(inStream).close();
149         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
150         Mockito.verify(conn).sendResponseHeader(response);
151         Mockito.verify(conn).sendResponseEntity(response);
152         Mockito.verify(conn).flush();
153         Mockito.verify(conn, Mockito.never()).close();
154         Mockito.verify(response).close();
155     }
156 
157     @Test
158     public void testExecutionEntityEnclosingRequestWithExpectContinue() throws Exception {
159         final HttpCoreContext context = HttpCoreContext.create();
160         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.POST, "/");
161         request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
162         final InputStream inStream = Mockito.mock(InputStream.class);
163         final InputStreamEntity entity = new InputStreamEntity(inStream, -1, null);
164         request.setEntity(entity);
165 
166         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
167         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
168         Mockito.when(connReuseStrategy.keepAlive(ArgumentMatchers.eq(request), ArgumentMatchers.argThat(errorResponse -> errorResponse.getCode() == HttpStatus.SC_NOT_IMPLEMENTED), ArgumentMatchers.eq(context))).thenReturn(Boolean.TRUE);
169 
170         httpservice.handleRequest(conn, context);
171         final ArgumentCaptor<ClassicHttpResponse> responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class);
172         Mockito.verify(conn, Mockito.times(2)).sendResponseHeader(responseCaptor.capture());
173         final List<ClassicHttpResponse> responses = responseCaptor.getAllValues();
174         Assertions.assertNotNull(responses);
175         Assertions.assertEquals(2, responses.size());
176         final ClassicHttpResponse ack = responses.get(0);
177         final ClassicHttpResponse response = responses.get(1);
178 
179         Assertions.assertEquals(HttpStatus.SC_CONTINUE, ack.getCode());
180         Assertions.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, response.getCode());
181 
182         Assertions.assertSame(request, context.getRequest());
183 
184         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
185         Mockito.verify(inStream).close();
186         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
187         Mockito.verify(conn).sendResponseHeader(response);
188         Mockito.verify(conn).sendResponseEntity(response);
189         Mockito.verify(conn, Mockito.times(2)).flush();
190         Mockito.verify(conn, Mockito.never()).close();
191         Mockito.verify(response).close();
192     }
193 
194     @Test
195     public void testMethodNotSupported() throws Exception {
196         final HttpCoreContext context = HttpCoreContext.create();
197         final ClassicHttpRequest request = new BasicClassicHttpRequest("whatever", "/");
198 
199         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
200         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
201         Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
202         Mockito.doThrow(new MethodNotSupportedException("whatever")).when(
203                 requestHandler).handle(request, response, context);
204 
205         httpservice.handleRequest(conn, context);
206         final ArgumentCaptor<ClassicHttpResponse> responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class);
207         Mockito.verify(conn).sendResponseHeader(responseCaptor.capture());
208         final ClassicHttpResponse error = responseCaptor.getValue();
209         Assertions.assertNotNull(error);
210 
211         Assertions.assertSame(request, context.getRequest());
212 
213         Assertions.assertEquals(HttpStatus.SC_NOT_IMPLEMENTED, error.getCode());
214 
215         Mockito.verify(httprocessor).process(error, error.getEntity(), context);
216         Mockito.verify(conn).sendResponseHeader(error);
217         Mockito.verify(conn).sendResponseEntity(error);
218         Mockito.verify(conn).close();
219     }
220 
221     @Test
222     public void testUnsupportedHttpVersionException() throws Exception {
223         final HttpCoreContext context = HttpCoreContext.create();
224         final ClassicHttpRequest request = new BasicClassicHttpRequest("whatever", "/");
225 
226         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
227         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
228         Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
229         Mockito.doThrow(new UnsupportedHttpVersionException()).when(
230                 requestHandler).handle(request, response, context);
231 
232         httpservice.handleRequest(conn, context);
233         final ArgumentCaptor<ClassicHttpResponse> responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class);
234         Mockito.verify(conn).sendResponseHeader(responseCaptor.capture());
235         final ClassicHttpResponse error = responseCaptor.getValue();
236         Assertions.assertNotNull(error);
237 
238         Assertions.assertSame(request, context.getRequest());
239 
240         Assertions.assertEquals(HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED, error.getCode());
241 
242         Mockito.verify(httprocessor).process(error, error.getEntity(), context);
243         Mockito.verify(conn).sendResponseHeader(error);
244         Mockito.verify(conn).sendResponseEntity(error);
245         Mockito.verify(conn).close();
246     }
247 
248     @Test
249     public void testProtocolException() throws Exception {
250         final HttpCoreContext context = HttpCoreContext.create();
251         final ClassicHttpRequest request = new BasicClassicHttpRequest("whatever", "/");
252 
253         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
254         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
255         Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
256         Mockito.doThrow(new ProtocolException("oh, this world is wrong")).when(
257                 requestHandler).handle(request, response, context);
258 
259         httpservice.handleRequest(conn, context);
260         final ArgumentCaptor<ClassicHttpResponse> responseCaptor = ArgumentCaptor.forClass(ClassicHttpResponse.class);
261         Mockito.verify(conn).sendResponseHeader(responseCaptor.capture());
262         final ClassicHttpResponse error = responseCaptor.getValue();
263         Assertions.assertNotNull(error);
264 
265         Assertions.assertSame(request, context.getRequest());
266 
267         Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, error.getCode());
268 
269         Mockito.verify(httprocessor).process(error, error.getEntity(), context);
270         Mockito.verify(conn).sendResponseHeader(error);
271         Mockito.verify(conn).sendResponseEntity(error);
272         Mockito.verify(conn).close();
273     }
274 
275     @Test
276     public void testConnectionKeepAlive() throws Exception {
277         final HttpCoreContext context = HttpCoreContext.create();
278         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/");
279         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
280         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
281         Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
282         Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.TRUE);
283 
284         httpservice.handleRequest(conn, context);
285 
286         Assertions.assertEquals(HttpStatus.SC_OK, response.getCode());
287 
288         Assertions.assertSame(request, context.getRequest());
289         Assertions.assertSame(response, context.getResponse());
290 
291         Mockito.verify(httprocessor).process(request, request.getEntity(), context);
292         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
293         Mockito.verify(conn).sendResponseHeader(response);
294         Mockito.verify(conn).sendResponseEntity(response);
295         Mockito.verify(conn).flush();
296         Mockito.verify(conn, Mockito.never()).close();
297         Mockito.verify(response).close();
298     }
299 
300     @Test
301     public void testNoContentResponse() throws Exception {
302         final HttpCoreContext context = HttpCoreContext.create();
303         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, "/");
304 
305         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
306         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
307         Mockito.when(handlerResolver.resolve(request, context)).thenReturn((request1, response, context1) -> response.setCode(HttpStatus.SC_NO_CONTENT));
308         Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.TRUE);
309 
310         httpservice.handleRequest(conn, context);
311 
312         Assertions.assertSame(request, context.getRequest());
313 
314         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
315 
316         Mockito.verify(conn).sendResponseHeader(response);
317         Mockito.verify(conn, Mockito.never()).sendResponseEntity(ArgumentMatchers.any());
318         Mockito.verify(conn).flush();
319         Mockito.verify(conn, Mockito.never()).close();
320         Mockito.verify(response).close();
321     }
322 
323     @Test
324     public void testResponseToHead() throws Exception {
325         final HttpCoreContext context = HttpCoreContext.create();
326         final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.HEAD, "/");
327 
328         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
329         Mockito.when(responseFactory.newHttpResponse(200)).thenReturn(response);
330         Mockito.when(handlerResolver.resolve(request, context)).thenReturn(requestHandler);
331         Mockito.when(connReuseStrategy.keepAlive(request, response, context)).thenReturn(Boolean.TRUE);
332 
333         httpservice.handleRequest(conn, context);
334 
335         Assertions.assertSame(request, context.getRequest());
336 
337         Mockito.verify(httprocessor).process(response, response.getEntity(), context);
338         Mockito.verify(requestHandler).handle(request, response, context);
339 
340         Mockito.verify(conn).sendResponseHeader(response);
341         Mockito.verify(conn, Mockito.never()).sendResponseEntity(ArgumentMatchers.any());
342         Mockito.verify(conn).flush();
343         Mockito.verify(conn, Mockito.never()).close();
344         Mockito.verify(response).close();
345     }
346 
347 }