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/09/02 15:28:05 UTC

[05/17] httpcomponents-client git commit: Moved classes and renamed packages (no functional changes)

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestCloseableHttpClient.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestCloseableHttpClient.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestCloseableHttpClient.java
new file mode 100644
index 0000000..f75a394
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestCloseableHttpClient.java
@@ -0,0 +1,182 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.impl.classic;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ *  Simple tests for {@link CloseableHttpClient}.
+ */
+@SuppressWarnings({"boxing","static-access"}) // test code
+public class TestCloseableHttpClient {
+
+    static abstract class NoopCloseableHttpClient extends CloseableHttpClient {
+
+        @Override
+        protected CloseableHttpResponse doExecute(
+                final HttpHost target,
+                final ClassicHttpRequest request,
+                final HttpContext context) throws IOException {
+            return null;
+        }
+
+    }
+
+    private NoopCloseableHttpClient client;
+    private InputStream content;
+    private HttpEntity entity;
+    private ClassicHttpResponse originResponse;
+    private CloseableHttpResponse response;
+
+    @Before
+    public void setup() throws Exception {
+        content = Mockito.mock(InputStream.class);
+        entity = Mockito.mock(HttpEntity.class);
+        originResponse = Mockito.mock(ClassicHttpResponse.class);
+        response = CloseableHttpResponse.adapt(originResponse);
+        Mockito.when(entity.getContent()).thenReturn(content);
+        Mockito.when(entity.isStreaming()).thenReturn(Boolean.TRUE);
+        Mockito.when(response.getEntity()).thenReturn(entity);
+        client = Mockito.mock(NoopCloseableHttpClient.class, Mockito.CALLS_REAL_METHODS);
+    }
+
+    @Test
+    public void testExecuteRequestAbsoluteURI() throws Exception {
+        final HttpGet httpget = new HttpGet("https://somehost:444/stuff");
+        client.execute(httpget);
+
+        Mockito.verify(client).doExecute(
+                Mockito.eq(new HttpHost("somehost", 444, "https")),
+                Mockito.same(httpget),
+                (HttpContext) Mockito.isNull());
+    }
+
+    @Test
+    public void testExecuteRequestRelativeURI() throws Exception {
+        final HttpGet httpget = new HttpGet("/stuff");
+        client.execute(httpget);
+
+        Mockito.verify(client).doExecute(
+                (HttpHost) Mockito.isNull(),
+                Mockito.same(httpget),
+                (HttpContext) Mockito.isNull());
+    }
+
+    @Test
+    public void testExecuteRequest() throws Exception {
+        final HttpGet httpget = new HttpGet("https://somehost:444/stuff");
+
+        Mockito.when(client.doExecute(
+                new HttpHost("somehost", 444, "https"), httpget, null)).thenReturn(response);
+
+        final CloseableHttpResponse result = client.execute(httpget);
+        Assert.assertSame(response, result);
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void testExecuteRequestHandleResponse() throws Exception {
+        final HttpGet httpget = new HttpGet("https://somehost:444/stuff");
+
+        Mockito.when(client.doExecute(
+                new HttpHost("somehost", 444, "https"), httpget, null)).thenReturn(response);
+
+        final HttpClientResponseHandler<HttpResponse> handler = Mockito.mock(HttpClientResponseHandler.class);
+
+        client.execute(httpget, handler);
+
+        Mockito.verify(client).doExecute(
+                Mockito.eq(new HttpHost("somehost", 444, "https")),
+                Mockito.same(httpget),
+                (HttpContext) Mockito.isNull());
+        Mockito.verify(handler).handleResponse(response);
+        Mockito.verify(content).close();
+    }
+
+    @Test(expected=IOException.class)
+    @SuppressWarnings("unchecked")
+    public void testExecuteRequestHandleResponseIOException() throws Exception {
+        final HttpGet httpget = new HttpGet("https://somehost:444/stuff");
+
+        Mockito.when(client.doExecute(
+                new HttpHost("somehost", 444, "https"), httpget, null)).thenReturn(response);
+
+        final HttpClientResponseHandler<HttpResponse> handler = Mockito.mock(HttpClientResponseHandler.class);
+
+        Mockito.when(handler.handleResponse(response)).thenThrow(new IOException());
+
+        try {
+            client.execute(httpget, handler);
+        } catch (final IOException ex) {
+            Mockito.verify(client).doExecute(
+                    Mockito.eq(new HttpHost("somehost", 444, "https")),
+                    Mockito.same(httpget),
+                    (HttpContext) Mockito.isNull());
+            Mockito.verify(originResponse).close();
+            throw ex;
+        }
+    }
+
+    @Test(expected=RuntimeException.class)
+    @SuppressWarnings("unchecked")
+    public void testExecuteRequestHandleResponseHttpException() throws Exception {
+        final HttpGet httpget = new HttpGet("https://somehost:444/stuff");
+
+        Mockito.when(client.doExecute(
+                new HttpHost("somehost", 444, "https"), httpget, null)).thenReturn(response);
+
+        final HttpClientResponseHandler<HttpResponse> handler = Mockito.mock(HttpClientResponseHandler.class);
+
+        Mockito.when(handler.handleResponse(response)).thenThrow(new RuntimeException());
+
+        try {
+            client.execute(httpget, handler);
+        } catch (final RuntimeException ex) {
+            Mockito.verify(client).doExecute(
+                    Mockito.eq(new HttpHost("somehost", 444, "https")),
+                    Mockito.same(httpget),
+                    (HttpContext) Mockito.isNull());
+            Mockito.verify(response).close();
+            throw ex;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestConnectExec.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestConnectExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestConnectExec.java
new file mode 100644
index 0000000..4f5d8be
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestConnectExec.java
@@ -0,0 +1,359 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.impl.classic;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.hc.client5.http.AuthenticationStrategy;
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.RouteInfo;
+import org.apache.hc.client5.http.auth.AuthChallenge;
+import org.apache.hc.client5.http.auth.AuthScheme;
+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.classic.ExecChain;
+import org.apache.hc.client5.http.classic.ExecRuntime;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.entity.EntityBuilder;
+import org.apache.hc.client5.http.impl.TunnelRefusedException;
+import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
+import org.apache.hc.client5.http.impl.auth.BasicScheme;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+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.HttpHeaders;
+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.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
+import org.apache.hc.core5.http.protocol.HttpProcessor;
+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;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+@SuppressWarnings({"boxing","static-access"}) // test code
+public class TestConnectExec {
+
+    @Mock
+    private ConnectionReuseStrategy reuseStrategy;
+    @Mock
+    private HttpProcessor proxyHttpProcessor;
+    @Mock
+    private AuthenticationStrategy proxyAuthStrategy;
+    @Mock
+    private ExecRuntime execRuntime;
+    @Mock
+    private ExecChain execChain;
+
+    private ConnectExec exec;
+    private HttpHost target;
+    private HttpHost proxy;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        exec = new ConnectExec(reuseStrategy, proxyHttpProcessor, proxyAuthStrategy);
+        target = new HttpHost("foo", 80);
+        proxy = new HttpHost("bar", 8888);
+    }
+
+    @Test
+    public void testExecAcquireConnection() 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(EntityBuilder.create()
+                .setStream(new ByteArrayInputStream(new byte[]{}))
+                .build());
+        context.setUserToken("Blah");
+
+        Mockito.when(execRuntime.isConnectionAcquired()).thenReturn(false);
+        Mockito.when(execRuntime.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, execRuntime, context);
+        exec.execute(request, scope, execChain);
+        Mockito.verify(execRuntime).acquireConnection(route, "Blah", context);
+        Mockito.verify(execRuntime).connect(context);
+    }
+
+    @Test
+    public void testEstablishDirectRoute() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
+
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(execRuntime.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        exec.execute(request, scope, execChain);
+
+        Mockito.verify(execRuntime).connect(context);
+        Mockito.verify(execRuntime, 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 ClassicHttpRequest request = new HttpGet("http://bar/test");
+
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(execRuntime.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        exec.execute(request, scope, execChain);
+
+        Mockito.verify(execRuntime).connect(context);
+        Mockito.verify(execRuntime, 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 ClassicHttpRequest request = new HttpGet("http://bar/test");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(execRuntime.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
+        Mockito.when(execRuntime.execute(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        exec.execute(request, scope, execChain);
+
+        Mockito.verify(execRuntime).connect(context);
+        final ArgumentCaptor<ClassicHttpRequest> reqCaptor = ArgumentCaptor.forClass(ClassicHttpRequest.class);
+        Mockito.verify(execRuntime).execute(
+                reqCaptor.capture(),
+                Mockito.same(context));
+        final HttpRequest connect = reqCaptor.getValue();
+        Assert.assertNotNull(connect);
+        Assert.assertEquals("CONNECT", connect.getMethod());
+        Assert.assertEquals(HttpVersion.HTTP_1_1, connect.getVersion());
+        Assert.assertEquals("foo:80", connect.getRequestUri());
+    }
+
+    @Test(expected = HttpException.class)
+    public void testEstablishRouteViaProxyTunnelUnexpectedResponse() throws Exception {
+        final HttpRoute route = new HttpRoute(target, null, proxy, true);
+        final HttpClientContext context = new HttpClientContext();
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(101, "Lost");
+
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(execRuntime.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
+        Mockito.when(execRuntime.execute(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        exec.execute(request, scope, execChain);
+    }
+
+    @Test(expected = HttpException.class)
+    public void testEstablishRouteViaProxyTunnelFailure() throws Exception {
+        final HttpRoute route = new HttpRoute(target, null, proxy, true);
+        final HttpClientContext context = new HttpClientContext();
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(500, "Boom");
+        response.setEntity(new StringEntity("Ka-boom"));
+
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(execRuntime.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
+        Mockito.when(execRuntime.execute(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        try {
+            exec.execute(request, scope, execChain);
+        } catch (final TunnelRefusedException ex) {
+            Assert.assertEquals("Ka-boom", ex.getResponseMessage());
+            Mockito.verify(execRuntime).disconnect();
+            Mockito.verify(execRuntime).discardConnection();
+            throw ex;
+        }
+    }
+
+    @Test
+    public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengePersistentConnection() throws Exception {
+        final HttpRoute route = new HttpRoute(target, null, proxy, true);
+        final HttpClientContext context = new HttpClientContext();
+        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}));
+        response1.setEntity(EntityBuilder.create()
+                .setStream(instream1)
+                .build());
+        final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK");
+
+        final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials("user", "pass".toCharArray()));
+        context.setCredentialsProvider(credentialsProvider);
+
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(execRuntime.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(request),
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(execRuntime.execute(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
+
+        Mockito.when(proxyAuthStrategy.select(
+                Mockito.eq(ChallengeType.PROXY),
+                Mockito.<Map<String, AuthChallenge>>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        exec.execute(request, scope, execChain);
+
+        Mockito.verify(execRuntime).connect(context);
+        Mockito.verify(instream1).close();
+    }
+
+    @Test
+    public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengeNonPersistentConnection() throws Exception {
+        final HttpRoute route = new HttpRoute(target, null, proxy, true);
+        final HttpClientContext context = new HttpClientContext();
+        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}));
+        response1.setEntity(EntityBuilder.create()
+                .setStream(instream1)
+                .build());
+        final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK");
+
+        final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials("user", "pass".toCharArray()));
+        context.setCredentialsProvider(credentialsProvider);
+
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(execRuntime.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(request),
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
+        Mockito.when(execRuntime.execute(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(response1, response2);
+
+        Mockito.when(proxyAuthStrategy.select(
+                Mockito.eq(ChallengeType.PROXY),
+                Mockito.<Map<String, AuthChallenge>>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        exec.execute(request, scope, execChain);
+
+        Mockito.verify(execRuntime).connect(context);
+        Mockito.verify(instream1, Mockito.never()).close();
+        Mockito.verify(execRuntime).disconnect();
+    }
+
+    @Test(expected = HttpException.class)
+    public void testEstablishRouteViaProxyTunnelMultipleHops() throws Exception {
+        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 ClassicHttpRequest request = new HttpGet("http://bar/test");
+
+        final ConnectionState connectionState = new ConnectionState();
+        Mockito.doAnswer(connectionState.connectAnswer()).when(execRuntime).connect(Mockito.<HttpClientContext>any());
+        Mockito.when(execRuntime.isConnected()).thenAnswer(connectionState.isConnectedAnswer());
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        exec.execute(request, scope, execChain);
+    }
+
+    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;
+                }
+
+            };
+
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestContentCompressionExec.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestContentCompressionExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestContentCompressionExec.java
new file mode 100644
index 0000000..950b6fb
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestContentCompressionExec.java
@@ -0,0 +1,229 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.impl.classic;
+
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.StandardMethods;
+import org.apache.hc.client5.http.classic.ExecChain;
+import org.apache.hc.client5.http.classic.ExecRuntime;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.entity.DecompressingEntity;
+import org.apache.hc.client5.http.entity.GzipDecompressingEntity;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
+import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class TestContentCompressionExec {
+
+    @Mock
+    private ExecRuntime execRuntime;
+    @Mock
+    private ExecChain execChain;
+    @Mock
+    private ClassicHttpRequest originaRequest;
+
+    private HttpClientContext context;
+    private HttpHost host;
+    private ExecChain.Scope scope;
+    private ContentCompressionExec impl;
+
+    @Before
+    public void setup() {
+        host = new HttpHost("somehost", 80);
+        context = HttpClientContext.create();
+        scope = new ExecChain.Scope(new HttpRoute(host), originaRequest, execRuntime, context);
+        impl = new ContentCompressionExec();
+    }
+
+
+    @Test
+    public void testContentEncodingNoEntity() throws Exception {
+        final ClassicHttpRequest request = new BasicClassicHttpRequest(StandardMethods.GET.name(), host, "/");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+
+        Mockito.when(execChain.proceed(request, scope)).thenReturn(response);
+
+        impl.execute(request, scope, execChain);
+
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNull(entity);
+    }
+
+    @Test
+    public void testNoContentEncoding() throws Exception {
+        final ClassicHttpRequest request = new BasicClassicHttpRequest(StandardMethods.GET.name(), host, "/");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        final StringEntity original = new StringEntity("plain stuff");
+        response.setEntity(original);
+
+        Mockito.when(execChain.proceed(request, scope)).thenReturn(response);
+
+        impl.execute(request, scope, execChain);
+
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNotNull(entity);
+        Assert.assertTrue(entity instanceof StringEntity);
+    }
+
+    @Test
+    public void testGzipContentEncoding() throws Exception {
+        final ClassicHttpRequest request = new BasicClassicHttpRequest(StandardMethods.GET.name(), host, "/");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        final StringEntity original = new StringEntity("encoded stuff");
+        original.setContentEncoding("GZip");
+        response.setEntity(original);
+
+        Mockito.when(execChain.proceed(request, scope)).thenReturn(response);
+
+        impl.execute(request, scope, execChain);
+
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNotNull(entity);
+        Assert.assertTrue(entity instanceof DecompressingEntity);
+    }
+
+    @Test
+    public void testGzipContentEncodingZeroLength() throws Exception {
+        final ClassicHttpRequest request = new BasicClassicHttpRequest(StandardMethods.GET.name(), host, "/");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        final StringEntity original = new StringEntity("");
+        original.setContentEncoding("GZip");
+        response.setEntity(original);
+
+        Mockito.when(execChain.proceed(request, scope)).thenReturn(response);
+
+        impl.execute(request, scope, execChain);
+
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNotNull(entity);
+        Assert.assertTrue(entity instanceof StringEntity);
+    }
+
+    @Test
+    public void testXGzipContentEncoding() throws Exception {
+        final ClassicHttpRequest request = new BasicClassicHttpRequest(StandardMethods.GET.name(), host, "/");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        final StringEntity original = new StringEntity("encoded stuff");
+        original.setContentEncoding("x-gzip");
+        response.setEntity(original);
+
+        Mockito.when(execChain.proceed(request, scope)).thenReturn(response);
+
+        impl.execute(request, scope, execChain);
+
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNotNull(entity);
+        Assert.assertTrue(entity instanceof DecompressingEntity);
+    }
+
+    @Test
+    public void testDeflateContentEncoding() throws Exception {
+        final ClassicHttpRequest request = new BasicClassicHttpRequest(StandardMethods.GET.name(), host, "/");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        final StringEntity original = new StringEntity("encoded stuff");
+        original.setContentEncoding("deFlaTe");
+        response.setEntity(original);
+
+        Mockito.when(execChain.proceed(request, scope)).thenReturn(response);
+
+        impl.execute(request, scope, execChain);
+
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNotNull(entity);
+        Assert.assertTrue(entity instanceof DecompressingEntity);
+    }
+
+    @Test
+    public void testIdentityContentEncoding() throws Exception {
+        final ClassicHttpRequest request = new BasicClassicHttpRequest(StandardMethods.GET.name(), host, "/");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        final StringEntity original = new StringEntity("encoded stuff");
+        original.setContentEncoding("identity");
+        response.setEntity(original);
+
+        Mockito.when(execChain.proceed(request, scope)).thenReturn(response);
+
+        impl.execute(request, scope, execChain);
+
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNotNull(entity);
+        Assert.assertTrue(entity instanceof StringEntity);
+    }
+
+    @Test(expected=HttpException.class)
+    public void testUnknownContentEncoding() throws Exception {
+        final ClassicHttpRequest request = new BasicClassicHttpRequest(StandardMethods.GET.name(), host, "/");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        final StringEntity original = new StringEntity("encoded stuff");
+        original.setContentEncoding("whatever");
+        response.setEntity(original);
+
+        impl = new ContentCompressionExec(false);
+
+        Mockito.when(execChain.proceed(request, scope)).thenReturn(response);
+
+        impl.execute(request, scope, execChain);
+    }
+
+    @Test
+    public void testContentEncodingRequestParameter() throws Exception {
+        final ClassicHttpRequest request = new BasicClassicHttpRequest(StandardMethods.GET.name(), host, "/");
+        final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
+        final StringEntity original = new StringEntity("encoded stuff");
+        original.setContentEncoding("GZip");
+        response.setEntity(original);
+
+        final RequestConfig config = RequestConfig.custom()
+                .setContentCompressionEnabled(false)
+                .build();
+
+        context.setAttribute(HttpClientContext.REQUEST_CONFIG, config);
+
+        Mockito.when(execChain.proceed(request, scope)).thenReturn(response);
+
+        impl.execute(request, scope, execChain);
+
+        final HttpEntity entity = response.getEntity();
+        Assert.assertNotNull(entity);
+        Assert.assertFalse(entity instanceof GzipDecompressingEntity);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestCookieIdentityComparator.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestCookieIdentityComparator.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestCookieIdentityComparator.java
new file mode 100644
index 0000000..ceeff4f
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestCookieIdentityComparator.java
@@ -0,0 +1,139 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.impl.classic;
+
+import org.apache.hc.client5.http.cookie.CookieIdentityComparator;
+import org.apache.hc.client5.http.impl.cookie.BasicClientCookie;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Simple tests for {@link CookieIdentityComparator}.
+ */
+public class TestCookieIdentityComparator {
+
+    @Test
+    public void testCookieIdentityComparasionByName() {
+        final CookieIdentityComparator comparator = new CookieIdentityComparator();
+        final BasicClientCookie c1 = new BasicClientCookie("name", "value1");
+        final BasicClientCookie c2 = new BasicClientCookie("name", "value2");
+        Assert.assertTrue(comparator.compare(c1, c2) == 0);
+
+        final BasicClientCookie c3 = new BasicClientCookie("name1", "value");
+        final BasicClientCookie c4 = new BasicClientCookie("name2", "value");
+        Assert.assertFalse(comparator.compare(c3, c4) == 0);
+    }
+
+    @Test
+    public void testCookieIdentityComparasionByNameAndDomain() {
+        final CookieIdentityComparator comparator = new CookieIdentityComparator();
+        final BasicClientCookie c1 = new BasicClientCookie("name", "value1");
+        c1.setDomain("www.domain.com");
+        final BasicClientCookie c2 = new BasicClientCookie("name", "value2");
+        c2.setDomain("www.domain.com");
+        Assert.assertTrue(comparator.compare(c1, c2) == 0);
+
+        final BasicClientCookie c3 = new BasicClientCookie("name", "value1");
+        c3.setDomain("www.domain.com");
+        final BasicClientCookie c4 = new BasicClientCookie("name", "value2");
+        c4.setDomain("domain.com");
+        Assert.assertFalse(comparator.compare(c3, c4) == 0);
+    }
+
+    @Test
+    public void testCookieIdentityComparasionByNameAndNullDomain() {
+        final CookieIdentityComparator comparator = new CookieIdentityComparator();
+        final BasicClientCookie c1 = new BasicClientCookie("name", "value1");
+        c1.setDomain(null);
+        final BasicClientCookie c2 = new BasicClientCookie("name", "value2");
+        c2.setDomain(null);
+        Assert.assertTrue(comparator.compare(c1, c2) == 0);
+
+        final BasicClientCookie c3 = new BasicClientCookie("name", "value1");
+        c3.setDomain("www.domain.com");
+        final BasicClientCookie c4 = new BasicClientCookie("name", "value2");
+        c4.setDomain(null);
+        Assert.assertFalse(comparator.compare(c3, c4) == 0);
+    }
+
+    @Test
+    public void testCookieIdentityComparasionByNameAndLocalHost() {
+        final CookieIdentityComparator comparator = new CookieIdentityComparator();
+        final BasicClientCookie c1 = new BasicClientCookie("name", "value1");
+        c1.setDomain("localhost");
+        final BasicClientCookie c2 = new BasicClientCookie("name", "value2");
+        c2.setDomain("localhost");
+        Assert.assertTrue(comparator.compare(c1, c2) == 0);
+
+        final BasicClientCookie c3 = new BasicClientCookie("name", "value1");
+        c3.setDomain("localhost.local");
+        final BasicClientCookie c4 = new BasicClientCookie("name", "value2");
+        c4.setDomain("localhost");
+        Assert.assertTrue(comparator.compare(c3, c4) == 0);
+    }
+
+    @Test
+    public void testCookieIdentityComparasionByNameDomainAndPath() {
+        final CookieIdentityComparator comparator = new CookieIdentityComparator();
+        final BasicClientCookie c1 = new BasicClientCookie("name", "value1");
+        c1.setDomain("www.domain.com");
+        c1.setPath("/whatever");
+        final BasicClientCookie c2 = new BasicClientCookie("name", "value2");
+        c2.setDomain("www.domain.com");
+        c2.setPath("/whatever");
+        Assert.assertTrue(comparator.compare(c1, c2) == 0);
+
+        final BasicClientCookie c3 = new BasicClientCookie("name", "value1");
+        c3.setDomain("www.domain.com");
+        c3.setPath("/whatever");
+        final BasicClientCookie c4 = new BasicClientCookie("name", "value2");
+        c4.setDomain("domain.com");
+        c4.setPath("/whatever-not");
+        Assert.assertFalse(comparator.compare(c3, c4) == 0);
+    }
+
+    @Test
+    public void testCookieIdentityComparasionByNameDomainAndNullPath() {
+        final CookieIdentityComparator comparator = new CookieIdentityComparator();
+        final BasicClientCookie c1 = new BasicClientCookie("name", "value1");
+        c1.setDomain("www.domain.com");
+        c1.setPath("/");
+        final BasicClientCookie c2 = new BasicClientCookie("name", "value2");
+        c2.setDomain("www.domain.com");
+        c2.setPath(null);
+        Assert.assertTrue(comparator.compare(c1, c2) == 0);
+
+        final BasicClientCookie c3 = new BasicClientCookie("name", "value1");
+        c3.setDomain("www.domain.com");
+        c3.setPath("/whatever");
+        final BasicClientCookie c4 = new BasicClientCookie("name", "value2");
+        c4.setDomain("domain.com");
+        c4.setPath(null);
+        Assert.assertFalse(comparator.compare(c3, c4) == 0);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestDefaultBackoffStrategy.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestDefaultBackoffStrategy.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestDefaultBackoffStrategy.java
new file mode 100644
index 0000000..f8614de
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestDefaultBackoffStrategy.java
@@ -0,0 +1,83 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.impl.classic;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.net.ConnectException;
+import java.net.SocketTimeoutException;
+
+import org.apache.hc.core5.http.ConnectionRequestTimeoutException;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class TestDefaultBackoffStrategy {
+
+    private DefaultBackoffStrategy impl;
+
+    @Before
+    public void setUp() {
+        impl = new DefaultBackoffStrategy();
+    }
+
+    @Test
+    public void backsOffForSocketTimeouts() {
+        assertTrue(impl.shouldBackoff(new SocketTimeoutException()));
+    }
+
+    @Test
+    public void backsOffForConnectionTimeouts() {
+        assertTrue(impl.shouldBackoff(new ConnectException()));
+    }
+
+    @Test
+    public void doesNotBackOffForConnectionManagerTimeout() {
+        assertFalse(impl.shouldBackoff(new ConnectionRequestTimeoutException()));
+    }
+
+    @Test
+    public void backsOffForServiceUnavailable() {
+        final HttpResponse resp = new BasicHttpResponse(HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+        assertTrue(impl.shouldBackoff(resp));
+    }
+
+    @Test
+    public void doesNotBackOffForNon503StatusCodes() {
+        for(int i = 100; i <= 599; i++) {
+            if (i == HttpStatus.SC_SERVICE_UNAVAILABLE) {
+                continue;
+            }
+            final HttpResponse resp = new BasicHttpResponse(i, "Foo");
+            assertFalse(impl.shouldBackoff(resp));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestExecRuntimeImpl.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestExecRuntimeImpl.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestExecRuntimeImpl.java
new file mode 100644
index 0000000..2e53cd1
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestExecRuntimeImpl.java
@@ -0,0 +1,308 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.impl.classic;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.hc.client5.http.CancellableAware;
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.config.RequestConfig;
+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.core5.concurrent.Cancellable;
+import org.apache.hc.core5.http.ConnectionRequestTimeoutException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
+import org.apache.hc.core5.io.ShutdownType;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.logging.log4j.Logger;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SuppressWarnings({"static-access"}) // test code
+public class TestExecRuntimeImpl {
+
+    @Mock
+    private Logger log;
+    @Mock
+    private HttpClientConnectionManager mgr;
+    @Mock
+    private LeaseRequest leaseRequest;
+    @Mock
+    private HttpRequestExecutor requestExecutor;
+    @Mock
+    private CancellableAware cancellableAware;
+    @Mock
+    private ConnectionEndpoint connectionEndpoint;
+
+    private HttpRoute route;
+    private ExecRuntimeImpl execRuntime;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        route = new HttpRoute(new HttpHost("host", 80));
+        execRuntime = new ExecRuntimeImpl(log, mgr, requestExecutor, cancellableAware);
+    }
+
+    @Test
+    public void testAcquireEndpoint() throws Exception {
+        final HttpClientContext context = HttpClientContext.create();
+        final RequestConfig config = RequestConfig.custom()
+                .setConnectTimeout(123, TimeUnit.MILLISECONDS)
+                .setSocketTimeout(234, TimeUnit.MILLISECONDS)
+                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
+                .build();
+        context.setRequestConfig(config);
+        final HttpRoute route = new HttpRoute(new HttpHost("host", 80));
+
+        Mockito.when(mgr.lease(route, null)).thenReturn(leaseRequest);
+        Mockito.when(leaseRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(connectionEndpoint);
+
+        execRuntime.acquireConnection(route, null, context);
+
+        Assert.assertTrue(execRuntime.isConnectionAcquired());
+        Assert.assertSame(connectionEndpoint, execRuntime.ensureValid());
+        Assert.assertFalse(execRuntime.isConnected());
+        Assert.assertFalse(execRuntime.isConnectionReusable());
+
+        Mockito.verify(leaseRequest).get(345, TimeUnit.MILLISECONDS);
+        Mockito.verify(cancellableAware, Mockito.times(1)).setCancellable(leaseRequest);
+        Mockito.verify(cancellableAware, Mockito.times(1)).setCancellable(execRuntime);
+        Mockito.verify(cancellableAware, Mockito.times(2)).setCancellable(Mockito.<Cancellable>any());
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testAcquireEndpointAlreadyAcquired() throws Exception {
+        final HttpClientContext context = HttpClientContext.create();
+
+        Mockito.when(mgr.lease(route, null)).thenReturn(leaseRequest);
+        Mockito.when(leaseRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(connectionEndpoint);
+
+        execRuntime.acquireConnection(route, null, context);
+
+        Assert.assertTrue(execRuntime.isConnectionAcquired());
+        Assert.assertSame(connectionEndpoint, execRuntime.ensureValid());
+
+        execRuntime.acquireConnection(route, null, context);
+    }
+
+    @Test(expected = ConnectionRequestTimeoutException.class)
+    public void testAcquireEndpointLeaseRequestTimeout() throws Exception {
+        final HttpClientContext context = HttpClientContext.create();
+
+        Mockito.when(mgr.lease(route, null)).thenReturn(leaseRequest);
+        Mockito.when(leaseRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenThrow(new TimeoutException());
+
+        execRuntime.acquireConnection(route, null, context);
+    }
+
+    @Test(expected = RequestFailedException.class)
+    public void testAcquireEndpointLeaseRequestFailure() throws Exception {
+        final HttpClientContext context = HttpClientContext.create();
+
+        Mockito.when(mgr.lease(route, null)).thenReturn(leaseRequest);
+        Mockito.when(leaseRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenThrow(new ExecutionException(new IllegalStateException()));
+
+        execRuntime.acquireConnection(route, null, context);
+    }
+
+    @Test
+    public void testAbortEndpoint() throws Exception {
+        final HttpClientContext context = HttpClientContext.create();
+        Mockito.when(mgr.lease(route, null)).thenReturn(leaseRequest);
+        Mockito.when(leaseRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(connectionEndpoint);
+
+        execRuntime.acquireConnection(new HttpRoute(new HttpHost("host", 80)), null, context);
+        Assert.assertTrue(execRuntime.isConnectionAcquired());
+        execRuntime.discardConnection();
+
+        Assert.assertFalse(execRuntime.isConnectionAcquired());
+
+        Mockito.verify(connectionEndpoint).shutdown(ShutdownType.IMMEDIATE);
+        Mockito.verify(mgr).release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS);
+
+        execRuntime.discardConnection();
+
+        Mockito.verify(connectionEndpoint, Mockito.times(1)).shutdown(ShutdownType.IMMEDIATE);
+        Mockito.verify(mgr, Mockito.times(1)).release(
+                Mockito.<ConnectionEndpoint>any(),
+                Mockito.any(),
+                Mockito.<TimeValue>any());
+    }
+
+    @Test
+    public void testCancell() throws Exception {
+        final HttpClientContext context = HttpClientContext.create();
+
+        Mockito.when(mgr.lease(route, null)).thenReturn(leaseRequest);
+        Mockito.when(leaseRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(connectionEndpoint);
+
+        execRuntime.acquireConnection(route, null, context);
+        Assert.assertTrue(execRuntime.isConnectionAcquired());
+
+        Assert.assertTrue(execRuntime.cancel());
+
+        Assert.assertFalse(execRuntime.isConnectionAcquired());
+
+        Mockito.verify(connectionEndpoint).shutdown(ShutdownType.IMMEDIATE);
+        Mockito.verify(mgr).release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS);
+
+        Assert.assertFalse(execRuntime.cancel());
+
+        Mockito.verify(connectionEndpoint, Mockito.times(1)).shutdown(ShutdownType.IMMEDIATE);
+        Mockito.verify(mgr, Mockito.times(1)).release(
+                Mockito.<ConnectionEndpoint>any(),
+                Mockito.any(),
+                Mockito.<TimeValue>any());
+    }
+
+    @Test
+    public void testReleaseEndpointReusable() throws Exception {
+        final HttpClientContext context = HttpClientContext.create();
+
+        Mockito.when(mgr.lease(route, null)).thenReturn(leaseRequest);
+        Mockito.when(leaseRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(connectionEndpoint);
+
+        execRuntime.acquireConnection(route, null, context);
+        Assert.assertTrue(execRuntime.isConnectionAcquired());
+
+        execRuntime.setConnectionState("some state");
+        execRuntime.setConnectionValidFor(TimeValue.ofMillis(100000));
+        execRuntime.markConnectionReusable();
+
+        execRuntime.releaseConnection();
+
+        Assert.assertFalse(execRuntime.isConnectionAcquired());
+
+        Mockito.verify(connectionEndpoint, Mockito.never()).close();
+        Mockito.verify(mgr).release(connectionEndpoint, "some state", TimeValue.ofMillis(100000));
+
+        execRuntime.releaseConnection();
+
+        Mockito.verify(mgr, Mockito.times(1)).release(
+                Mockito.<ConnectionEndpoint>any(),
+                Mockito.any(),
+                Mockito.<TimeValue>any());
+    }
+
+    @Test
+    public void testReleaseEndpointNonReusable() throws Exception {
+        final HttpClientContext context = HttpClientContext.create();
+
+        Mockito.when(mgr.lease(route, null)).thenReturn(leaseRequest);
+        Mockito.when(leaseRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(connectionEndpoint);
+
+        execRuntime.acquireConnection(route, null, context);
+        Assert.assertTrue(execRuntime.isConnectionAcquired());
+
+        execRuntime.setConnectionState("some state");
+        execRuntime.setConnectionValidFor(TimeValue.ofMillis(100000));
+        execRuntime.markConnectionNonReusable();
+
+        execRuntime.releaseConnection();
+
+        Assert.assertFalse(execRuntime.isConnectionAcquired());
+
+        Mockito.verify(connectionEndpoint, Mockito.times(1)).close();
+        Mockito.verify(mgr).release(connectionEndpoint, null, TimeValue.ZERO_MILLISECONDS);
+
+        execRuntime.releaseConnection();
+
+        Mockito.verify(mgr, Mockito.times(1)).release(
+                Mockito.<ConnectionEndpoint>any(),
+                Mockito.any(),
+                Mockito.<TimeValue>any());
+    }
+
+    @Test
+    public void testConnectEndpoint() throws Exception {
+        final HttpClientContext context = HttpClientContext.create();
+        final RequestConfig config = RequestConfig.custom()
+                .setConnectTimeout(123, TimeUnit.MILLISECONDS)
+                .setSocketTimeout(234, TimeUnit.MILLISECONDS)
+                .setConnectionRequestTimeout(345, TimeUnit.MILLISECONDS)
+                .build();
+        context.setRequestConfig(config);
+
+        Mockito.when(mgr.lease(route, null)).thenReturn(leaseRequest);
+        Mockito.when(leaseRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(connectionEndpoint);
+
+        execRuntime.acquireConnection(route, null, context);
+        Assert.assertTrue(execRuntime.isConnectionAcquired());
+
+        Mockito.when(connectionEndpoint.isConnected()).thenReturn(false);
+        Assert.assertFalse(execRuntime.isConnected());
+
+        execRuntime.connect(context);
+
+        Mockito.verify(mgr).connect(connectionEndpoint, TimeValue.ofMillis(123), context);
+        Mockito.verify(connectionEndpoint).setSocketTimeout(234);
+    }
+
+    @Test
+    public void testDisonnectEndpoint() throws Exception {
+        final HttpClientContext context = HttpClientContext.create();
+
+        Mockito.when(mgr.lease(route, null)).thenReturn(leaseRequest);
+        Mockito.when(leaseRequest.get(
+                Mockito.anyLong(), Mockito.<TimeUnit>any())).thenReturn(connectionEndpoint);
+
+        execRuntime.acquireConnection(route, null, context);
+        Assert.assertTrue(execRuntime.isConnectionAcquired());
+
+        Mockito.when(connectionEndpoint.isConnected()).thenReturn(true);
+        Assert.assertTrue(execRuntime.isConnected());
+
+        execRuntime.connect(context);
+
+        Mockito.verify(mgr, Mockito.never()).connect(
+                Mockito.same(connectionEndpoint), Mockito.<TimeValue>any(), Mockito.<HttpClientContext>any());
+
+        execRuntime.disconnect();
+
+        Mockito.verify(connectionEndpoint).close();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestFutureRequestExecutionService.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestFutureRequestExecutionService.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestFutureRequestExecutionService.java
new file mode 100644
index 0000000..461cb34
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestFutureRequestExecutionService.java
@@ -0,0 +1,201 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.impl.classic;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.io.HttpClientConnectionManager;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+@SuppressWarnings("boxing") // test code
+public class TestFutureRequestExecutionService {
+
+    private HttpServer localServer;
+    private String uri;
+    private FutureRequestExecutionService httpAsyncClientWithFuture;
+
+    private final AtomicBoolean blocked = new AtomicBoolean(false);
+
+    @Before
+    public void before() throws Exception {
+        this.localServer = ServerBootstrap.bootstrap()
+                .register("/wait", new HttpRequestHandler() {
+
+            @Override
+            public void handle(
+                    final ClassicHttpRequest request,
+                    final ClassicHttpResponse response,
+                    final HttpContext context) throws HttpException, IOException {
+                try {
+                    while(blocked.get()) {
+                        Thread.sleep(10);
+                    }
+                } catch (final InterruptedException e) {
+                    throw new IllegalStateException(e);
+                }
+                response.setCode(200);
+            }
+        }).create();
+
+        this.localServer.start();
+        uri = "http://localhost:" + this.localServer.getLocalPort() + "/wait";
+        final HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create()
+                .setMaxConnPerRoute(5)
+                .build();
+        final CloseableHttpClient httpClient = HttpClientBuilder.create()
+                .setConnectionManager(cm)
+                .build();
+        final ExecutorService executorService = Executors.newFixedThreadPool(5);
+        httpAsyncClientWithFuture = new FutureRequestExecutionService(httpClient, executorService);
+    }
+
+    @After
+    public void after() throws Exception {
+        blocked.set(false); // any remaining requests should unblock
+        this.localServer.stop();
+        httpAsyncClientWithFuture.close();
+    }
+
+    @Test
+    public void shouldExecuteSingleCall() throws InterruptedException, ExecutionException {
+        final HttpRequestFutureTask<Boolean> task = httpAsyncClientWithFuture.execute(
+            new HttpGet(uri), HttpClientContext.create(), new OkidokiHandler());
+        Assert.assertTrue("request should have returned OK", task.get().booleanValue());
+    }
+
+    @Test(expected=CancellationException.class)
+    public void shouldCancel() throws InterruptedException, ExecutionException {
+        final HttpRequestFutureTask<Boolean> task = httpAsyncClientWithFuture.execute(
+            new HttpGet(uri), HttpClientContext.create(), new OkidokiHandler());
+        task.cancel(true);
+        task.get();
+    }
+
+    @Test(expected=TimeoutException.class)
+    public void shouldTimeout() throws InterruptedException, ExecutionException, TimeoutException {
+        blocked.set(true);
+        final HttpRequestFutureTask<Boolean> task = httpAsyncClientWithFuture.execute(
+            new HttpGet(uri), HttpClientContext.create(), new OkidokiHandler());
+        task.get(10, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void shouldExecuteMultipleCalls() throws Exception {
+        final int reqNo = 100;
+        final Queue<Future<Boolean>> tasks = new LinkedList<>();
+        for(int i = 0; i < reqNo; i++) {
+            final Future<Boolean> task = httpAsyncClientWithFuture.execute(
+                    new HttpGet(uri), HttpClientContext.create(), new OkidokiHandler());
+            tasks.add(task);
+        }
+        for (final Future<Boolean> task : tasks) {
+            final Boolean b = task.get();
+            Assert.assertNotNull(b);
+            Assert.assertTrue("request should have returned OK", b.booleanValue());
+        }
+    }
+
+    @Test
+    public void shouldExecuteMultipleCallsAndCallback() throws Exception {
+        final int reqNo = 100;
+        final Queue<Future<Boolean>> tasks = new LinkedList<>();
+        final CountDownLatch latch = new CountDownLatch(reqNo);
+        for(int i = 0; i < reqNo; i++) {
+            final Future<Boolean> task = httpAsyncClientWithFuture.execute(
+                    new HttpGet(uri), HttpClientContext.create(),
+                    new OkidokiHandler(), new CountingCallback(latch));
+            tasks.add(task);
+        }
+        Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
+        for (final Future<Boolean> task : tasks) {
+            final Boolean b = task.get();
+            Assert.assertNotNull(b);
+            Assert.assertTrue("request should have returned OK", b.booleanValue());
+        }
+    }
+
+    private final class CountingCallback implements FutureCallback<Boolean> {
+
+        private final CountDownLatch latch;
+
+        CountingCallback(final CountDownLatch latch) {
+            super();
+            this.latch = latch;
+        }
+
+        @Override
+        public void failed(final Exception ex) {
+            latch.countDown();
+        }
+
+        @Override
+        public void completed(final Boolean result) {
+            latch.countDown();
+        }
+
+        @Override
+        public void cancelled() {
+            latch.countDown();
+        }
+    }
+
+
+    private final class OkidokiHandler implements HttpClientResponseHandler<Boolean> {
+        @Override
+        public Boolean handleResponse(
+                final ClassicHttpResponse response) throws IOException {
+            return response.getCode() == 200;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestInternalHttpClient.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestInternalHttpClient.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestInternalHttpClient.java
new file mode 100644
index 0000000..3612ef4
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestInternalHttpClient.java
@@ -0,0 +1,219 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.impl.classic;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.hc.client5.http.ClientProtocolException;
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.auth.AuthSchemeProvider;
+import org.apache.hc.client5.http.auth.CredentialsProvider;
+import org.apache.hc.client5.http.classic.ExecChain;
+import org.apache.hc.client5.http.classic.ExecChainHandler;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+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.HttpClientContext;
+import org.apache.hc.client5.http.routing.HttpRoutePlanner;
+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}.
+ */
+@SuppressWarnings({"static-access"}) // test code
+public class TestInternalHttpClient {
+
+    @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;
+
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        client = new InternalHttpClient(connManager, requestExecutor, new ExecChainElement(execChain, null), routePlanner,
+                cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider,
+                defaultConfig, Arrays.asList(closeable1, closeable2));
+
+    }
+
+    @Test
+    public void testExecute() throws Exception {
+        final HttpGet httpget = new HttpGet("http://somehost/stuff");
+        final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80));
+
+        Mockito.when(routePlanner.determineRoute(
+                Mockito.eq(new HttpHost("somehost")),
+                Mockito.<HttpClientContext>any())).thenReturn(route);
+
+        client.execute(httpget);
+
+        Mockito.verify(execChain).execute(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any(),
+                Mockito.<ExecChain>any());
+    }
+
+    @Test(expected=ClientProtocolException.class)
+    public void testExecuteHttpException() throws Exception {
+        final HttpGet httpget = new HttpGet("http://somehost/stuff");
+        final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80));
+
+        Mockito.when(routePlanner.determineRoute(
+                Mockito.eq(new HttpHost("somehost")),
+                Mockito.<HttpClientContext>any())).thenReturn(route);
+        Mockito.when(execChain.execute(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any(),
+                Mockito.<ExecChain>any())).thenThrow(new HttpException());
+
+        client.execute(httpget);
+    }
+
+    @Test
+    public void testExecuteDefaultContext() throws Exception {
+        final HttpGet httpget = new HttpGet("http://somehost/stuff");
+        final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80));
+
+        Mockito.when(routePlanner.determineRoute(
+                Mockito.eq(new HttpHost("somehost")),
+                Mockito.<HttpClientContext>any())).thenReturn(route);
+
+        final HttpClientContext context = HttpClientContext.create();
+        client.execute(httpget, context);
+
+        Assert.assertSame(cookieSpecRegistry, context.getCookieSpecRegistry());
+        Assert.assertSame(authSchemeRegistry, context.getAuthSchemeRegistry());
+        Assert.assertSame(cookieStore, context.getCookieStore());
+        Assert.assertSame(credentialsProvider, context.getCredentialsProvider());
+        Assert.assertSame(defaultConfig, context.getRequestConfig());
+    }
+
+    @Test
+    public void testExecuteRequestConfig() throws Exception {
+        final HttpGet httpget = new HttpGet("http://somehost/stuff");
+        final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80));
+
+        Mockito.when(routePlanner.determineRoute(
+                Mockito.eq(new HttpHost("somehost")),
+                Mockito.<HttpClientContext>any())).thenReturn(route);
+
+        final RequestConfig config = RequestConfig.custom().build();
+        httpget.setConfig(config);
+        final HttpClientContext context = HttpClientContext.create();
+        client.execute(httpget, context);
+
+        Assert.assertSame(config, context.getRequestConfig());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testExecuteLocalContext() throws Exception {
+        final HttpGet httpget = new HttpGet("http://somehost/stuff");
+        final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80));
+
+        Mockito.when(routePlanner.determineRoute(
+                Mockito.eq(new HttpHost("somehost")),
+                Mockito.<HttpClientContext>any())).thenReturn(route);
+
+        final HttpClientContext context = HttpClientContext.create();
+
+        final Lookup<CookieSpecProvider> localCookieSpecRegistry = Mockito.mock(Lookup.class);
+        final Lookup<AuthSchemeProvider> localAuthSchemeRegistry = Mockito.mock(Lookup.class);
+        final CookieStore localCookieStore = Mockito.mock(CookieStore.class);
+        final CredentialsProvider localCredentialsProvider = Mockito.mock(CredentialsProvider.class);
+        final RequestConfig localConfig = RequestConfig.custom().build();
+
+        context.setCookieSpecRegistry(localCookieSpecRegistry);
+        context.setAuthSchemeRegistry(localAuthSchemeRegistry);
+        context.setCookieStore(localCookieStore);
+        context.setCredentialsProvider(localCredentialsProvider);
+        context.setRequestConfig(localConfig);
+
+        client.execute(httpget, context);
+
+        Assert.assertSame(localCookieSpecRegistry, context.getCookieSpecRegistry());
+        Assert.assertSame(localAuthSchemeRegistry, context.getAuthSchemeRegistry());
+        Assert.assertSame(localCookieStore, context.getCookieStore());
+        Assert.assertSame(localCredentialsProvider, context.getCredentialsProvider());
+        Assert.assertSame(localConfig, context.getRequestConfig());
+    }
+
+    @Test
+    public void testClientClose() throws Exception {
+        client.close();
+
+        Mockito.verify(closeable1).close();
+        Mockito.verify(closeable2).close();
+    }
+
+    @Test
+    public void testClientCloseIOException() throws Exception {
+        Mockito.doThrow(new IOException()).when(closeable1).close();
+
+        client.close();
+
+        Mockito.verify(closeable1).close();
+        Mockito.verify(closeable2).close();
+    }
+
+}