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.ByteArrayOutputStream;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.io.InterruptedIOException;
34  import java.util.Arrays;
35  import java.util.HashMap;
36  import java.util.LinkedList;
37  import java.util.Map;
38  import java.util.concurrent.ExecutionException;
39  import java.util.concurrent.TimeUnit;
40  
41  import org.apache.http.ConnectionReuseStrategy;
42  import org.apache.http.Header;
43  import org.apache.http.HttpClientConnection;
44  import org.apache.http.HttpEntityEnclosingRequest;
45  import org.apache.http.HttpException;
46  import org.apache.http.HttpHost;
47  import org.apache.http.HttpRequest;
48  import org.apache.http.HttpResponse;
49  import org.apache.http.HttpVersion;
50  import org.apache.http.auth.AUTH;
51  import org.apache.http.auth.AuthOption;
52  import org.apache.http.auth.AuthProtocolState;
53  import org.apache.http.auth.AuthState;
54  import org.apache.http.auth.NTCredentials;
55  import org.apache.http.auth.UsernamePasswordCredentials;
56  import org.apache.http.client.AuthenticationStrategy;
57  import org.apache.http.client.NonRepeatableRequestException;
58  import org.apache.http.client.UserTokenHandler;
59  import org.apache.http.client.config.RequestConfig;
60  import org.apache.http.client.entity.EntityBuilder;
61  import org.apache.http.client.methods.CloseableHttpResponse;
62  import org.apache.http.client.methods.HttpExecutionAware;
63  import org.apache.http.client.methods.HttpGet;
64  import org.apache.http.client.methods.HttpPost;
65  import org.apache.http.client.methods.HttpRequestWrapper;
66  import org.apache.http.client.protocol.HttpClientContext;
67  import org.apache.http.concurrent.Cancellable;
68  import org.apache.http.conn.ConnectionKeepAliveStrategy;
69  import org.apache.http.conn.ConnectionRequest;
70  import org.apache.http.conn.HttpClientConnectionManager;
71  import org.apache.http.conn.routing.HttpRoute;
72  import org.apache.http.conn.routing.RouteInfo;
73  import org.apache.http.entity.StringEntity;
74  import org.apache.http.impl.auth.BasicScheme;
75  import org.apache.http.impl.auth.NTLMScheme;
76  import org.apache.http.impl.conn.ConnectionShutdownException;
77  import org.apache.http.message.BasicHeader;
78  import org.apache.http.message.BasicHttpResponse;
79  import org.apache.http.protocol.HttpContext;
80  import org.apache.http.protocol.HttpRequestExecutor;
81  import org.apache.http.util.EntityUtils;
82  import org.junit.Assert;
83  import org.junit.Before;
84  import org.junit.Test;
85  import org.mockito.ArgumentCaptor;
86  import org.mockito.Mock;
87  import org.mockito.Mockito;
88  import org.mockito.MockitoAnnotations;
89  import org.mockito.invocation.InvocationOnMock;
90  import org.mockito.stubbing.Answer;
91  
92  @SuppressWarnings({"boxing","static-access"}) // test code
93  public class TestMainClientExec {
94  
95      @Mock
96      private HttpRequestExecutor requestExecutor;
97      @Mock
98      private HttpClientConnectionManager connManager;
99      @Mock
100     private ConnectionReuseStrategy reuseStrategy;
101     @Mock
102     private ConnectionKeepAliveStrategy keepAliveStrategy;
103     @Mock
104     private AuthenticationStrategy targetAuthStrategy;
105     @Mock
106     private AuthenticationStrategy proxyAuthStrategy;
107     @Mock
108     private UserTokenHandler userTokenHandler;
109     @Mock
110     private HttpExecutionAware execAware;
111     @Mock
112     private ConnectionRequest connRequest;
113     @Mock
114     private HttpClientConnection managedConn;
115 
116     private MainClientExec mainClientExec;
117     private HttpHost target;
118     private HttpHost proxy;
119 
120     @Before
121     public void setup() throws Exception {
122         MockitoAnnotations.initMocks(this);
123         mainClientExec = new MainClientExec(requestExecutor, connManager, reuseStrategy,
124             keepAliveStrategy, targetAuthStrategy, proxyAuthStrategy, userTokenHandler);
125         target = new HttpHost("foo", 80);
126         proxy = new HttpHost("bar", 8888);
127 
128         Mockito.when(connManager.requestConnection(
129                 Mockito.<HttpRoute>any(), Mockito.any())).thenReturn(connRequest);
130         Mockito.when(connRequest.get(
131                 Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(managedConn);
132         final Map<String, Header> challenges = new HashMap<String, Header>();
133         challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test"));
134         final AuthOption authOption = new AuthOption(
135                 new BasicScheme(), new UsernamePasswordCredentials("user:pass"));
136         Mockito.when(targetAuthStrategy.getChallenges(
137                 Mockito.eq(target),
138                 Mockito.<HttpResponse>any(),
139                 Mockito.<HttpClientContext>any())).thenReturn(challenges);
140         Mockito.when(targetAuthStrategy.getChallenges(
141                 Mockito.eq(target),
142                 Mockito.<HttpResponse>any(),
143                 Mockito.<HttpClientContext>any())).thenReturn(challenges);
144         Mockito.when(targetAuthStrategy.select(
145                 Mockito.same(challenges),
146                 Mockito.eq(target),
147                 Mockito.<HttpResponse>any(),
148                 Mockito.<HttpClientContext>any())).thenReturn(
149                 new LinkedList<AuthOption>(Arrays.asList(authOption)));
150         Mockito.when(proxyAuthStrategy.getChallenges(
151                 Mockito.eq(proxy),
152                 Mockito.<HttpResponse>any(),
153                 Mockito.<HttpClientContext>any())).thenReturn(challenges);
154         Mockito.when(proxyAuthStrategy.getChallenges(
155                 Mockito.eq(proxy),
156                 Mockito.<HttpResponse>any(),
157                 Mockito.<HttpClientContext>any())).thenReturn(challenges);
158         Mockito.when(proxyAuthStrategy.select(
159                 Mockito.same(challenges),
160                 Mockito.eq(proxy),
161                 Mockito.<HttpResponse>any(),
162                 Mockito.<HttpClientContext>any())).thenReturn(
163                 new LinkedList<AuthOption>(Arrays.asList(authOption)));
164 
165     }
166 
167     @Test
168     public void testExecRequestNonPersistentConnection() throws Exception {
169         final HttpRoute route = new HttpRoute(target);
170         final HttpClientContext context = new HttpClientContext();
171         final RequestConfig config = RequestConfig.custom()
172                 .setConnectTimeout(123)
173                 .setSocketTimeout(234)
174                 .setConnectionRequestTimeout(345)
175                 .build();
176         context.setRequestConfig(config);
177         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
178         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
179         Mockito.when(requestExecutor.execute(
180                 Mockito.same(request),
181                 Mockito.<HttpClientConnection>any(),
182                 Mockito.<HttpClientContext>any())).thenReturn(response);
183 
184         final CloseableHttpResponse finalResponse = mainClientExec.execute(
185                 route, request, context, execAware);
186         Mockito.verify(connManager).requestConnection(route, null);
187         Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS);
188         Mockito.verify(execAware, Mockito.times(1)).setCancellable(connRequest);
189         Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.<Cancellable>any());
190         Mockito.verify(connManager).connect(managedConn, route, 123, context);
191         Mockito.verify(connManager).routeComplete(managedConn, route, context);
192         Mockito.verify(managedConn).setSocketTimeout(234);
193         Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
194         Mockito.verify(managedConn, Mockito.times(1)).close();
195         Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);
196 
197         Assert.assertNotNull(context.getTargetAuthState());
198         Assert.assertNotNull(context.getProxyAuthState());
199         Assert.assertSame(managedConn, context.getConnection());
200         Assert.assertNull(context.getUserToken());
201         Assert.assertNotNull(finalResponse);
202         Assert.assertTrue(finalResponse instanceof HttpResponseProxy);
203     }
204 
205     @Test
206     public void testExecRequestPersistentConnection() throws Exception {
207         final HttpRoute route = new HttpRoute(target);
208         final HttpClientContext context = new HttpClientContext();
209         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
210         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
211 
212         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
213         Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
214         Mockito.when(requestExecutor.execute(
215                 Mockito.same(request),
216                 Mockito.<HttpClientConnection>any(),
217                 Mockito.<HttpClientContext>any())).thenReturn(response);
218         Mockito.when(reuseStrategy.keepAlive(
219                 Mockito.same(response),
220                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
221         Mockito.when(keepAliveStrategy.getKeepAliveDuration(
222                 Mockito.same(response),
223                 Mockito.<HttpClientContext>any())).thenReturn(678L);
224 
225         final CloseableHttpResponse finalResponse = mainClientExec.execute(
226                 route, request, context, execAware);
227         Mockito.verify(connManager).requestConnection(route, null);
228         Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
229         Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
230         Mockito.verify(connManager).releaseConnection(managedConn, null, 678L, TimeUnit.MILLISECONDS);
231         Mockito.verify(managedConn, Mockito.never()).close();
232 
233         Assert.assertNotNull(finalResponse);
234         Assert.assertTrue(finalResponse instanceof HttpResponseProxy);
235     }
236 
237     @Test
238     public void testExecRequestPersistentStatefulConnection() throws Exception {
239         final HttpRoute route = new HttpRoute(target);
240         final HttpClientContext context = new HttpClientContext();
241         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
242         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
243 
244         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
245         Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
246         Mockito.when(requestExecutor.execute(
247                 Mockito.same(request),
248                 Mockito.<HttpClientConnection>any(),
249                 Mockito.<HttpClientContext>any())).thenReturn(response);
250         Mockito.when(reuseStrategy.keepAlive(
251                 Mockito.same(response),
252                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
253         Mockito.when(userTokenHandler.getUserToken(
254                 Mockito.<HttpClientContext>any())).thenReturn("this and that");
255 
256         mainClientExec.execute(route, request, context, execAware);
257         Mockito.verify(connManager).requestConnection(route, null);
258         Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
259         Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
260         Mockito.verify(connManager).releaseConnection(managedConn, "this and that", 0, TimeUnit.MILLISECONDS);
261         Mockito.verify(managedConn, Mockito.never()).close();
262 
263         Assert.assertEquals("this and that", context.getUserToken());
264     }
265 
266     @Test
267     public void testExecRequestConnectionRelease() throws Exception {
268         final HttpRoute route = new HttpRoute(target);
269         final HttpClientContext context = new HttpClientContext();
270         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
271         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
272         // The entity is streaming
273         response.setEntity(EntityBuilder.create()
274                 .setStream(new ByteArrayInputStream(new byte[]{}))
275                 .build());
276 
277         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
278         Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
279         Mockito.when(requestExecutor.execute(
280                 Mockito.same(request),
281                 Mockito.<HttpClientConnection>any(),
282                 Mockito.<HttpClientContext>any())).thenReturn(response);
283         Mockito.when(reuseStrategy.keepAlive(
284                 Mockito.same(response),
285                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
286 
287         final CloseableHttpResponse finalResponse = mainClientExec.execute(
288                 route, request, context, execAware);
289         Mockito.verify(connManager).requestConnection(route, null);
290         Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
291         Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
292         Mockito.verify(connManager, Mockito.never()).releaseConnection(
293                 Mockito.same(managedConn),
294                 Mockito.any(),
295                 Mockito.anyInt(),
296                 Mockito.<TimeUnit>any());
297         Mockito.verify(managedConn, Mockito.never()).close();
298 
299         Assert.assertNotNull(finalResponse);
300         Assert.assertTrue(finalResponse instanceof HttpResponseProxy);
301         finalResponse.close();
302 
303         Mockito.verify(connManager, Mockito.times(1)).releaseConnection(
304                 managedConn, null, 0, TimeUnit.MILLISECONDS);
305         Mockito.verify(managedConn, Mockito.times(1)).shutdown();
306     }
307 
308     @Test
309     public void testSocketTimeoutExistingConnection() throws Exception {
310         final HttpRoute route = new HttpRoute(target);
311         final HttpClientContext context = new HttpClientContext();
312         final RequestConfig config = RequestConfig.custom().setSocketTimeout(3000).build();
313         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
314         context.setRequestConfig(config);
315         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
316         Mockito.when(managedConn.isOpen()).thenReturn(true);
317         Mockito.when(requestExecutor.execute(
318                 Mockito.same(request),
319                 Mockito.<HttpClientConnection>any(),
320                 Mockito.<HttpClientContext>any())).thenReturn(response);
321 
322         mainClientExec.execute(route, request, context, execAware);
323         Mockito.verify(managedConn).setSocketTimeout(3000);
324     }
325 
326     @Test
327     public void testSocketTimeoutReset() throws Exception {
328         final HttpRoute route = new HttpRoute(target);
329         final HttpClientContext context = new HttpClientContext();
330         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
331         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
332         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
333         Mockito.when(requestExecutor.execute(
334                 Mockito.same(request),
335                 Mockito.<HttpClientConnection>any(),
336                 Mockito.<HttpClientContext>any())).thenReturn(response);
337 
338         mainClientExec.execute(route, request, context, execAware);
339         Mockito.verify(managedConn, Mockito.never()).setSocketTimeout(Mockito.anyInt());
340     }
341 
342     @Test(expected=RequestAbortedException.class)
343     public void testExecAbortedPriorToConnectionLease() throws Exception {
344         final HttpRoute route = new HttpRoute(target);
345         final HttpClientContext context = new HttpClientContext();
346         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
347 
348         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE);
349         Mockito.when(execAware.isAborted()).thenReturn(Boolean.TRUE);
350         try {
351             mainClientExec.execute(route, request, context, execAware);
352         } catch (final IOException ex) {
353             Mockito.verify(connRequest, Mockito.times(1)).cancel();
354             throw ex;
355         }
356     }
357 
358     @Test(expected=RequestAbortedException.class)
359     public void testExecAbortedPriorToConnectionSetup() throws Exception {
360         final HttpRoute route = new HttpRoute(target);
361         final HttpClientContext context = new HttpClientContext();
362         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
363 
364         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE);
365         Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.TRUE);
366         try {
367             mainClientExec.execute(route, request, context, execAware);
368         } catch (final IOException ex) {
369             Mockito.verify(connRequest, Mockito.times(1)).get(0, TimeUnit.MILLISECONDS);
370             Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.<Cancellable>any());
371             Mockito.verify(connManager, Mockito.never()).connect(
372                     Mockito.same(managedConn),
373                     Mockito.<HttpRoute>any(),
374                     Mockito.anyInt(),
375                     Mockito.<HttpContext>any());
376             throw ex;
377         }
378     }
379 
380     @Test(expected=RequestAbortedException.class)
381     public void testExecAbortedPriorToRequestExecution() throws Exception {
382         final HttpRoute route = new HttpRoute(target);
383         final HttpClientContext context = new HttpClientContext();
384         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
385 
386         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE);
387         Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE);
388         try {
389             mainClientExec.execute(route, request, context, execAware);
390         } catch (final IOException ex) {
391             Mockito.verify(connRequest, Mockito.times(1)).get(0, TimeUnit.MILLISECONDS);
392             Mockito.verify(connManager, Mockito.times(1)).connect(managedConn, route, 0, context);
393             Mockito.verify(requestExecutor, Mockito.never()).execute(
394                     Mockito.same(request),
395                     Mockito.<HttpClientConnection>any(),
396                     Mockito.<HttpClientContext>any());
397             throw ex;
398         }
399     }
400 
401     @Test(expected=RequestAbortedException.class)
402     public void testExecConnectionRequestFailed() throws Exception {
403         final HttpRoute route = new HttpRoute(target);
404         final HttpClientContext context = new HttpClientContext();
405         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
406 
407         Mockito.when(connRequest.get(Mockito.anyInt(), Mockito.<TimeUnit>any()))
408                 .thenThrow(new ExecutionException("Opppsie", null));
409         mainClientExec.execute(route, request, context, execAware);
410     }
411 
412     @Test
413     public void testExecRequestRetryOnAuthChallenge() throws Exception {
414         final HttpRoute route = new HttpRoute(target);
415         final HttpClientContext context = new HttpClientContext();
416         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
417         final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
418         final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
419         response1.setEntity(EntityBuilder.create()
420                 .setStream(instream1)
421                 .build());
422         final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
423         final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
424         response2.setEntity(EntityBuilder.create()
425                 .setStream(instream2)
426                 .build());
427 
428         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
429         Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
430         Mockito.when(requestExecutor.execute(
431                 Mockito.same(request),
432                 Mockito.<HttpClientConnection>any(),
433                 Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
434         Mockito.when(reuseStrategy.keepAlive(
435                 Mockito.<HttpResponse>any(),
436                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
437         Mockito.when(targetAuthStrategy.isAuthenticationRequested(
438                 Mockito.eq(target),
439                 Mockito.same(response1),
440                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
441 
442         final CloseableHttpResponse finalResponse = mainClientExec.execute(
443                 route, request, context, execAware);
444         Mockito.verify(requestExecutor, Mockito.times(2)).execute(request, managedConn, context);
445         Mockito.verify(instream1).close();
446         Mockito.verify(instream2, Mockito.never()).close();
447 
448         Assert.assertNotNull(finalResponse);
449         Assert.assertEquals(200, finalResponse.getStatusLine().getStatusCode());
450     }
451 
452     @Test
453     public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exception {
454         final HttpRoute route = new HttpRoute(target);
455         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
456         final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
457         final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
458         response1.setEntity(EntityBuilder.create()
459                 .setStream(instream1)
460                 .build());
461         final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
462         final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
463         response2.setEntity(EntityBuilder.create()
464                 .setStream(instream2)
465                 .build());
466 
467         final AuthState proxyAuthState = new AuthState();
468         proxyAuthState.setState(AuthProtocolState.SUCCESS);
469         proxyAuthState.update(new NTLMScheme(), new NTCredentials("user:pass"));
470 
471         final HttpClientContext context = new HttpClientContext();
472         context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState);
473 
474         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
475         Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
476         Mockito.when(requestExecutor.execute(
477                 Mockito.same(request),
478                 Mockito.<HttpClientConnection>any(),
479                 Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
480         Mockito.when(reuseStrategy.keepAlive(
481                 Mockito.<HttpResponse>any(),
482                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
483         Mockito.when(targetAuthStrategy.isAuthenticationRequested(
484                 Mockito.eq(target),
485                 Mockito.same(response1),
486                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
487 
488         final CloseableHttpResponse finalResponse = mainClientExec.execute(
489                 route, request, context, execAware);
490         Mockito.verify(requestExecutor, Mockito.times(2)).execute(request, managedConn, context);
491         Mockito.verify(managedConn).close();
492         Mockito.verify(instream2, Mockito.never()).close();
493 
494         Assert.assertNotNull(finalResponse);
495         Assert.assertEquals(200, finalResponse.getStatusLine().getStatusCode());
496         Assert.assertNull(proxyAuthState.getAuthScheme());
497         Assert.assertNull(proxyAuthState.getCredentials());
498     }
499 
500     @Test(expected = NonRepeatableRequestException.class)
501     public void testExecEntityEnclosingRequest() throws Exception {
502         final HttpRoute route = new HttpRoute(target);
503         final HttpClientContext context = new HttpClientContext();
504         final HttpPost post = new HttpPost("http://bar/test");
505         final InputStream instream0 = new ByteArrayInputStream(new byte[] {1, 2, 3});
506         post.setEntity(EntityBuilder.create()
507                 .setStream(instream0)
508                 .build());
509         final HttpRequestWrapper request = HttpRequestWrapper.wrap(post);
510 
511         final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
512         final InputStream instream1 = new ByteArrayInputStream(new byte[] {1, 2, 3});
513         response1.setEntity(EntityBuilder.create()
514                 .setStream(instream1)
515                 .build());
516 
517         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
518         Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
519         Mockito.when(requestExecutor.execute(
520                 Mockito.same(request),
521                 Mockito.<HttpClientConnection>any(),
522                 Mockito.<HttpClientContext>any())).thenAnswer(new Answer<HttpResponse>() {
523 
524             @Override
525             public HttpResponse answer(final InvocationOnMock invocationOnMock) throws Throwable {
526                 final Object[] args = invocationOnMock.getArguments();
527                 final HttpEntityEnclosingRequest requestEE = (HttpEntityEnclosingRequest) args[0];
528                 requestEE.getEntity().writeTo(new ByteArrayOutputStream());
529                 return response1;
530             }
531 
532         });
533         Mockito.when(reuseStrategy.keepAlive(
534                 Mockito.<HttpResponse>any(),
535                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
536         Mockito.when(targetAuthStrategy.isAuthenticationRequested(
537                 Mockito.eq(target),
538                 Mockito.same(response1),
539                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
540 
541         mainClientExec.execute(route, request, context, execAware);
542     }
543 
544     @Test(expected=InterruptedIOException.class)
545     public void testExecConnectionShutDown() throws Exception {
546         final HttpRoute route = new HttpRoute(target);
547         final HttpClientContext context = new HttpClientContext();
548         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
549 
550         Mockito.when(requestExecutor.execute(
551                 Mockito.<HttpRequest>any(),
552                 Mockito.<HttpClientConnection>any(),
553                 Mockito.<HttpClientContext>any())).thenThrow(new ConnectionShutdownException());
554 
555         mainClientExec.execute(route, request, context, execAware);
556     }
557 
558     @Test(expected=RuntimeException.class)
559     public void testExecRuntimeException() throws Exception {
560         final HttpRoute route = new HttpRoute(target);
561         final HttpClientContext context = new HttpClientContext();
562         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
563 
564         Mockito.when(requestExecutor.execute(
565                 Mockito.<HttpRequest>any(),
566                 Mockito.<HttpClientConnection>any(),
567                 Mockito.<HttpClientContext>any())).thenThrow(new RuntimeException("Ka-boom"));
568 
569         try {
570             mainClientExec.execute(route, request, context, execAware);
571         } catch (final Exception ex) {
572             Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);
573 
574             throw ex;
575         }
576     }
577 
578     @Test(expected=HttpException.class)
579     public void testExecHttpException() throws Exception {
580         final HttpRoute route = new HttpRoute(target);
581         final HttpClientContext context = new HttpClientContext();
582         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
583 
584         Mockito.when(requestExecutor.execute(
585                 Mockito.<HttpRequest>any(),
586                 Mockito.<HttpClientConnection>any(),
587                 Mockito.<HttpClientContext>any())).thenThrow(new HttpException("Ka-boom"));
588 
589         try {
590             mainClientExec.execute(route, request, context, execAware);
591         } catch (final Exception ex) {
592             Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);
593 
594             throw ex;
595         }
596     }
597 
598     @Test(expected=IOException.class)
599     public void testExecIOException() throws Exception {
600         final HttpRoute route = new HttpRoute(target);
601         final HttpClientContext context = new HttpClientContext();
602         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
603 
604         Mockito.when(requestExecutor.execute(
605                 Mockito.<HttpRequest>any(),
606                 Mockito.<HttpClientConnection>any(),
607                 Mockito.<HttpClientContext>any())).thenThrow(new IOException("Ka-boom"));
608 
609         try {
610             mainClientExec.execute(route, request, context, execAware);
611         } catch (final Exception ex) {
612             Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);
613 
614             throw ex;
615         }
616     }
617 
618     @Test
619     public void testEstablishDirectRoute() throws Exception {
620         final AuthState authState = new AuthState();
621         final HttpRoute route = new HttpRoute(target);
622         final HttpClientContext context = new HttpClientContext();
623         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
624 
625         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
626 
627         mainClientExec.establishRoute(authState, managedConn, route, request, context);
628 
629         Mockito.verify(connManager).connect(managedConn, route, 0, context);
630         Mockito.verify(connManager).routeComplete(managedConn, route, context);
631     }
632 
633     @Test
634     public void testEstablishRouteDirectProxy() throws Exception {
635         final AuthState authState = new AuthState();
636         final HttpRoute route = new HttpRoute(target, null, proxy, false);
637         final HttpClientContext context = new HttpClientContext();
638         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
639 
640         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
641 
642         mainClientExec.establishRoute(authState, managedConn, route, request, context);
643 
644         Mockito.verify(connManager).connect(managedConn, route, 0, context);
645         Mockito.verify(connManager).routeComplete(managedConn, route, context);
646     }
647 
648     @Test
649     public void testEstablishRouteViaProxyTunnel() throws Exception {
650         final AuthState authState = new AuthState();
651         final HttpRoute route = new HttpRoute(target, null, proxy, true);
652         final HttpClientContext context = new HttpClientContext();
653         final RequestConfig config = RequestConfig.custom()
654                 .setConnectTimeout(321)
655                 .build();
656         context.setRequestConfig(config);
657         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
658         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
659 
660         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
661         Mockito.when(requestExecutor.execute(
662                 Mockito.<HttpRequest>any(),
663                 Mockito.<HttpClientConnection>any(),
664                 Mockito.<HttpClientContext>any())).thenReturn(response);
665 
666         mainClientExec.establishRoute(authState, managedConn, route, request, context);
667 
668         Mockito.verify(connManager).connect(managedConn, route, 321, context);
669         Mockito.verify(connManager).routeComplete(managedConn, route, context);
670         final ArgumentCaptor<HttpRequest> reqCaptor = ArgumentCaptor.forClass(HttpRequest.class);
671         Mockito.verify(requestExecutor).execute(
672                 reqCaptor.capture(),
673                 Mockito.same(managedConn),
674                 Mockito.same(context));
675         final HttpRequest connect = reqCaptor.getValue();
676         Assert.assertNotNull(connect);
677         Assert.assertEquals("CONNECT", connect.getRequestLine().getMethod());
678         Assert.assertEquals(HttpVersion.HTTP_1_1, connect.getRequestLine().getProtocolVersion());
679         Assert.assertEquals("foo:80", connect.getRequestLine().getUri());
680     }
681 
682     @Test(expected = HttpException.class)
683     public void testEstablishRouteViaProxyTunnelUnexpectedResponse() throws Exception {
684         final AuthState authState = new AuthState();
685         final HttpRoute route = new HttpRoute(target, null, proxy, true);
686         final HttpClientContext context = new HttpClientContext();
687         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
688         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 101, "Lost");
689 
690         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
691         Mockito.when(requestExecutor.execute(
692                 Mockito.<HttpRequest>any(),
693                 Mockito.<HttpClientConnection>any(),
694                 Mockito.<HttpClientContext>any())).thenReturn(response);
695 
696         mainClientExec.establishRoute(authState, managedConn, route, request, context);
697     }
698 
699     @Test(expected = HttpException.class)
700     public void testEstablishRouteViaProxyTunnelFailure() throws Exception {
701         final AuthState authState = new AuthState();
702         final HttpRoute route = new HttpRoute(target, null, proxy, true);
703         final HttpClientContext context = new HttpClientContext();
704         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
705         final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 500, "Boom");
706         response.setEntity(new StringEntity("Ka-boom"));
707 
708         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
709         Mockito.when(requestExecutor.execute(
710                 Mockito.<HttpRequest>any(),
711                 Mockito.<HttpClientConnection>any(),
712                 Mockito.<HttpClientContext>any())).thenReturn(response);
713 
714         try {
715             mainClientExec.establishRoute(authState, managedConn, route, request, context);
716         } catch (final TunnelRefusedException ex) {
717             final HttpResponse r = ex.getResponse();
718             Assert.assertEquals("Ka-boom", EntityUtils.toString(r.getEntity()));
719 
720             Mockito.verify(managedConn).close();
721 
722             throw ex;
723         }
724     }
725 
726     @Test
727     public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengePersistentConnection() throws Exception {
728         final AuthState authState = new AuthState();
729         final HttpRoute route = new HttpRoute(target, null, proxy, true);
730         final HttpClientContext context = new HttpClientContext();
731         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
732         final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
733         final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
734         response1.setEntity(EntityBuilder.create()
735                 .setStream(instream1)
736                 .build());
737         final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
738 
739         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
740         Mockito.when(proxyAuthStrategy.isAuthenticationRequested(
741                 Mockito.eq(proxy),
742                 Mockito.same(response1),
743                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
744         Mockito.when(reuseStrategy.keepAlive(
745                 Mockito.<HttpResponse>any(),
746                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
747         Mockito.when(requestExecutor.execute(
748                 Mockito.<HttpRequest>any(),
749                 Mockito.<HttpClientConnection>any(),
750                 Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
751 
752         mainClientExec.establishRoute(authState, managedConn, route, request, context);
753 
754         Mockito.verify(connManager).connect(managedConn, route, 0, context);
755         Mockito.verify(connManager).routeComplete(managedConn, route, context);
756         Mockito.verify(instream1).close();
757     }
758 
759     @Test
760     public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengeNonPersistentConnection() throws Exception {
761         final AuthState authState = new AuthState();
762         final HttpRoute route = new HttpRoute(target, null, proxy, true);
763         final HttpClientContext context = new HttpClientContext();
764         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
765         final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
766         final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
767         response1.setEntity(EntityBuilder.create()
768                 .setStream(instream1)
769                 .build());
770         final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
771 
772         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
773         Mockito.when(proxyAuthStrategy.isAuthenticationRequested(
774                 Mockito.eq(proxy),
775                 Mockito.same(response1),
776                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
777         Mockito.when(reuseStrategy.keepAlive(
778                 Mockito.<HttpResponse>any(),
779                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
780         Mockito.when(requestExecutor.execute(
781                 Mockito.<HttpRequest>any(),
782                 Mockito.<HttpClientConnection>any(),
783                 Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
784 
785         mainClientExec.establishRoute(authState, managedConn, route, request, context);
786 
787         Mockito.verify(connManager).connect(managedConn, route, 0, context);
788         Mockito.verify(connManager).routeComplete(managedConn, route, context);
789         Mockito.verify(instream1, Mockito.never()).close();
790         Mockito.verify(managedConn).close();
791     }
792 
793     @Test(expected = HttpException.class)
794     public void testEstablishRouteViaProxyTunnelMultipleHops() throws Exception {
795         final AuthState authState = new AuthState();
796         final HttpHost proxy1 = new HttpHost("this", 8888);
797         final HttpHost proxy2 = new HttpHost("that", 8888);
798         final HttpRoute route = new HttpRoute(target, null, new HttpHost[] {proxy1, proxy2},
799                 true, RouteInfo.TunnelType.TUNNELLED, RouteInfo.LayerType.LAYERED);
800         final HttpClientContext context = new HttpClientContext();
801         final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
802 
803         Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
804 
805         mainClientExec.establishRoute(authState, managedConn, route, request, context);
806     }
807 
808 }