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