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 2017/05/01 11:11:11 UTC

svn commit: r1793308 [10/11] - in /httpcomponents/httpclient/trunk: httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/ httpclient5-cache/src/test/java/org/apache...

Modified: httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestInternalHttpClient.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestInternalHttpClient.java?rev=1793308&r1=1793307&r2=1793308&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestInternalHttpClient.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestInternalHttpClient.java Mon May  1 11:11:09 2017
@@ -36,17 +36,24 @@ import org.apache.hc.client5.http.auth.C
 import org.apache.hc.client5.http.config.RequestConfig;
 import org.apache.hc.client5.http.cookie.CookieSpecProvider;
 import org.apache.hc.client5.http.cookie.CookieStore;
+import org.apache.hc.client5.http.io.HttpClientConnectionManager;
 import org.apache.hc.client5.http.protocol.ClientProtocolException;
 import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.client5.http.routing.HttpRoutePlanner;
+import org.apache.hc.client5.http.sync.ExecChain;
+import org.apache.hc.client5.http.sync.ExecChainHandler;
 import org.apache.hc.client5.http.sync.methods.HttpGet;
+import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.config.Lookup;
+import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
 
 /**
  *  Simple tests for {@link InternalHttpClient}.
@@ -54,14 +61,27 @@ import org.mockito.Mockito;
 @SuppressWarnings({"static-access"}) // test code
 public class TestInternalHttpClient {
 
-    private ClientExecChain execChain;
+    @Mock
+    private HttpClientConnectionManager connManager;
+    @Mock
+    private HttpRequestExecutor requestExecutor;
+    @Mock
+    private ExecChainHandler execChain;
+    @Mock
     private HttpRoutePlanner routePlanner;
+    @Mock
     private Lookup<CookieSpecProvider> cookieSpecRegistry;
+    @Mock
     private Lookup<AuthSchemeProvider> authSchemeRegistry;
+    @Mock
     private CookieStore cookieStore;
+    @Mock
     private CredentialsProvider credentialsProvider;
+    @Mock
     private RequestConfig defaultConfig;
+    @Mock
     private Closeable closeable1;
+    @Mock
     private Closeable closeable2;
 
     private InternalHttpClient client;
@@ -69,17 +89,8 @@ public class TestInternalHttpClient {
     @SuppressWarnings("unchecked")
     @Before
     public void setup() throws Exception {
-        execChain = Mockito.mock(ClientExecChain.class);
-        routePlanner = Mockito.mock(HttpRoutePlanner.class);
-        cookieSpecRegistry = Mockito.mock(Lookup.class);
-        authSchemeRegistry = Mockito.mock(Lookup.class);
-        cookieStore = Mockito.mock(CookieStore.class);
-        credentialsProvider = Mockito.mock(CredentialsProvider.class);
-        defaultConfig = RequestConfig.custom().build();
-        closeable1 = Mockito.mock(Closeable.class);
-        closeable2 = Mockito.mock(Closeable.class);
-
-        client = new InternalHttpClient(execChain, routePlanner,
+        MockitoAnnotations.initMocks(this);
+        client = new InternalHttpClient(connManager, requestExecutor, new ExecChainElement(execChain, null), routePlanner,
                 cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider,
                 defaultConfig, Arrays.asList(closeable1, closeable2));
 
@@ -97,9 +108,9 @@ public class TestInternalHttpClient {
         client.execute(httpget);
 
         Mockito.verify(execChain).execute(
-                Mockito.<RoutedHttpRequest>any(),
-                Mockito.<HttpClientContext>any(),
-                Mockito.same(httpget));
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any(),
+                Mockito.<ExecChain>any());
     }
 
     @Test(expected=ClientProtocolException.class)
@@ -111,9 +122,9 @@ public class TestInternalHttpClient {
                 Mockito.eq(new HttpHost("somehost")),
                 Mockito.<HttpClientContext>any())).thenReturn(route);
         Mockito.when(execChain.execute(
-                Mockito.<RoutedHttpRequest>any(),
-                Mockito.<HttpClientContext>any(),
-                Mockito.same(httpget))).thenThrow(new HttpException());
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any(),
+                Mockito.<ExecChain>any())).thenThrow(new HttpException());
 
         client.execute(httpget);
     }

Modified: httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMainClientExec.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMainClientExec.java?rev=1793308&r1=1793307&r2=1793308&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMainClientExec.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMainClientExec.java Mon May  1 11:11:09 2017
@@ -33,8 +33,6 @@ import java.io.InputStream;
 import java.io.InterruptedIOException;
 import java.util.Collections;
 import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
 
 import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
 import org.apache.hc.client5.http.HttpRoute;
@@ -45,22 +43,18 @@ import org.apache.hc.client5.http.auth.A
 import org.apache.hc.client5.http.auth.AuthScope;
 import org.apache.hc.client5.http.auth.ChallengeType;
 import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
-import org.apache.hc.client5.http.config.RequestConfig;
 import org.apache.hc.client5.http.entity.EntityBuilder;
 import org.apache.hc.client5.http.impl.ConnectionShutdownException;
 import org.apache.hc.client5.http.impl.auth.BasicScheme;
 import org.apache.hc.client5.http.impl.auth.NTLMScheme;
-import org.apache.hc.client5.http.io.ConnectionEndpoint;
-import org.apache.hc.client5.http.io.HttpClientConnectionManager;
-import org.apache.hc.client5.http.io.LeaseRequest;
 import org.apache.hc.client5.http.protocol.AuthenticationStrategy;
 import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.client5.http.protocol.NonRepeatableRequestException;
 import org.apache.hc.client5.http.protocol.UserTokenHandler;
-import org.apache.hc.client5.http.sync.methods.HttpExecutionAware;
+import org.apache.hc.client5.http.sync.ExecChain;
+import org.apache.hc.client5.http.sync.ExecRuntime;
 import org.apache.hc.client5.http.sync.methods.HttpGet;
 import org.apache.hc.client5.http.sync.methods.HttpPost;
-import org.apache.hc.core5.concurrent.Cancellable;
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.ConnectionReuseStrategy;
@@ -70,12 +64,9 @@ import org.apache.hc.core5.http.HttpHost
 import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.HttpVersion;
-import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
-import org.apache.hc.core5.http.io.HttpClientConnection;
 import org.apache.hc.core5.http.io.entity.EntityUtils;
 import org.apache.hc.core5.http.io.entity.StringEntity;
 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
-import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.util.TimeValue;
 import org.junit.Assert;
@@ -92,10 +83,6 @@ import org.mockito.stubbing.Answer;
 public class TestMainClientExec {
 
     @Mock
-    private HttpRequestExecutor requestExecutor;
-    @Mock
-    private HttpClientConnectionManager connManager;
-    @Mock
     private ConnectionReuseStrategy reuseStrategy;
     @Mock
     private ConnectionKeepAliveStrategy keepAliveStrategy;
@@ -108,11 +95,7 @@ public class TestMainClientExec {
     @Mock
     private UserTokenHandler userTokenHandler;
     @Mock
-    private HttpExecutionAware execAware;
-    @Mock
-    private LeaseRequest connRequest;
-    @Mock
-    private ConnectionEndpoint endpoint;
+    private ExecRuntime endpoint;
 
     private MainClientExec mainClientExec;
     private HttpHost target;
@@ -121,44 +104,38 @@ public class TestMainClientExec {
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mainClientExec = new MainClientExec(requestExecutor, connManager, reuseStrategy,
-            keepAliveStrategy, proxyHttpProcessor, targetAuthStrategy, proxyAuthStrategy, userTokenHandler);
+        mainClientExec = new MainClientExec(reuseStrategy, keepAliveStrategy, proxyHttpProcessor, targetAuthStrategy,
+                proxyAuthStrategy, userTokenHandler);
         target = new HttpHost("foo", 80);
         proxy = new HttpHost("bar", 8888);
-
-        Mockito.when(connManager.lease(
-                Mockito.<HttpRoute>any(), Mockito.any())).thenReturn(connRequest);
-        Mockito.when(connRequest.get(
-                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(endpoint);
     }
 
     @Test
     public void testExecRequestNonPersistentConnection() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectTimeout(123, TimeUnit.MILLISECONDS)
-                .setSocketTimeout(234, TimeUnit.MILLISECONDS)
-                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        response.setEntity(EntityBuilder.create()
+                .setStream(new ByteArrayInputStream(new byte[]{}))
+                .build());
+
+        Mockito.when(endpoint.isConnectionAcquired()).thenReturn(false);
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(request),
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(false);
 
-        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, context, execAware);
-        Mockito.verify(connManager).lease(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(endpoint, TimeValue.ofMillis(123), context);
-        Mockito.verify(endpoint).setSocketTimeout(234);
-        Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context);
-        Mockito.verify(endpoint, Mockito.times(1)).close();
-        Mockito.verify(connManager).release(endpoint, null, TimeValue.ZERO_MILLISECONDS);
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
+        Mockito.verify(endpoint).acquireConnection(route, null, context);
+        Mockito.verify(endpoint).connect(context);
+        Mockito.verify(endpoint).execute(request, context);
+        Mockito.verify(endpoint, Mockito.times(1)).markConnectionNonReusable();
+        Mockito.verify(endpoint, Mockito.never()).releaseConnection();
 
         Assert.assertNull(context.getUserToken());
         Assert.assertNotNull(finalResponse);
@@ -166,238 +143,223 @@ public class TestMainClientExec {
     }
 
     @Test
+    public void testExecRequestNonPersistentConnectionNoResponseEntity() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        response.setEntity(null);
+
+        Mockito.when(endpoint.isConnectionAcquired()).thenReturn(false);
+        Mockito.when(endpoint.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(request),
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(false);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
+
+        Mockito.verify(endpoint).acquireConnection(route, null, context);
+        Mockito.verify(endpoint).connect(context);
+        Mockito.verify(endpoint).execute(request, context);
+        Mockito.verify(endpoint).markConnectionNonReusable();
+        Mockito.verify(endpoint).releaseConnection();
+
+        Assert.assertNotNull(finalResponse);
+        Assert.assertTrue(finalResponse instanceof CloseableHttpResponse);
+    }
+
+    @Test
     public void testExecRequestPersistentConnection() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        // The entity is streaming
+        response.setEntity(EntityBuilder.create()
+                .setStream(new ByteArrayInputStream(new byte[]{}))
+                .build());
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
         Mockito.when(reuseStrategy.keepAlive(
                 Mockito.same(request),
                 Mockito.same(response),
-                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+                Mockito.<HttpClientContext>any())).thenReturn(true);
         Mockito.when(keepAliveStrategy.getKeepAliveDuration(
                 Mockito.same(response),
                 Mockito.<HttpClientContext>any())).thenReturn(TimeValue.ofMillis(678L));
 
-        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, context, execAware);
-        Mockito.verify(connManager).lease(route, null);
-        Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS);
-        Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context);
-        Mockito.verify(connManager).release(endpoint, null, TimeValue.ofMillis(678L));
-        Mockito.verify(endpoint, Mockito.never()).close();
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
+
+        Mockito.verify(endpoint).acquireConnection(route, null, context);
+        Mockito.verify(endpoint).connect(context);
+        Mockito.verify(endpoint).execute(request, context);
+        Mockito.verify(endpoint).markConnectionReusable();
+        Mockito.verify(endpoint, Mockito.never()).releaseConnection();
 
         Assert.assertNotNull(finalResponse);
         Assert.assertTrue(finalResponse instanceof CloseableHttpResponse);
     }
 
     @Test
-    public void testExecRequestPersistentStatefulConnection() throws Exception {
+    public void testExecRequestPersistentConnectionNoResponseEntity() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
         Mockito.when(reuseStrategy.keepAlive(
                 Mockito.same(request),
                 Mockito.same(response),
-                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+                Mockito.<HttpClientContext>any())).thenReturn(true);
         Mockito.when(keepAliveStrategy.getKeepAliveDuration(
                 Mockito.same(response),
-                Mockito.<HttpClientContext>any())).thenReturn(TimeValue.ofMinutes(1));
-        Mockito.when(userTokenHandler.getUserToken(
-                Mockito.same(route),
-                Mockito.<HttpClientContext>any())).thenReturn("this and that");
-
-        mainClientExec.execute(request, context, execAware);
-        Mockito.verify(connManager).lease(route, null);
-        Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS);
-        Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context);
-        Mockito.verify(connManager).release(endpoint, "this and that", TimeValue.ofMinutes(1));
-        Mockito.verify(endpoint, Mockito.never()).close();
+                Mockito.<HttpClientContext>any())).thenReturn(TimeValue.ofMillis(678L));
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
+
+        Mockito.verify(endpoint).acquireConnection(route, null, context);
+        Mockito.verify(endpoint).connect(context);
+        Mockito.verify(endpoint).execute(request, context);
+        Mockito.verify(endpoint).releaseConnection();
 
-        Assert.assertEquals("this and that", context.getUserToken());
+        Assert.assertNotNull(finalResponse);
+        Assert.assertTrue(finalResponse instanceof CloseableHttpResponse);
     }
 
     @Test
     public void testExecRequestConnectionRelease() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
         // The entity is streaming
         response.setEntity(EntityBuilder.create()
                 .setStream(new ByteArrayInputStream(new byte[]{}))
                 .build());
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
         Mockito.when(reuseStrategy.keepAlive(
                 Mockito.same(request),
                 Mockito.same(response),
                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
 
-        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, context, execAware);
-        Mockito.verify(connManager).lease(route, null);
-        Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS);
-        Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context);
-        Mockito.verify(connManager, Mockito.never()).release(
-                Mockito.same(endpoint),
-                Mockito.any(),
-                Mockito.<TimeValue>any());
-        Mockito.verify(endpoint, Mockito.never()).close();
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
+        Mockito.verify(endpoint, Mockito.times(1)).execute(request, context);
+        Mockito.verify(endpoint, Mockito.never()).disconnect();
+        Mockito.verify(endpoint, Mockito.never()).releaseConnection();
 
         Assert.assertNotNull(finalResponse);
         Assert.assertTrue(finalResponse instanceof CloseableHttpResponse);
         finalResponse.close();
 
-        Mockito.verify(connManager, Mockito.times(1)).release(
-                endpoint, null, TimeValue.ZERO_MILLISECONDS);
-        Mockito.verify(endpoint, Mockito.times(1)).close();
+        Mockito.verify(endpoint).disconnect();
+        Mockito.verify(endpoint).discardConnection();
     }
 
-    @Test
-    public void testSocketTimeoutExistingConnection() throws Exception {
+    @Test(expected=InterruptedIOException.class)
+    public void testExecConnectionShutDown() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom().setSocketTimeout(3000, TimeUnit.MILLISECONDS).build();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
-        context.setRequestConfig(config);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
-        Mockito.when(endpoint.isConnected()).thenReturn(true);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
+
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
-                Mockito.<HttpClientContext>any())).thenReturn(response);
+                Mockito.<HttpClientContext>any())).thenThrow(new ConnectionShutdownException());
 
-        mainClientExec.execute(request, context, execAware);
-        Mockito.verify(endpoint).setSocketTimeout(3000);
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        try {
+            mainClientExec.execute(request, scope, null);
+        } catch (Exception ex) {
+            Mockito.verify(endpoint).discardConnection();
+            throw ex;
+        }
     }
 
-    @Test
-    public void testSocketTimeoutReset() throws Exception {
+    @Test(expected=RuntimeException.class)
+    public void testExecRuntimeException() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
-        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
+
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
-                Mockito.<HttpClientContext>any())).thenReturn(response);
-
-        mainClientExec.execute(request, context, execAware);
-        Mockito.verify(endpoint, 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 RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+                Mockito.<HttpClientContext>any())).thenThrow(new RuntimeException("Ka-boom"));
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.FALSE);
-        Mockito.when(execAware.isAborted()).thenReturn(Boolean.TRUE);
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
         try {
-            mainClientExec.execute(request, context, execAware);
-        } catch (final IOException ex) {
-            Mockito.verify(connRequest, Mockito.times(1)).cancel();
+            mainClientExec.execute(request, scope, null);
+        } catch (final Exception ex) {
+            Mockito.verify(endpoint).discardConnection();
             throw ex;
         }
     }
 
-    @Test(expected=RequestAbortedException.class)
-    public void testExecAbortedPriorToConnectionSetup() throws Exception {
+    @Test(expected=HttpException.class)
+    public void testExecHttpException() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.FALSE);
-        Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.TRUE);
+        Mockito.when(endpoint.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientContext>any())).thenThrow(new HttpException("Ka-boom"));
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
         try {
-            mainClientExec.execute(request, context, execAware);
-        } catch (final IOException ex) {
-            Mockito.verify(connRequest, Mockito.times(1)).get(345, TimeUnit.MILLISECONDS);
-            Mockito.verify(execAware, Mockito.times(2)).setCancellable(Mockito.<Cancellable>any());
-            Mockito.verify(connManager, Mockito.never()).connect(
-                    Mockito.same(endpoint),
-                    Mockito.<TimeValue>any(),
-                    Mockito.<HttpContext>any());
+            mainClientExec.execute(request, scope, null);
+        } catch (final Exception ex) {
+            Mockito.verify(endpoint).discardConnection();
             throw ex;
         }
     }
 
-    @Test(expected=RequestAbortedException.class)
-    public void testExecAbortedPriorToRequestExecution() throws Exception {
+    @Test(expected=IOException.class)
+    public void testExecIOException() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectTimeout(567, TimeUnit.MILLISECONDS)
-                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.FALSE);
-        Mockito.when(execAware.isAborted()).thenReturn(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE);
+        Mockito.when(endpoint.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientContext>any())).thenThrow(new IOException("Ka-boom"));
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
         try {
-            mainClientExec.execute(request, context, execAware);
-        } catch (final IOException ex) {
-            Mockito.verify(connRequest, Mockito.times(1)).get(345, TimeUnit.MILLISECONDS);
-            Mockito.verify(connManager, Mockito.times(1)).connect(endpoint, TimeValue.ofMillis(567), context);
-            Mockito.verify(requestExecutor, Mockito.never()).execute(
-                    Mockito.same(request),
-                    Mockito.<HttpClientConnection>any(),
-                    Mockito.<HttpClientContext>any());
+            mainClientExec.execute(request, scope, null);
+        } catch (final Exception ex) {
+            Mockito.verify(endpoint).discardConnection();
             throw ex;
         }
     }
 
-    @Test(expected=RequestAbortedException.class)
-    public void testExecConnectionRequestFailed() throws Exception {
-        final HttpRoute route = new HttpRoute(target);
-        final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
-
-        Mockito.when(connRequest.get(Mockito.anyInt(), Mockito.<TimeUnit>any()))
-                .thenThrow(new ExecutionException("Opppsie", null));
-        mainClientExec.execute(request, context, execAware);
-    }
-
     @Test
     public void testExecRequestRetryOnAuthChallenge() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://foo/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://foo/test");
         final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?");
         response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=test");
         final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
@@ -414,10 +376,11 @@ public class TestMainClientExec {
         credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray()));
         context.setCredentialsProvider(credentialsProvider);
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
         Mockito.when(reuseStrategy.keepAlive(
                 Mockito.same(request),
@@ -427,10 +390,11 @@ public class TestMainClientExec {
                 Mockito.eq(ChallengeType.TARGET),
                 Mockito.<Map<String, AuthChallenge>>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
+        Mockito.when(endpoint.isConnectionReusable()).thenReturn(true);
 
-        final ClassicHttpResponse finalResponse = mainClientExec.execute(
-                request, context, execAware);
-        Mockito.verify(endpoint, Mockito.times(2)).execute(request, requestExecutor, context);
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
+        Mockito.verify(endpoint, Mockito.times(2)).execute(request, context);
         Mockito.verify(instream1).close();
         Mockito.verify(instream2, Mockito.never()).close();
 
@@ -441,7 +405,7 @@ public class TestMainClientExec {
     @Test
     public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exception {
         final HttpRoute route = new HttpRoute(target, proxy);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://foo/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://foo/test");
         final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?");
         response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=test");
         final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
@@ -465,10 +429,11 @@ public class TestMainClientExec {
         credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray()));
         context.setCredentialsProvider(credentialsProvider);
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
         Mockito.when(reuseStrategy.keepAlive(
                 Mockito.same(request),
@@ -480,10 +445,10 @@ public class TestMainClientExec {
                 Mockito.<Map<String, AuthChallenge>>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
 
-        final ClassicHttpResponse finalResponse = mainClientExec.execute(
-                request, context, execAware);
-        Mockito.verify(endpoint, Mockito.times(2)).execute(request, requestExecutor, context);
-        Mockito.verify(endpoint).close();
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
+        Mockito.verify(endpoint, Mockito.times(2)).execute(request, context);
+        Mockito.verify(endpoint).disconnect();
         Mockito.verify(instream2, Mockito.never()).close();
 
         Assert.assertNotNull(finalResponse);
@@ -495,13 +460,11 @@ public class TestMainClientExec {
     public void testExecEntityEnclosingRequest() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final HttpPost post = new HttpPost("http://foo/test");
+        final HttpPost request = new HttpPost("http://foo/test");
         final InputStream instream0 = new ByteArrayInputStream(new byte[] {1, 2, 3});
-        post.setEntity(EntityBuilder.create()
+        request.setEntity(EntityBuilder.create()
                 .setStream(instream0)
                 .build());
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(post, route);
-
         final ClassicHttpResponse response1 = new BasicClassicHttpResponse(401, "Huh?");
         response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=test");
         final InputStream instream1 = new ByteArrayInputStream(new byte[] {1, 2, 3});
@@ -513,10 +476,11 @@ public class TestMainClientExec {
         credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray()));
         context.setCredentialsProvider(credentialsProvider);
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenAnswer(new Answer<HttpResponse>() {
 
             @Override
@@ -538,141 +502,62 @@ public class TestMainClientExec {
                 Mockito.<Map<String, AuthChallenge>>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
 
-        mainClientExec.execute(request, context, execAware);
-    }
-
-    @Test(expected=InterruptedIOException.class)
-    public void testExecConnectionShutDown() throws Exception {
-        final HttpRoute route = new HttpRoute(target);
-        final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
-
-        Mockito.when(endpoint.execute(
-                Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
-                Mockito.<HttpClientContext>any())).thenThrow(new ConnectionShutdownException());
-
-        mainClientExec.execute(request, context, execAware);
-    }
-
-    @Test(expected=RuntimeException.class)
-    public void testExecRuntimeException() throws Exception {
-        final HttpRoute route = new HttpRoute(target);
-        final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
-
-        Mockito.when(endpoint.execute(
-                Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
-                Mockito.<HttpClientContext>any())).thenThrow(new RuntimeException("Ka-boom"));
-
-        try {
-            mainClientExec.execute(request, context, execAware);
-        } catch (final Exception ex) {
-            Mockito.verify(connManager).release(endpoint, null, TimeValue.ZERO_MILLISECONDS);
-
-            throw ex;
-        }
-    }
-
-    @Test(expected=HttpException.class)
-    public void testExecHttpException() throws Exception {
-        final HttpRoute route = new HttpRoute(target);
-        final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
-
-        Mockito.when(endpoint.execute(
-                Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
-                Mockito.<HttpClientContext>any())).thenThrow(new HttpException("Ka-boom"));
-
-        try {
-            mainClientExec.execute(request, context, execAware);
-        } catch (final Exception ex) {
-            Mockito.verify(connManager).release(endpoint, null, TimeValue.ZERO_MILLISECONDS);
-
-            throw ex;
-        }
-    }
-
-    @Test(expected=IOException.class)
-    public void testExecIOException() throws Exception {
-        final HttpRoute route = new HttpRoute(target);
-        final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
-
-        Mockito.when(endpoint.execute(
-                Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
-                Mockito.<HttpClientContext>any())).thenThrow(new IOException("Ka-boom"));
-
-        try {
-            mainClientExec.execute(request, context, execAware);
-        } catch (final Exception ex) {
-            Mockito.verify(connManager).release(endpoint, null, TimeValue.ZERO_MILLISECONDS);
-
-            throw ex;
-        }
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        mainClientExec.execute(request, scope, null);
     }
 
     @Test
     public void testEstablishDirectRoute() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectTimeout(567, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
 
-        mainClientExec.establishRoute(endpoint, route, request, context);
+        mainClientExec.establishRoute(route, request, endpoint, context);
 
-        Mockito.verify(connManager).connect(endpoint, TimeValue.ofMillis(567), context);
+        Mockito.verify(endpoint).connect(context);
+        Mockito.verify(endpoint, Mockito.never()).execute(Mockito.<ClassicHttpRequest>any(), Mockito.<HttpClientContext>any());
     }
 
     @Test
     public void testEstablishRouteDirectProxy() throws Exception {
         final HttpRoute route = new HttpRoute(target, null, proxy, false);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectTimeout(567, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
 
-        mainClientExec.establishRoute(endpoint, route, request, context);
+        mainClientExec.establishRoute(route, request, endpoint, context);
 
-        Mockito.verify(connManager).connect(endpoint, TimeValue.ofMillis(567), context);
+        Mockito.verify(endpoint).connect(context);
+        Mockito.verify(endpoint, Mockito.never()).execute(Mockito.<ClassicHttpRequest>any(), Mockito.<HttpClientContext>any());
     }
 
     @Test
     public void testEstablishRouteViaProxyTunnel() throws Exception {
         final HttpRoute route = new HttpRoute(target, null, proxy, true);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectTimeout(321, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(endpoint.execute(
                 Mockito.<ClassicHttpRequest>any(),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
 
-        mainClientExec.establishRoute(endpoint, route, request, context);
+        mainClientExec.establishRoute(route, request, endpoint, context);
 
-        Mockito.verify(connManager).connect(endpoint, TimeValue.ofMillis(321), context);
+        Mockito.verify(endpoint).connect(context);
         final ArgumentCaptor<ClassicHttpRequest> reqCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class);
         Mockito.verify(endpoint).execute(
                 reqCaptor.capture(),
-                Mockito.same(requestExecutor),
                 Mockito.same(context));
         final HttpRequest connect = reqCaptor.getValue();
         Assert.assertNotNull(connect);
@@ -685,38 +570,41 @@ public class TestMainClientExec {
     public void testEstablishRouteViaProxyTunnelUnexpectedResponse() throws Exception {
         final HttpRoute route = new HttpRoute(target, null, proxy, true);
         final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(101, "Lost");
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(endpoint.execute(
                 Mockito.<ClassicHttpRequest>any(),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
 
-        mainClientExec.establishRoute(endpoint, route, request, context);
+        mainClientExec.establishRoute(route, request, endpoint, context);
     }
 
     @Test(expected = HttpException.class)
     public void testEstablishRouteViaProxyTunnelFailure() throws Exception {
         final HttpRoute route = new HttpRoute(target, null, proxy, true);
         final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(500, "Boom");
         response.setEntity(new StringEntity("Ka-boom"));
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(endpoint.execute(
                 Mockito.<ClassicHttpRequest>any(),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
 
         try {
-            mainClientExec.establishRoute(endpoint, route, request, context);
+            mainClientExec.establishRoute(route, request, endpoint, context);
         } catch (final TunnelRefusedException ex) {
             final ClassicHttpResponse r = ex.getResponse();
             Assert.assertEquals("Ka-boom", EntityUtils.toString(r.getEntity()));
-            Mockito.verify(endpoint).close();
+            Mockito.verify(endpoint).disconnect();
+            Mockito.verify(endpoint).discardConnection();
             throw ex;
         }
     }
@@ -725,11 +613,7 @@ public class TestMainClientExec {
     public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengePersistentConnection() throws Exception {
         final HttpRoute route = new HttpRoute(target, null, proxy, true);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectTimeout(567, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response1 = new BasicClassicHttpResponse(407, "Huh?");
         response1.setHeader(HttpHeaders.PROXY_AUTHENTICATE, "Basic realm=test");
         final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
@@ -742,14 +626,15 @@ public class TestMainClientExec {
         credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials("user", "pass".toCharArray()));
         context.setCredentialsProvider(credentialsProvider);
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(reuseStrategy.keepAlive(
                 Mockito.same(request),
                 Mockito.<HttpResponse>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
         Mockito.when(endpoint.execute(
                 Mockito.<ClassicHttpRequest>any(),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
 
         Mockito.when(proxyAuthStrategy.select(
@@ -757,9 +642,9 @@ public class TestMainClientExec {
                 Mockito.<Map<String, AuthChallenge>>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
 
-        mainClientExec.establishRoute(endpoint, route, request, context);
+        mainClientExec.establishRoute(route, request, endpoint, context);
 
-        Mockito.verify(connManager).connect(endpoint, TimeValue.ofMillis(567), context);
+        Mockito.verify(endpoint).connect(context);
         Mockito.verify(instream1).close();
     }
 
@@ -767,11 +652,7 @@ public class TestMainClientExec {
     public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengeNonPersistentConnection() throws Exception {
         final HttpRoute route = new HttpRoute(target, null, proxy, true);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectTimeout(567, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response1 = new BasicClassicHttpResponse(407, "Huh?");
         response1.setHeader(HttpHeaders.PROXY_AUTHENTICATE, "Basic realm=test");
         final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
@@ -784,14 +665,15 @@ public class TestMainClientExec {
         credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials("user", "pass".toCharArray()));
         context.setCredentialsProvider(credentialsProvider);
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
         Mockito.when(reuseStrategy.keepAlive(
                 Mockito.same(request),
                 Mockito.<HttpResponse>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
         Mockito.when(endpoint.execute(
                 Mockito.<ClassicHttpRequest>any(),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
 
         Mockito.when(proxyAuthStrategy.select(
@@ -799,11 +681,11 @@ public class TestMainClientExec {
                 Mockito.<Map<String, AuthChallenge>>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
 
-        mainClientExec.establishRoute(endpoint, route, request, context);
+        mainClientExec.establishRoute(route, request, endpoint, context);
 
-        Mockito.verify(connManager).connect(endpoint, TimeValue.ofMillis(567), context);
+        Mockito.verify(endpoint).connect(context);
         Mockito.verify(instream1, Mockito.never()).close();
-        Mockito.verify(endpoint).close();
+        Mockito.verify(endpoint).disconnect();
     }
 
     @Test(expected = HttpException.class)
@@ -813,11 +695,44 @@ public class TestMainClientExec {
         final HttpRoute route = new HttpRoute(target, null, new HttpHost[] {proxy1, proxy2},
                 true, RouteInfo.TunnelType.TUNNELLED, RouteInfo.LayerType.LAYERED);
         final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
+
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(endpoint).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(endpoint.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
+
+        mainClientExec.establishRoute(route, request, endpoint, context);
+    }
+
+    static class ConnectionState {
+
+        private boolean connected;
+
+        public Answer connectAnswer() {
+
+            return new Answer() {
+
+                @Override
+                public Object answer(final InvocationOnMock invocationOnMock) throws Throwable {
+                    connected = true;
+                    return null;
+                }
+
+            };
+        }
+
+        public Answer<Boolean> isConnectedAnswer() {
+
+            return new Answer<Boolean>() {
+
+                @Override
+                public Boolean answer(final InvocationOnMock invocationOnMock) throws Throwable {
+                    return connected;
+                }
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+            };
 
-        mainClientExec.establishRoute(endpoint, route, request, context);
+        };
     }
 
 }

Modified: httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMinimalClientExec.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMinimalClientExec.java?rev=1793308&r1=1793307&r2=1793308&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMinimalClientExec.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5/src/test/java/org/apache/hc/client5/http/impl/sync/TestMinimalClientExec.java Mon May  1 11:11:09 2017
@@ -29,33 +29,25 @@ package org.apache.hc.client5.http.impl.
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InterruptedIOException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
 
 import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
 import org.apache.hc.client5.http.HttpRoute;
-import org.apache.hc.client5.http.config.RequestConfig;
 import org.apache.hc.client5.http.entity.EntityBuilder;
 import org.apache.hc.client5.http.impl.ConnectionShutdownException;
-import org.apache.hc.client5.http.io.ConnectionEndpoint;
-import org.apache.hc.client5.http.io.HttpClientConnectionManager;
-import org.apache.hc.client5.http.io.LeaseRequest;
 import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.client5.http.sync.methods.HttpExecutionAware;
+import org.apache.hc.client5.http.sync.ExecChain;
+import org.apache.hc.client5.http.sync.ExecRuntime;
 import org.apache.hc.client5.http.sync.methods.HttpGet;
-import org.apache.hc.core5.concurrent.Cancellable;
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.ConnectionReuseStrategy;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpHost;
-import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
 import org.apache.hc.core5.util.TimeValue;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -64,19 +56,11 @@ import org.mockito.MockitoAnnotations;
 public class TestMinimalClientExec {
 
     @Mock
-    private HttpRequestExecutor requestExecutor;
-    @Mock
-    private HttpClientConnectionManager connManager;
-    @Mock
     private ConnectionReuseStrategy reuseStrategy;
     @Mock
     private ConnectionKeepAliveStrategy keepAliveStrategy;
     @Mock
-    private HttpExecutionAware execAware;
-    @Mock
-    private LeaseRequest connRequest;
-    @Mock
-    private ConnectionEndpoint endpoint;
+    private ExecRuntime endpoint;
 
     private MinimalClientExec minimalClientExec;
     private HttpHost target;
@@ -84,221 +68,206 @@ public class TestMinimalClientExec {
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
-        minimalClientExec = new MinimalClientExec(
-                requestExecutor, connManager, reuseStrategy, keepAliveStrategy);
+        minimalClientExec = new MinimalClientExec(reuseStrategy, keepAliveStrategy);
         target = new HttpHost("foo", 80);
-
-        Mockito.when(connManager.lease(
-                Mockito.<HttpRoute>any(), Mockito.any())).thenReturn(connRequest);
-        Mockito.when(connRequest.get(
-                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(endpoint);
     }
 
     @Test
     public void testExecRequestNonPersistentConnection() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectTimeout(123, TimeUnit.MILLISECONDS)
-                .setSocketTimeout(234, TimeUnit.MILLISECONDS)
-                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        response.setEntity(EntityBuilder.create()
+                .setStream(new ByteArrayInputStream(new byte[]{}))
+                .build());
+
+        Mockito.when(endpoint.isConnectionAcquired()).thenReturn(false);
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(request),
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(false);
 
-        final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, context, execAware);
-        Mockito.verify(connManager).lease(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(endpoint, TimeValue.ofMillis(123), context);
-        Mockito.verify(endpoint).setSocketTimeout(234);
-        Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context);
-        Mockito.verify(endpoint, Mockito.times(1)).close();
-        Mockito.verify(connManager).release(endpoint, null, TimeValue.ZERO_MILLISECONDS);
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, scope, null);
+        Mockito.verify(endpoint).acquireConnection(route, null, context);
+        Mockito.verify(endpoint).connect(context);
+        Mockito.verify(endpoint).execute(request, context);
+        Mockito.verify(endpoint).markConnectionNonReusable();
+        Mockito.verify(endpoint, Mockito.never()).releaseConnection();
 
         Assert.assertNotNull(finalResponse);
         Assert.assertTrue(finalResponse instanceof CloseableHttpResponse);
     }
 
     @Test
-    public void testExecRequestPersistentConnection() throws Exception {
+    public void testExecRequestNonPersistentConnectionNoResponseEntity() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectTimeout(123, TimeUnit.MILLISECONDS)
-                .setSocketTimeout(234, TimeUnit.MILLISECONDS)
-                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        response.setEntity(null);
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        Mockito.when(endpoint.isConnectionAcquired()).thenReturn(false);
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
         Mockito.when(reuseStrategy.keepAlive(
                 Mockito.same(request),
                 Mockito.same(response),
-                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
-        Mockito.when(keepAliveStrategy.getKeepAliveDuration(
-                Mockito.same(response),
-                Mockito.<HttpClientContext>any())).thenReturn(TimeValue.ofMillis(678L));
+                Mockito.<HttpClientContext>any())).thenReturn(false);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, scope, null);
 
-        final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, context, execAware);
-        Mockito.verify(connManager).lease(route, null);
-        Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS);
-        Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context);
-        Mockito.verify(connManager).release(endpoint, null, TimeValue.ofMillis(678L));
-        Mockito.verify(endpoint, Mockito.never()).close();
+        Mockito.verify(endpoint).acquireConnection(route, null, context);
+        Mockito.verify(endpoint).connect(context);
+        Mockito.verify(endpoint).execute(request, context);
+        Mockito.verify(endpoint).markConnectionNonReusable();
+        Mockito.verify(endpoint).releaseConnection();
 
         Assert.assertNotNull(finalResponse);
         Assert.assertTrue(finalResponse instanceof CloseableHttpResponse);
     }
 
     @Test
-    public void testExecRequestConnectionRelease() throws Exception {
+    public void testExecRequestPersistentConnection() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom()
-                .setConnectTimeout(123, TimeUnit.MILLISECONDS)
-                .setSocketTimeout(234, TimeUnit.MILLISECONDS)
-                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
-                .build();
-        context.setRequestConfig(config);
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
         // The entity is streaming
         response.setEntity(EntityBuilder.create()
                 .setStream(new ByteArrayInputStream(new byte[]{}))
                 .build());
 
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
+        Mockito.when(endpoint.isConnected()).thenReturn(false);
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
         Mockito.when(reuseStrategy.keepAlive(
                 Mockito.same(request),
                 Mockito.same(response),
-                Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
+                Mockito.<HttpClientContext>any())).thenReturn(true);
+        Mockito.when(keepAliveStrategy.getKeepAliveDuration(
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(TimeValue.ofMillis(678L));
 
-        final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, context, execAware);
-        Mockito.verify(connManager).lease(route, null);
-        Mockito.verify(connRequest).get(345, TimeUnit.MILLISECONDS);
-        Mockito.verify(endpoint, Mockito.times(1)).execute(request, requestExecutor, context);
-        Mockito.verify(connManager, Mockito.never()).release(
-                Mockito.same(endpoint),
-                Mockito.any(),
-                Mockito.<TimeValue>any());
-        Mockito.verify(endpoint, Mockito.never()).close();
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, scope, null);
+
+        Mockito.verify(endpoint).acquireConnection(route, null, context);
+        Mockito.verify(endpoint).connect(context);
+        Mockito.verify(endpoint).execute(request, context);
+        Mockito.verify(endpoint).markConnectionReusable();
+        Mockito.verify(endpoint, Mockito.never()).releaseConnection();
 
         Assert.assertNotNull(finalResponse);
         Assert.assertTrue(finalResponse instanceof CloseableHttpResponse);
-        finalResponse.close();
-
-        Mockito.verify(connManager, Mockito.times(1)).release(
-                endpoint, null, TimeValue.ZERO_MILLISECONDS);
-        Mockito.verify(endpoint, Mockito.times(1)).close();
     }
 
     @Test
-    public void testSocketTimeoutExistingConnection() throws Exception {
+    public void testExecRequestPersistentConnectionNoResponseEntity() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RequestConfig config = RequestConfig.custom().setSocketTimeout(3000, TimeUnit.MILLISECONDS).build();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
-        context.setRequestConfig(config);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
-        Mockito.when(endpoint.isConnected()).thenReturn(true);
+
+        Mockito.when(endpoint.isConnected()).thenReturn(false);
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(request),
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(true);
+        Mockito.when(keepAliveStrategy.getKeepAliveDuration(
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(TimeValue.ofMillis(678L));
 
-        minimalClientExec.execute(request, context, execAware);
-        Mockito.verify(endpoint).setSocketTimeout(3000);
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, scope, null);
+
+        Mockito.verify(endpoint).acquireConnection(route, null, context);
+        Mockito.verify(endpoint).connect(context);
+        Mockito.verify(endpoint).execute(request, context);
+        Mockito.verify(endpoint).releaseConnection();
+
+        Assert.assertNotNull(finalResponse);
+        Assert.assertTrue(finalResponse instanceof CloseableHttpResponse);
     }
 
     @Test
-    public void testSocketTimeoutReset() throws Exception {
+    public void testExecRequestConnectionRelease() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
         final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        // The entity is streaming
+        response.setEntity(EntityBuilder.create()
+                .setStream(new ByteArrayInputStream(new byte[]{}))
+                .build());
+
         Mockito.when(endpoint.isConnected()).thenReturn(Boolean.TRUE);
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenReturn(response);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(request),
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
 
-        minimalClientExec.execute(request, context, execAware);
-        Mockito.verify(endpoint, 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 RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
-
-        Mockito.when(endpoint.isConnected()).thenReturn(Boolean.FALSE);
-        Mockito.when(execAware.isAborted()).thenReturn(Boolean.TRUE);
-        try {
-            minimalClientExec.execute(request, context, execAware);
-        } catch (final IOException ex) {
-            Mockito.verify(connRequest, Mockito.times(1)).cancel();
-            throw ex;
-        }
-    }
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = minimalClientExec.execute(request, scope, null);
+        Mockito.verify(endpoint, Mockito.times(1)).execute(request, context);
+        Mockito.verify(endpoint, Mockito.never()).disconnect();
+        Mockito.verify(endpoint, Mockito.never()).releaseConnection();
 
-    @Test(expected=RequestAbortedException.class)
-    public void testExecConnectionRequestFailed() throws Exception {
-        final HttpRoute route = new HttpRoute(target);
-        final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        Assert.assertNotNull(finalResponse);
+        Assert.assertTrue(finalResponse instanceof CloseableHttpResponse);
+        finalResponse.close();
 
-        Mockito.when(connRequest.get(Mockito.anyInt(), Mockito.<TimeUnit>any()))
-                .thenThrow(new ExecutionException("Opppsie", null));
-        minimalClientExec.execute(request, context, execAware);
+        Mockito.verify(endpoint).disconnect();
+        Mockito.verify(endpoint).discardConnection();
     }
 
     @Test(expected=InterruptedIOException.class)
     public void testExecConnectionShutDown() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
 
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenThrow(new ConnectionShutdownException());
 
-        minimalClientExec.execute(request, context, execAware);
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        try {
+            minimalClientExec.execute(request, scope, null);
+        } catch (Exception ex) {
+            Mockito.verify(endpoint).discardConnection();
+            throw ex;
+        }
     }
 
     @Test(expected=RuntimeException.class)
     public void testExecRuntimeException() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
 
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenThrow(new RuntimeException("Ka-boom"));
 
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
         try {
-            minimalClientExec.execute(request, context, execAware);
+            minimalClientExec.execute(request, scope, null);
         } catch (final Exception ex) {
-            Mockito.verify(connManager).release(endpoint, null, TimeValue.ZERO_MILLISECONDS);
+            Mockito.verify(endpoint).discardConnection();
             throw ex;
         }
     }
@@ -307,17 +276,17 @@ public class TestMinimalClientExec {
     public void testExecHttpException() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
 
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenThrow(new HttpException("Ka-boom"));
 
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
         try {
-            minimalClientExec.execute(request, context, execAware);
+            minimalClientExec.execute(request, scope, null);
         } catch (final Exception ex) {
-            Mockito.verify(connManager).release(endpoint, null, TimeValue.ZERO_MILLISECONDS);
+            Mockito.verify(endpoint).discardConnection();
             throw ex;
         }
     }
@@ -326,38 +295,19 @@ public class TestMinimalClientExec {
     public void testExecIOException() throws Exception {
         final HttpRoute route = new HttpRoute(target);
         final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
 
         Mockito.when(endpoint.execute(
                 Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
                 Mockito.<HttpClientContext>any())).thenThrow(new IOException("Ka-boom"));
 
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
         try {
-            minimalClientExec.execute(request, context, execAware);
+            minimalClientExec.execute(request, scope, null);
         } catch (final Exception ex) {
-            Mockito.verify(connManager).release(endpoint, null, TimeValue.ZERO_MILLISECONDS);
+            Mockito.verify(endpoint).discardConnection();
             throw ex;
         }
     }
 
-    @Test
-    public void absoluteUriIsRewrittenToRelativeBeforeBeingPassedInRequestLine() throws Exception {
-        final HttpRoute route = new HttpRoute(target);
-        final HttpClientContext context = new HttpClientContext();
-        final RoutedHttpRequest request = RoutedHttpRequest.adapt(new HttpGet("http://bar/test"), route);
-
-        final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
-        Mockito.when(endpoint.execute(
-                Mockito.same(request),
-                Mockito.<HttpRequestExecutor>any(),
-                Mockito.<HttpClientContext>any())).thenReturn(response);
-
-        minimalClientExec.execute(request, context, execAware);
-
-        final ArgumentCaptor<ClassicHttpRequest> reqCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class);
-        Mockito.verify(endpoint).execute(reqCaptor.capture(), Mockito.<HttpRequestExecutor>any(), Mockito.<HttpClientContext>any());
-
-        Assert.assertEquals("/test", reqCaptor.getValue().getRequestUri());
-    }
 }