You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2013/08/07 21:20:06 UTC

svn commit: r1511445 - in /httpcomponents/httpclient/trunk/httpclient/src: main/java/org/apache/http/impl/execchain/MainClientExec.java test/java/org/apache/http/impl/execchain/TestMainClientExec.java

Author: olegk
Date: Wed Aug  7 19:20:05 2013
New Revision: 1511445

URL: http://svn.apache.org/r1511445
Log:
Unit tests for MainClientExec

Modified:
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java?rev=1511445&r1=1511444&r2=1511445&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java Wed Aug  7 19:20:05 2013
@@ -193,12 +193,7 @@ public class MainClientExec implements C
         final ConnectionHolder connHolder = new ConnectionHolder(this.log, this.connManager, managedConn);
         try {
             if (execAware != null) {
-                if (execAware.isAborted()) {
-                    connHolder.close();
-                    throw new RequestAbortedException("Request aborted");
-                } else {
-                    execAware.setCancellable(connHolder);
-                }
+                execAware.setCancellable(connHolder);
             }
 
             HttpResponse response = null;
@@ -340,7 +335,7 @@ public class MainClientExec implements C
     /**
      * Establishes the target route.
      */
-    private void establishRoute(
+    void establishRoute(
             final AuthState proxyAuthState,
             final HttpClientConnection managedConn,
             final HttpRoute route,

Modified: httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java?rev=1511445&r1=1511444&r2=1511445&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java Wed Aug  7 19:20:05 2013
@@ -26,104 +26,760 @@
  */
 package org.apache.http.impl.execchain;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 
+import junit.framework.Assert;
 import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.Header;
 import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpEntityEnclosingRequest;
+import org.apache.http.HttpException;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
+import org.apache.http.HttpVersion;
+import org.apache.http.auth.AUTH;
+import org.apache.http.auth.AuthOption;
+import org.apache.http.auth.AuthProtocolState;
+import org.apache.http.auth.AuthState;
+import org.apache.http.auth.NTCredentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
 import org.apache.http.client.AuthenticationStrategy;
+import org.apache.http.client.NonRepeatableRequestException;
 import org.apache.http.client.UserTokenHandler;
 import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.entity.EntityBuilder;
+import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpExecutionAware;
 import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
 import org.apache.http.client.methods.HttpRequestWrapper;
 import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.concurrent.Cancellable;
 import org.apache.http.conn.ConnectionKeepAliveStrategy;
 import org.apache.http.conn.ConnectionRequest;
 import org.apache.http.conn.HttpClientConnectionManager;
 import org.apache.http.conn.routing.HttpRoute;
+import org.apache.http.conn.routing.RouteInfo;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.auth.NTLMScheme;
+import org.apache.http.impl.conn.ConnectionShutdownException;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.protocol.HttpContext;
 import org.apache.http.protocol.HttpRequestExecutor;
+import org.apache.http.util.EntityUtils;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
 public class TestMainClientExec {
 
-    private MainClientExec mainClientExec;
+    @Mock
     private HttpRequestExecutor requestExecutor;
+    @Mock
     private HttpClientConnectionManager connManager;
+    @Mock
     private ConnectionReuseStrategy reuseStrategy;
+    @Mock
     private ConnectionKeepAliveStrategy keepAliveStrategy;
+    @Mock
     private AuthenticationStrategy targetAuthStrategy;
+    @Mock
     private AuthenticationStrategy proxyAuthStrategy;
+    @Mock
     private UserTokenHandler userTokenHandler;
-    private HttpClientContext context;
-    private HttpGet request;
+    @Mock
     private HttpExecutionAware execAware;
-    private HttpRoute route;
+    @Mock
     private ConnectionRequest connRequest;
+    @Mock
     private HttpClientConnection managedConn;
-    private HttpResponse response;
-    private RequestConfig config;
+
+    private MainClientExec mainClientExec;
+    private HttpHost target;
+    private HttpHost proxy;
 
     @Before
     public void setup() throws Exception {
-        requestExecutor = Mockito.mock(HttpRequestExecutor.class);
-        connManager = Mockito.mock(HttpClientConnectionManager.class);
-        reuseStrategy = Mockito.mock(ConnectionReuseStrategy.class);
-        keepAliveStrategy = Mockito.mock(ConnectionKeepAliveStrategy.class);
-        targetAuthStrategy = Mockito.mock(AuthenticationStrategy.class);
-        proxyAuthStrategy = Mockito.mock(AuthenticationStrategy.class);
-        userTokenHandler = Mockito.mock(UserTokenHandler.class);
+        MockitoAnnotations.initMocks(this);
         mainClientExec = new MainClientExec(requestExecutor, connManager, reuseStrategy,
             keepAliveStrategy, targetAuthStrategy, proxyAuthStrategy, userTokenHandler);
-        route = new HttpRoute(new HttpHost("foo", 8080));
-        context = new HttpClientContext();
-        config = RequestConfig.custom().setSocketTimeout(3000).build();
+        target = new HttpHost("foo", 80);
+        proxy = new HttpHost("bar", 8888);
+
+        Mockito.when(connManager.requestConnection(
+                Mockito.<HttpRoute>any(), Mockito.any())).thenReturn(connRequest);
+        Mockito.when(connRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(managedConn);
+        final Map<String, Header> challenges = new HashMap<String, Header>();
+        challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test"));
+        final AuthOption authOption = new AuthOption(
+                new BasicScheme(), new UsernamePasswordCredentials("user:pass"));
+        Mockito.when(targetAuthStrategy.getChallenges(
+                Mockito.eq(target),
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(challenges);
+        Mockito.when(targetAuthStrategy.getChallenges(
+                Mockito.eq(target),
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(challenges);
+        Mockito.when(targetAuthStrategy.select(
+                Mockito.same(challenges),
+                Mockito.eq(target),
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(
+                new LinkedList<AuthOption>(Arrays.asList(authOption)));
+        Mockito.when(proxyAuthStrategy.getChallenges(
+                Mockito.eq(proxy),
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(challenges);
+        Mockito.when(proxyAuthStrategy.getChallenges(
+                Mockito.eq(proxy),
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(challenges);
+        Mockito.when(proxyAuthStrategy.select(
+                Mockito.same(challenges),
+                Mockito.eq(proxy),
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(
+                new LinkedList<AuthOption>(Arrays.asList(authOption)));
+
+    }
+
+    @Test
+    public void testExecRequestNonPersistentConnection() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final RequestConfig config = RequestConfig.custom()
+                .setConnectTimeout(123)
+                .setSocketTimeout(234)
+                .setConnectionRequestTimeout(345)
+                .build();
         context.setRequestConfig(config);
-        execAware = Mockito.mock(HttpExecutionAware.class);
-        connRequest = Mockito.mock(ConnectionRequest.class);
-        managedConn = Mockito.mock(HttpClientConnection.class);
-        response = Mockito.mock(HttpResponse.class);
-        Mockito.when(
-            connManager.requestConnection(Mockito.any(HttpRoute.class), Mockito.any(Object.class)))
-            .thenReturn(connRequest);
-        Mockito.when(connRequest.get(Mockito.anyLong(), Mockito.any(TimeUnit.class))).thenReturn(
-            managedConn);
-        managedConn.setSocketTimeout(Mockito.eq(3000));
-        Mockito.when(
-            requestExecutor.execute(Mockito.any(HttpRequest.class),
-                Mockito.any(HttpClientConnection.class), Mockito.any(HttpClientContext.class)))
-            .thenReturn(response);
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+        Mockito.when(requestExecutor.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+
+        final CloseableHttpResponse finalResponse = mainClientExec.execute(
+                route, request, context, execAware);
+        Mockito.verify(connManager).requestConnection(route, null);
+        Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS);
+        Mockito.verify(execAware, Mockito.times(1)).setCancellable(connRequest);
+        Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.<Cancellable>any());
+        Mockito.verify(connManager).connect(managedConn, route, 123, context);
+        Mockito.verify(connManager).routeComplete(managedConn, route, context);
+        Mockito.verify(managedConn).setSocketTimeout(234);
+        Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
+        Mockito.verify(managedConn, Mockito.times(1)).close();
+        Mockito.verify(connManager).releaseConnection(managedConn, null, 0, TimeUnit.MILLISECONDS);
+
+        Assert.assertNotNull(context.getTargetAuthState());
+        Assert.assertNotNull(context.getProxyAuthState());
+        Assert.assertSame(managedConn, context.getConnection());
+        Assert.assertNull(context.getUserToken());
+        Assert.assertNotNull(finalResponse);
+        Assert.assertTrue(Proxy.isProxyClass(finalResponse.getClass()));
     }
 
     @Test
-    public void testSocketTimeoutNewConnection() throws Exception {
-        request = new HttpGet("http://bar/test");
-        request.setConfig(config);
-        mainClientExec.execute(route, HttpRequestWrapper.wrap(request), context, execAware);
-        Mockito.verify(managedConn).setSocketTimeout(3000);
+    public void testExecRequestPersistentConnection() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(keepAliveStrategy.getKeepAliveDuration(
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(678L);
+
+        final CloseableHttpResponse finalResponse = mainClientExec.execute(
+                route, request, context, execAware);
+        Mockito.verify(connManager).requestConnection(route, null);
+        Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
+        Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
+        Mockito.verify(connManager).releaseConnection(managedConn, null, 678L, TimeUnit.MILLISECONDS);
+        Mockito.verify(managedConn, Mockito.never()).close();
+
+        Assert.assertNotNull(finalResponse);
+        Assert.assertTrue(Proxy.isProxyClass(finalResponse.getClass()));
+    }
+
+    @Test
+    public void testExecRequestPersistentStatefulConnection() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(userTokenHandler.getUserToken(
+                Mockito.<HttpClientContext>any())).thenReturn("this and that");
+
+        mainClientExec.execute(route, request, context, execAware);
+        Mockito.verify(connManager).requestConnection(route, null);
+        Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
+        Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
+        Mockito.verify(connManager).releaseConnection(managedConn, "this and that", 0, TimeUnit.MILLISECONDS);
+        Mockito.verify(managedConn, Mockito.never()).close();
+
+        Assert.assertEquals("this and that", context.getUserToken());
+    }
+
+    @Test
+    public void testExecRequestConnectionRelease() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+        // The entity is streaming
+        response.setEntity(EntityBuilder.create()
+                .setStream(new ByteArrayInputStream(new byte[]{}))
+                .build());
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
+
+        final CloseableHttpResponse finalResponse = mainClientExec.execute(
+                route, request, context, execAware);
+        Mockito.verify(connManager).requestConnection(route, null);
+        Mockito.verify(connRequest).get(0, TimeUnit.MILLISECONDS);
+        Mockito.verify(requestExecutor, Mockito.times(1)).execute(request, managedConn, context);
+        Mockito.verify(connManager, Mockito.never()).releaseConnection(
+                Mockito.same(managedConn),
+                Mockito.any(),
+                Mockito.anyInt(),
+                Mockito.<TimeUnit>any());
+        Mockito.verify(managedConn, Mockito.never()).close();
+
+        Assert.assertNotNull(finalResponse);
+        Assert.assertTrue(Proxy.isProxyClass(finalResponse.getClass()));
+        finalResponse.close();
+
+        Mockito.verify(connManager, Mockito.times(1)).releaseConnection(
+                managedConn, null, 0, TimeUnit.MILLISECONDS);
+        Mockito.verify(managedConn, Mockito.times(1)).shutdown();
+    }
+
+    @Test
+    public void testExecRequestStaleConnectionCheck() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final RequestConfig config = RequestConfig.custom()
+                .setStaleConnectionCheckEnabled(true)
+                .build();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        context.setRequestConfig(config);
+        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(managedConn.isStale()).thenReturn(Boolean.TRUE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+
+        final CloseableHttpResponse finalResponse = mainClientExec.execute(
+                route, request, context, execAware);
+        Mockito.verify(managedConn, Mockito.times(1)).close();
+
+        Assert.assertNotNull(finalResponse);
+        Assert.assertTrue(Proxy.isProxyClass(finalResponse.getClass()));
     }
 
     @Test
     public void testSocketTimeoutExistingConnection() throws Exception {
-        request = new HttpGet("http://bar/test");
-        request.setConfig(config);
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final RequestConfig config = RequestConfig.custom().setSocketTimeout(3000).build();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        context.setRequestConfig(config);
+        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
         Mockito.when(managedConn.isOpen()).thenReturn(true);
-        mainClientExec.execute(route, HttpRequestWrapper.wrap(request), context, execAware);
+        Mockito.when(requestExecutor.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+
+        mainClientExec.execute(route, request, context, execAware);
         Mockito.verify(managedConn).setSocketTimeout(3000);
     }
 
     @Test
     public void testSocketTimeoutReset() throws Exception {
-        request = new HttpGet("http://bar/test");
-        config = RequestConfig.custom().build();
-        request.setConfig(config);
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+
+        mainClientExec.execute(route, request, context, execAware);
+        Mockito.verify(managedConn, Mockito.never()).setSocketTimeout(Mockito.anyInt());
+    }
+
+    @Test(expected=RequestAbortedException.class)
+    public void testExecAbortedPriorToConnectionLease() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE);
+        Mockito.when(execAware.isAborted()).thenReturn(Boolean.TRUE);
+        try {
+            mainClientExec.execute(route, request, context, execAware);
+        } catch (IOException ex) {
+            Mockito.verify(connRequest, Mockito.times(1)).cancel();
+            throw ex;
+        }
+    }
+
+    @Test(expected=RequestAbortedException.class)
+    public void testExecAbortedPriorToConnectionSetup() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE);
+        Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.TRUE);
+        try {
+            mainClientExec.execute(route, request, context, execAware);
+        } catch (IOException ex) {
+            Mockito.verify(connRequest, Mockito.times(1)).get(0, TimeUnit.MILLISECONDS);
+            Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.<Cancellable>any());
+            Mockito.verify(connManager, Mockito.never()).connect(
+                    Mockito.same(managedConn),
+                    Mockito.<HttpRoute>any(),
+                    Mockito.anyInt(),
+                    Mockito.<HttpContext>any());
+            throw ex;
+        }
+    }
+
+    @Test(expected=RequestAbortedException.class)
+    public void testExecAbortedPriorToRequestExecution() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.FALSE);
+        Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE);
+        try {
+            mainClientExec.execute(route, request, context, execAware);
+        } catch (IOException ex) {
+            Mockito.verify(connRequest, Mockito.times(1)).get(0, TimeUnit.MILLISECONDS);
+            Mockito.verify(connManager, Mockito.times(1)).connect(managedConn, route, 0, context);
+            Mockito.verify(requestExecutor, Mockito.never()).execute(
+                    Mockito.same(request),
+                    Mockito.<HttpClientConnection>any(),
+                    Mockito.<HttpClientContext>any());
+            throw ex;
+        }
+    }
+
+    @Test(expected=RequestAbortedException.class)
+    public void testExecConnectionRequestInterrupted() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+
+        Mockito.when(connRequest.get(Mockito.anyInt(), Mockito.<TimeUnit>any()))
+                .thenThrow(new InterruptedException());
+        mainClientExec.execute(route, request, context, execAware);
+    }
+
+    @Test(expected=RequestAbortedException.class)
+    public void testExecConnectionRequestFailed() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+
+        Mockito.when(connRequest.get(Mockito.anyInt(), Mockito.<TimeUnit>any()))
+                .thenThrow(new ExecutionException("Opppsie", null));
+        mainClientExec.execute(route, request, context, execAware);
+    }
+
+    @Test
+    public void testExecRequestRetryOnAuthChallenge() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
+        final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
+        response1.setEntity(EntityBuilder.create()
+                .setStream(instream1)
+                .build());
+        final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+        final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
+        response2.setEntity(EntityBuilder.create()
+                .setStream(instream2)
+                .build());
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(targetAuthStrategy.isAuthenticationRequested(
+                Mockito.eq(target),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+
+        final CloseableHttpResponse finalResponse = mainClientExec.execute(
+                route, request, context, execAware);
+        Mockito.verify(requestExecutor, Mockito.times(2)).execute(request, managedConn, context);
+        Mockito.verify(instream1).close();
+        Mockito.verify(instream2, Mockito.never()).close();
+
+        Assert.assertNotNull(finalResponse);
+        Assert.assertEquals(200, finalResponse.getStatusLine().getStatusCode());
+    }
+
+    @Test
+    public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
+        final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
+        response1.setEntity(EntityBuilder.create()
+                .setStream(instream1)
+                .build());
+        final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+        final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
+        response2.setEntity(EntityBuilder.create()
+                .setStream(instream2)
+                .build());
+
+        final AuthState proxyAuthState = new AuthState();
+        proxyAuthState.setState(AuthProtocolState.SUCCESS);
+        proxyAuthState.update(new NTLMScheme(), new NTCredentials("user:pass"));
+
+        final HttpClientContext context = new HttpClientContext();
+        context.setAttribute(HttpClientContext.PROXY_AUTH_STATE, proxyAuthState);
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
+        Mockito.when(targetAuthStrategy.isAuthenticationRequested(
+                Mockito.eq(target),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+
+        final CloseableHttpResponse finalResponse = mainClientExec.execute(
+                route, request, context, execAware);
+        Mockito.verify(requestExecutor, Mockito.times(2)).execute(request, managedConn, context);
+        Mockito.verify(instream1).close();
+        Mockito.verify(managedConn).close();
+        Mockito.verify(instream2, Mockito.never()).close();
+
+        Assert.assertNotNull(finalResponse);
+        Assert.assertEquals(200, finalResponse.getStatusLine().getStatusCode());
+        Assert.assertNull(proxyAuthState.getAuthScheme());
+        Assert.assertNull(proxyAuthState.getCredentials());
+    }
+
+    @Test(expected = NonRepeatableRequestException.class)
+    public void testExecEntityEnclosingRequest() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpPost post = new HttpPost("http://bar/test");
+        final InputStream instream0 = new ByteArrayInputStream(new byte[] {1, 2, 3});
+        post.setEntity(EntityBuilder.create()
+                .setStream(instream0)
+                .build());
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(post);
+
+        final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
+        final InputStream instream1 = new ByteArrayInputStream(new byte[] {1, 2, 3});
+        response1.setEntity(EntityBuilder.create()
+                .setStream(instream1)
+                .build());
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(managedConn.isStale()).thenReturn(Boolean.FALSE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenAnswer(new Answer<HttpResponse>() {
+
+            public HttpResponse answer(final InvocationOnMock invocationOnMock) throws Throwable {
+                final Object[] args = invocationOnMock.getArguments();
+                final HttpEntityEnclosingRequest request = (HttpEntityEnclosingRequest) args[0];
+                request.getEntity().writeTo(new ByteArrayOutputStream());
+                return response1;
+            }
+
+        });
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(targetAuthStrategy.isAuthenticationRequested(
+                Mockito.eq(target),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+
+        mainClientExec.execute(route, request, context, execAware);
+    }
+
+    @Test(expected=InterruptedIOException.class)
+    public void testExecConnectionShutDown() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+
+        Mockito.when(requestExecutor.execute(
+                Mockito.<HttpRequest>any(),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenThrow(new ConnectionShutdownException());
+
+        mainClientExec.execute(route, request, context, execAware);
+    }
+
+    @Test
+    public void testEstablishDirectRoute() throws Exception {
+        final AuthState authState = new AuthState();
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+
+        mainClientExec.establishRoute(authState, managedConn, route, request, context);
+
+        Mockito.verify(connManager).connect(managedConn, route, 0, context);
+        Mockito.verify(connManager).routeComplete(managedConn, route, context);
+    }
+
+    @Test
+    public void testEstablishRouteDirectProxy() throws Exception {
+        final AuthState authState = new AuthState();
+        final HttpRoute route = new HttpRoute(target, null, proxy, false);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+
+        mainClientExec.establishRoute(authState, managedConn, route, request, context);
+
+        Mockito.verify(connManager).connect(managedConn, route, 0, context);
+        Mockito.verify(connManager).routeComplete(managedConn, route, context);
+    }
+
+    @Test
+    public void testEstablishRouteViaProxyTunnel() throws Exception {
+        final AuthState authState = new AuthState();
+        final HttpRoute route = new HttpRoute(target, null, proxy, true);
+        final HttpClientContext context = new HttpClientContext();
+        final RequestConfig config = RequestConfig.custom()
+                .setConnectTimeout(321)
+                .build();
         context.setRequestConfig(config);
-        Mockito.when(managedConn.isOpen()).thenReturn(true);
-        mainClientExec.execute(route, HttpRequestWrapper.wrap(request), context, execAware);
-        Mockito.verify(managedConn).setSocketTimeout(0);
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+
+        final ArgumentCaptor<HttpRequest> reqCaptor = ArgumentCaptor.forClass(HttpRequest.class);
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(requestExecutor.execute(
+                reqCaptor.capture(),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+
+        mainClientExec.establishRoute(authState, managedConn, route, request, context);
+
+        Mockito.verify(connManager).connect(managedConn, route, 321, context);
+        Mockito.verify(connManager).routeComplete(managedConn, route, context);
+
+        final HttpRequest connect = reqCaptor.getValue();
+        Assert.assertNotNull(connect);
+        Assert.assertEquals("CONNECT", connect.getRequestLine().getMethod());
+        Assert.assertEquals(HttpVersion.HTTP_1_1, connect.getRequestLine().getProtocolVersion());
+        Assert.assertEquals("foo:80", connect.getRequestLine().getUri());
+    }
+
+    @Test(expected = HttpException.class)
+    public void testEstablishRouteViaProxyTunnelUnexpectedResponse() throws Exception {
+        final AuthState authState = new AuthState();
+        final HttpRoute route = new HttpRoute(target, null, proxy, true);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 101, "Lost");
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.<HttpRequest>any(),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+
+        mainClientExec.establishRoute(authState, managedConn, route, request, context);
     }
-}
+
+    @Test(expected = HttpException.class)
+    public void testEstablishRouteViaProxyTunnelFailure() throws Exception {
+        final AuthState authState = new AuthState();
+        final HttpRoute route = new HttpRoute(target, null, proxy, true);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 500, "Boom");
+        response.setEntity(new StringEntity("Ka-boom"));
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.<HttpRequest>any(),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+
+        try {
+            mainClientExec.establishRoute(authState, managedConn, route, request, context);
+        } catch (TunnelRefusedException ex) {
+            final HttpResponse r = ex.getResponse();
+            Assert.assertEquals("Ka-boom", EntityUtils.toString(r.getEntity()));
+
+            Mockito.verify(managedConn).close();
+
+            throw ex;
+        }
+    }
+
+    @Test
+    public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengePersistentConnection() throws Exception {
+        final AuthState authState = new AuthState();
+        final HttpRoute route = new HttpRoute(target, null, proxy, true);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
+        final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
+        response1.setEntity(EntityBuilder.create()
+                .setStream(instream1)
+                .build());
+        final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(proxyAuthStrategy.isAuthenticationRequested(
+                Mockito.eq(proxy),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.<HttpRequest>any(),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
+
+        mainClientExec.establishRoute(authState, managedConn, route, request, context);
+
+        Mockito.verify(connManager).connect(managedConn, route, 0, context);
+        Mockito.verify(connManager).routeComplete(managedConn, route, context);
+        Mockito.verify(instream1).close();
+    }
+
+    @Test
+    public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengeNonPersistentConnection() throws Exception {
+        final AuthState authState = new AuthState();
+        final HttpRoute route = new HttpRoute(target, null, proxy, true);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+        final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?");
+        final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
+        response1.setEntity(EntityBuilder.create()
+                .setStream(instream1)
+                .build());
+        final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK");
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+        Mockito.when(proxyAuthStrategy.isAuthenticationRequested(
+                Mockito.eq(proxy),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
+        Mockito.when(requestExecutor.execute(
+                Mockito.<HttpRequest>any(),
+                Mockito.<HttpClientConnection>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
+
+        mainClientExec.establishRoute(authState, managedConn, route, request, context);
+
+        Mockito.verify(connManager).connect(managedConn, route, 0, context);
+        Mockito.verify(connManager).routeComplete(managedConn, route, context);
+        Mockito.verify(instream1, Mockito.never()).close();
+        Mockito.verify(managedConn).close();
+    }
+
+    @Test(expected = HttpException.class)
+    public void testEstablishRouteViaProxyTunnelMultipleHops() throws Exception {
+        final AuthState authState = new AuthState();
+        final HttpHost proxy1 = new HttpHost("this", 8888);
+        final HttpHost proxy2 = new HttpHost("that", 8888);
+        final HttpRoute route = new HttpRoute(target, null, new HttpHost[] {proxy1, proxy2},
+                true, RouteInfo.TunnelType.TUNNELLED, RouteInfo.LayerType.LAYERED);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test"));
+
+        Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE);
+
+        mainClientExec.establishRoute(authState, managedConn, route, request, context);
+    }
+
+}
\ No newline at end of file