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.http.impl.execchain;
28  
29  import java.io.ByteArrayInputStream;
30  import java.io.IOException;
31  import java.io.InterruptedIOException;
32  import java.util.concurrent.ExecutionException;
33  import java.util.concurrent.TimeUnit;
34  
35  import org.apache.http.ConnectionReuseStrategy;
36  import org.apache.http.HttpClientConnection;
37  import org.apache.http.HttpException;
38  import org.apache.http.HttpHost;
39  import org.apache.http.HttpRequest;
40  import org.apache.http.HttpResponse;
41  import org.apache.http.HttpVersion;
42  import org.apache.http.client.config.RequestConfig;
43  import org.apache.http.client.entity.EntityBuilder;
44  import org.apache.http.client.methods.CloseableHttpResponse;
45  import org.apache.http.client.methods.HttpExecutionAware;
46  import org.apache.http.client.methods.HttpGet;
47  import org.apache.http.client.methods.HttpRequestWrapper;
48  import org.apache.http.client.protocol.HttpClientContext;
49  import org.apache.http.concurrent.Cancellable;
50  import org.apache.http.conn.ConnectionKeepAliveStrategy;
51  import org.apache.http.conn.ConnectionRequest;
52  import org.apache.http.conn.HttpClientConnectionManager;
53  import org.apache.http.conn.routing.HttpRoute;
54  import org.apache.http.impl.conn.ConnectionShutdownException;
55  import org.apache.http.message.BasicHttpResponse;
56  import org.apache.http.protocol.HttpRequestExecutor;
57  import org.junit.Assert;
58  import org.junit.Before;
59  import org.junit.Test;
60  import org.mockito.ArgumentCaptor;
61  import org.mockito.Mock;
62  import org.mockito.Mockito;
63  import org.mockito.MockitoAnnotations;
64  
65  @SuppressWarnings({"boxing","static-access"}) // test code
66  public class TestMinimalClientExec {
67  
68      @Mock
69      private HttpRequestExecutor requestExecutor;
70      @Mock
71      private HttpClientConnectionManager connManager;
72      @Mock
73      private ConnectionReuseStrategy reuseStrategy;
74      @Mock
75      private ConnectionKeepAliveStrategy keepAliveStrategy;
76      @Mock
77      private HttpExecutionAware execAware;
78      @Mock
79      private ConnectionRequest connRequest;
80      @Mock
81      private HttpClientConnection managedConn;
82  
83      private MinimalClientExec minimalClientExec;
84      private HttpHost target;
85  
86      @Before
87      public void setup() throws Exception {
88          MockitoAnnotations.initMocks(this);
89          minimalClientExec = new MinimalClientExec(
90                  requestExecutor, connManager, reuseStrategy, keepAliveStrategy);
91          target = new HttpHost("foo", 80);
92  
93          Mockito.when(connManager.requestConnection(
94                  Mockito.<HttpRoute>any(), Mockito.any())).thenReturn(connRequest);
95          Mockito.when(connRequest.get(
96                  Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(managedConn);
97      }
98  
99      @Test
100     public void testExecRequestNonPersistentConnection() throws Exception {
101         final HttpRoute route = new HttpRoute(target);
102         final HttpClientContext context = new HttpClientContext();
103         final RequestConfig config = RequestConfig.custom()
104                 .setConnectTimeout(123)
105                 .setSocketTimeout(234)
106                 .setConnectionRequestTimeout(345)
107                 .build();
108         context.setRequestConfig(config);
109         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
110         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
111         Mockito.when(requestExecutor.execute(
112                 Mockito.same(request),
113                 Mockito.<HttpClientConnection>any(),
114                 Mockito.<HttpClientContext>any())).thenReturn(response);
115 
116         final CloseableHttpResponse finalResponse = minimalClientExec.execute(
117                 route, request, context, execAware);
118         Mockito.verify(connManager).requestConnection(route, null);
119         Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS);
120         Mockito.verify(execAware, Mockito.times(1)).setCancellable(connRequest);
121         Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.<Cancellable>any());
122         Mockito.verify(connManager).connect(managedConn, route, 123, context);
123         Mockito.verify(connManager).routeComplete(managedConn, route, context);
124         Mockito.verify(managedConn).setSocketTimeout(234);
125         Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
126         Mockito.verify(managedConn, Mockito.times(1)).close();
127         Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);
128 
129         Assert.assertSame(managedConn, context.getConnection());
130         Assert.assertNotNull(finalResponse);
131         Assert.assertTrue(finalResponse instanceof HttpResponseProxy);
132     }
133 
134     @Test
135     public void testExecRequestPersistentConnection() throws Exception {
136         final HttpRoute route = new HttpRoute(target);
137         final HttpClientContext context = new HttpClientContext();
138         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
139         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
140 
141         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
142         Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
143         Mockito.when(requestExecutor.execute(
144                 Mockito.same(request),
145                 Mockito.<HttpClientConnection>any(),
146                 Mockito.<HttpClientContext>any())).thenReturn(response);
147         Mockito.when(reuseStrategy.keepAlive(
148                 Mockito.same(response),
149                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
150         Mockito.when(keepAliveStrategy.getKeepAliveDuration(
151                 Mockito.same(response),
152                 Mockito.<HttpClientContext>any())).thenReturn(678L);
153 
154         final CloseableHttpResponse finalResponse = minimalClientExec.execute(
155                 route, request, context, execAware);
156         Mockito.verify(connManager).requestConnection(route, null);
157         Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
158         Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
159         Mockito.verify(connManager).releaseConnection(managedConn, null, 678L, TimeUnit.MILLISECONDS);
160         Mockito.verify(managedConn, Mockito.never()).close();
161 
162         Assert.assertNotNull(finalResponse);
163         Assert.assertTrue(finalResponse instanceof HttpResponseProxy);
164     }
165 
166     @Test
167     public void testExecRequestConnectionRelease() throws Exception {
168         final HttpRoute route = new HttpRoute(target);
169         final HttpClientContext context = new HttpClientContext();
170         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
171         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
172         // The entity is streaming
173         response.setEntity(EntityBuilder.create()
174                 .setStream(new ByteArrayInputStream(new byte[]{}))
175                 .build());
176 
177         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
178         Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
179         Mockito.when(requestExecutor.execute(
180                 Mockito.same(request),
181                 Mockito.<HttpClientConnection>any(),
182                 Mockito.<HttpClientContext>any())).thenReturn(response);
183         Mockito.when(reuseStrategy.keepAlive(
184                 Mockito.same(response),
185                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
186 
187         final CloseableHttpResponse finalResponse = minimalClientExec.execute(
188                 route, request, context, execAware);
189         Mockito.verify(connManager).requestConnection(route, null);
190         Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
191         Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
192         Mockito.verify(connManager, Mockito.never()).releaseConnection(
193                 Mockito.same(managedConn),
194                 Mockito.any(),
195                 Mockito.anyInt(),
196                 Mockito.<TimeUnit>any());
197         Mockito.verify(managedConn, Mockito.never()).close();
198 
199         Assert.assertNotNull(finalResponse);
200         Assert.assertTrue(finalResponse instanceof HttpResponseProxy);
201         finalResponse.close();
202 
203         Mockito.verify(connManager, Mockito.times(1)).releaseConnection(
204                 managedConn, null, 0, TimeUnit.MILLISECONDS);
205         Mockito.verify(managedConn, Mockito.times(1)).close();
206     }
207 
208     @Test
209     public void testSocketTimeoutExistingConnection() throws Exception {
210         final HttpRoute route = new HttpRoute(target);
211         final HttpClientContext context = new HttpClientContext();
212         final RequestConfig config = RequestConfig.custom().setSocketTimeout(3000).build();
213         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
214         context.setRequestConfig(config);
215         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
216         Mockito.when(managedConn.isOpen()).thenReturn(true);
217         Mockito.when(requestExecutor.execute(
218                 Mockito.same(request),
219                 Mockito.<HttpClientConnection>any(),
220                 Mockito.<HttpClientContext>any())).thenReturn(response);
221 
222         minimalClientExec.execute(route, request, context, execAware);
223         Mockito.verify(managedConn).setSocketTimeout(3000);
224     }
225 
226     @Test
227     public void testSocketTimeoutReset() throws Exception {
228         final HttpRoute route = new HttpRoute(target);
229         final HttpClientContext context = new HttpClientContext();
230         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
231         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
232         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
233         Mockito.when(requestExecutor.execute(
234                 Mockito.same(request),
235                 Mockito.<HttpClientConnection>any(),
236                 Mockito.<HttpClientContext>any())).thenReturn(response);
237 
238         minimalClientExec.execute(route, request, context, execAware);
239         Mockito.verify(managedConn, Mockito.never()).setSocketTimeout(Mockito.anyInt());
240     }
241 
242     @Test(expected=RequestAbortedException.class)
243     public void testExecAbortedPriorToConnectionLease() throws Exception {
244         final HttpRoute route = new HttpRoute(target);
245         final HttpClientContext context = new HttpClientContext();
246         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
247 
248         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE);
249         Mockito.when(execAware.isAborted()).thenReturn(Boolean.TRUE);
250         try {
251             minimalClientExec.execute(route, request, context, execAware);
252         } catch (final IOException ex) {
253             Mockito.verify(connRequest, Mockito.times(1)).cancel();
254             throw ex;
255         }
256     }
257 
258     @Test(expected=RequestAbortedException.class)
259     public void testExecConnectionRequestFailed() throws Exception {
260         final HttpRoute route = new HttpRoute(target);
261         final HttpClientContext context = new HttpClientContext();
262         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
263 
264         Mockito.when(connRequest.get(Mockito.anyInt(), Mockito.<TimeUnit>any()))
265                 .thenThrow(new ExecutionException("Opppsie", null));
266         minimalClientExec.execute(route, request, context, execAware);
267     }
268 
269     @Test(expected=InterruptedIOException.class)
270     public void testExecConnectionShutDown() throws Exception {
271         final HttpRoute route = new HttpRoute(target);
272         final HttpClientContext context = new HttpClientContext();
273         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
274 
275         Mockito.when(requestExecutor.execute(
276                 Mockito.<HttpRequest>any(),
277                 Mockito.<HttpClientConnection>any(),
278                 Mockito.<HttpClientContext>any())).thenThrow(new ConnectionShutdownException());
279 
280         minimalClientExec.execute(route, request, context, execAware);
281     }
282 
283     @Test(expected=RuntimeException.class)
284     public void testExecRuntimeException() throws Exception {
285         final HttpRoute route = new HttpRoute(target);
286         final HttpClientContext context = new HttpClientContext();
287         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
288 
289         Mockito.when(requestExecutor.execute(
290                 Mockito.<HttpRequest>any(),
291                 Mockito.<HttpClientConnection>any(),
292                 Mockito.<HttpClientContext>any())).thenThrow(new RuntimeException("Ka-boom"));
293 
294         try {
295             minimalClientExec.execute(route, request, context, execAware);
296         } catch (final Exception ex) {
297             Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);
298 
299             throw ex;
300         }
301     }
302 
303     @Test(expected=HttpException.class)
304     public void testExecHttpException() throws Exception {
305         final HttpRoute route = new HttpRoute(target);
306         final HttpClientContext context = new HttpClientContext();
307         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
308 
309         Mockito.when(requestExecutor.execute(
310                 Mockito.<HttpRequest>any(),
311                 Mockito.<HttpClientConnection>any(),
312                 Mockito.<HttpClientContext>any())).thenThrow(new HttpException("Ka-boom"));
313 
314         try {
315             minimalClientExec.execute(route, request, context, execAware);
316         } catch (final Exception ex) {
317             Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);
318 
319             throw ex;
320         }
321     }
322 
323     @Test(expected=IOException.class)
324     public void testExecIOException() throws Exception {
325         final HttpRoute route = new HttpRoute(target);
326         final HttpClientContext context = new HttpClientContext();
327         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
328 
329         Mockito.when(requestExecutor.execute(
330                 Mockito.<HttpRequest>any(),
331                 Mockito.<HttpClientConnection>any(),
332                 Mockito.<HttpClientContext>any())).thenThrow(new IOException("Ka-boom"));
333 
334         try {
335             minimalClientExec.execute(route, request, context, execAware);
336         } catch (final Exception ex) {
337             Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);
338 
339             throw ex;
340         }
341     }
342 
343     @Test
344     public void absoluteUriIsRewrittenToRelativeBeforeBeingPassedInRequestLine() throws Exception {
345         final HttpRoute route = new HttpRoute(target);
346         final HttpClientContext context = new HttpClientContext();
347         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
348 
349         final HttpResponse response = Mockito.mock(HttpResponse.class);
350         Mockito.when(requestExecutor.execute(
351                 Mockito.<HttpRequest>any(),
352                 Mockito.<HttpClientConnection>any(),
353                 Mockito.<HttpClientContext>any())).thenReturn(response);
354 
355         minimalClientExec.execute(route, request, context, execAware);
356 
357         final ArgumentCaptor<HttpRequest> reqCaptor = ArgumentCaptor.forClass(HttpRequest.class);
358         Mockito.verify(requestExecutor).execute(reqCaptor.capture(), Mockito.<HttpClientConnection>any(), Mockito.<HttpClientContext>any());
359 
360         Assert.assertEquals("/test", reqCaptor.getValue().getRequestLine().getUri());
361     }
362 }