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:04 UTC

[04/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/TestMainClientExec.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java
new file mode 100644
index 0000000..10313d1
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestMainClientExec.java
@@ -0,0 +1,348 @@
+/*
+ * ====================================================================
+ * 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.IOException;
+import java.io.InterruptedIOException;
+
+import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.UserTokenHandler;
+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.ConnectionShutdownException;
+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.HttpHost;
+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.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 TestMainClientExec {
+
+    @Mock
+    private ConnectionReuseStrategy reuseStrategy;
+    @Mock
+    private ConnectionKeepAliveStrategy keepAliveStrategy;
+    @Mock
+    private UserTokenHandler userTokenHandler;
+    @Mock
+    private ExecRuntime endpoint;
+
+    private MainClientExec mainClientExec;
+    private HttpHost target;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mainClientExec = new MainClientExec(reuseStrategy, keepAliveStrategy, userTokenHandler);
+        target = new HttpHost("foo", 80);
+    }
+
+    @Test
+    public void testExecRequestNonPersistentConnection() 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());
+
+        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).execute(request, context);
+        Mockito.verify(endpoint, Mockito.times(1)).markConnectionNonReusable();
+        Mockito.verify(endpoint, Mockito.never()).releaseConnection();
+
+        Assert.assertNull(context.getUserToken());
+        Assert.assertNotNull(finalResponse);
+        Assert.assertTrue(finalResponse instanceof CloseableHttpResponse);
+    }
+
+    @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).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 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());
+
+        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.<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));
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
+
+        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 testExecRequestPersistentConnectionNoResponseEntity() 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");
+
+        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.<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));
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = mainClientExec.execute(request, scope, null);
+
+        Mockito.verify(endpoint).execute(request, context);
+        Mockito.verify(endpoint).releaseConnection();
+
+        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 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());
+
+        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.<HttpClientContext>any())).thenReturn(response);
+        Mockito.when(reuseStrategy.keepAlive(
+                Mockito.same(request),
+                Mockito.same(response),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.FALSE);
+
+        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(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 ClassicHttpRequest request = new HttpGet("http://bar/test");
+
+        Mockito.when(endpoint.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientContext>any())).thenThrow(new ConnectionShutdownException());
+
+        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(expected=RuntimeException.class)
+    public void testExecRuntimeException() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
+
+        Mockito.when(endpoint.execute(
+                Mockito.same(request),
+                Mockito.<HttpClientContext>any())).thenThrow(new RuntimeException("Ka-boom"));
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        try {
+            mainClientExec.execute(request, scope, null);
+        } catch (final Exception ex) {
+            Mockito.verify(endpoint).discardConnection();
+            throw ex;
+        }
+    }
+
+    @Test(expected=HttpException.class)
+    public void testExecHttpException() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
+
+        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, scope, null);
+        } catch (final Exception ex) {
+            Mockito.verify(endpoint).discardConnection();
+            throw ex;
+        }
+    }
+
+    @Test(expected=IOException.class)
+    public void testExecIOException() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final ClassicHttpRequest request = new HttpGet("http://bar/test");
+
+        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, scope, null);
+        } catch (final Exception ex) {
+            Mockito.verify(endpoint).discardConnection();
+            throw ex;
+        }
+    }
+
+    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/TestNullBackoffStrategy.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestNullBackoffStrategy.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestNullBackoffStrategy.java
new file mode 100644
index 0000000..9c7411a
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestNullBackoffStrategy.java
@@ -0,0 +1,57 @@
+/*
+ * ====================================================================
+ * 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 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 TestNullBackoffStrategy {
+
+    private NullBackoffStrategy impl;
+
+    @Before
+    public void setUp() {
+        impl = new NullBackoffStrategy();
+    }
+
+    @Test
+    public void doesNotBackoffForThrowables() {
+        assertFalse(impl.shouldBackoff(new Exception()));
+    }
+
+    @Test
+    public void doesNotBackoffForResponses() {
+        final HttpResponse resp = new BasicHttpResponse(HttpStatus.SC_SERVICE_UNAVAILABLE, "Service Unavailable");
+        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/TestProtocolExec.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestProtocolExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestProtocolExec.java
new file mode 100644
index 0000000..881af34
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestProtocolExec.java
@@ -0,0 +1,341 @@
+/*
+ * ====================================================================
+ * 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.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+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.NonRepeatableRequestException;
+import org.apache.hc.client5.http.auth.AuthChallenge;
+import org.apache.hc.client5.http.auth.AuthExchange;
+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.Credentials;
+import org.apache.hc.client5.http.auth.CredentialsProvider;
+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.classic.methods.HttpPost;
+import org.apache.hc.client5.http.entity.EntityBuilder;
+import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
+import org.apache.hc.client5.http.impl.auth.BasicScheme;
+import org.apache.hc.client5.http.impl.auth.NTLMScheme;
+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.EntityDetails;
+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.HttpResponse;
+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.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+@SuppressWarnings({"static-access"}) // test code
+public class TestProtocolExec {
+
+    @Mock
+    private HttpProcessor httpProcessor;
+    @Mock
+    private AuthenticationStrategy targetAuthStrategy;
+    @Mock
+    private AuthenticationStrategy proxyAuthStrategy;
+    @Mock
+    private ExecChain chain;
+    @Mock
+    private ExecRuntime execRuntime;
+
+    private ProtocolExec protocolExec;
+    private HttpHost target;
+    private HttpHost proxy;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        protocolExec = new ProtocolExec(httpProcessor, targetAuthStrategy, proxyAuthStrategy);
+        target = new HttpHost("foo", 80);
+        proxy = new HttpHost("bar", 8888);
+    }
+
+    @Test
+    public void testFundamentals() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final ClassicHttpRequest request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
+
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        protocolExec.execute(request, scope, chain);
+
+        Mockito.verify(httpProcessor).process(request, null, context);
+        Mockito.verify(chain).proceed(request, scope);
+        Mockito.verify(httpProcessor).process(response, null, context);
+
+        Assert.assertEquals(route, context.getHttpRoute());
+        Assert.assertSame(request, context.getRequest());
+        Assert.assertSame(response, context.getResponse());
+    }
+
+    @Test
+    public void testUserInfoInRequestURI() throws Exception {
+        final HttpRoute route = new HttpRoute(new HttpHost("somehost", 8080));
+        final ClassicHttpRequest request = new HttpGet("http://somefella:secret@bar/test");
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(new BasicCredentialsProvider());
+
+        final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        protocolExec.execute(request, scope, chain);
+        Assert.assertEquals(new URI("http://bar/test"), request.getUri());
+        final CredentialsProvider credentialsProvider = context.getCredentialsProvider();
+        final Credentials creds = credentialsProvider.getCredentials(new AuthScope("bar", -1, null), null);
+        Assert.assertNotNull(creds);
+        Assert.assertEquals("somefella", creds.getUserPrincipal().getName());
+    }
+
+    @Test(expected = HttpException.class)
+    public void testPostProcessHttpException() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final ClassicHttpRequest request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
+
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response);
+        Mockito.doThrow(new HttpException("Ooopsie")).when(httpProcessor).process(
+                Mockito.same(response), Mockito.<EntityDetails>isNull(), Mockito.<HttpContext>any());
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        try {
+            protocolExec.execute(request, scope, chain);
+        } catch (final Exception ex) {
+            Mockito.verify(execRuntime).discardConnection();
+            throw ex;
+        }
+    }
+
+    @Test(expected = IOException.class)
+    public void testPostProcessIOException() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final ClassicHttpRequest request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response);
+        Mockito.doThrow(new IOException("Ooopsie")).when(httpProcessor).process(
+                Mockito.same(response), Mockito.<EntityDetails>isNull(), Mockito.<HttpContext>any());
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        try {
+            protocolExec.execute(request, scope, chain);
+        } catch (final Exception ex) {
+            Mockito.verify(execRuntime).discardConnection();
+            throw ex;
+        }
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testPostProcessRuntimeException() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final ClassicHttpRequest request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response);
+        Mockito.doThrow(new RuntimeException("Ooopsie")).when(httpProcessor).process(
+                Mockito.same(response), Mockito.<EntityDetails>isNull(), Mockito.<HttpContext>any());
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        try {
+            protocolExec.execute(request, scope, chain);
+        } catch (final Exception ex) {
+            Mockito.verify(execRuntime).discardConnection();
+            throw ex;
+        }
+    }
+
+    @Test
+    public void testExecRequestRetryOnAuthChallenge() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        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}));
+        response1.setEntity(EntityBuilder.create()
+                .setStream(instream1)
+                .build());
+        final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK");
+        final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
+        response2.setEntity(EntityBuilder.create()
+                .setStream(instream2)
+                .build());
+
+        final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray()));
+        context.setCredentialsProvider(credentialsProvider);
+
+        Mockito.when(chain.proceed(
+                Mockito.same(request),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response1, response2);
+        Mockito.when(targetAuthStrategy.select(
+                Mockito.eq(ChallengeType.TARGET),
+                Mockito.<Map<String, AuthChallenge>>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Collections.<AuthScheme>singletonList(new BasicScheme()));
+        Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, execRuntime, context);
+        final ClassicHttpResponse finalResponse = protocolExec.execute(request, scope, chain);
+        Mockito.verify(chain, Mockito.times(2)).proceed(request, scope);
+        Mockito.verify(instream1).close();
+        Mockito.verify(instream2, Mockito.never()).close();
+
+        Assert.assertNotNull(finalResponse);
+        Assert.assertEquals(200, finalResponse.getCode());
+    }
+
+    @Test
+    public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exception {
+        final HttpRoute route = new HttpRoute(target, proxy);
+        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}));
+        response1.setEntity(EntityBuilder.create()
+                .setStream(instream1)
+                .build());
+        final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK");
+        final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {2, 3, 4}));
+        response2.setEntity(EntityBuilder.create()
+                .setStream(instream2)
+                .build());
+
+        final HttpClientContext context = new HttpClientContext();
+
+        final AuthExchange proxyAuthExchange = new AuthExchange();
+        proxyAuthExchange.setState(AuthExchange.State.SUCCESS);
+        proxyAuthExchange.select(new NTLMScheme());
+        context.setAuthExchange(proxy, proxyAuthExchange);
+
+        final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray()));
+        context.setCredentialsProvider(credentialsProvider);
+
+        Mockito.when(chain.proceed(
+                Mockito.same(request),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response1, response2);
+
+        Mockito.when(targetAuthStrategy.select(
+                Mockito.eq(ChallengeType.TARGET),
+                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);
+        final ClassicHttpResponse finalResponse = protocolExec.execute(request, scope, chain);
+        Mockito.verify(chain, Mockito.times(2)).proceed(request, scope);
+        Mockito.verify(execRuntime).disconnect();
+        Mockito.verify(instream2, Mockito.never()).close();
+
+        Assert.assertNotNull(finalResponse);
+        Assert.assertEquals(200, finalResponse.getCode());
+        Assert.assertNull(proxyAuthExchange.getAuthScheme());
+    }
+
+    @Test(expected = NonRepeatableRequestException.class)
+    public void testExecEntityEnclosingRequest() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpClientContext context = new HttpClientContext();
+        final HttpPost request = new HttpPost("http://foo/test");
+        final InputStream instream0 = new ByteArrayInputStream(new byte[] {1, 2, 3});
+        request.setEntity(EntityBuilder.create()
+                .setStream(instream0)
+                .build());
+        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});
+        response1.setEntity(EntityBuilder.create()
+                .setStream(instream1)
+                .build());
+
+        final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+        credentialsProvider.setCredentials(new AuthScope(target), new UsernamePasswordCredentials("user", "pass".toCharArray()));
+        context.setCredentialsProvider(credentialsProvider);
+
+        Mockito.when(chain.proceed(
+                Mockito.same(request),
+                Mockito.<ExecChain.Scope>any())).thenAnswer(new Answer<HttpResponse>() {
+
+            @Override
+            public HttpResponse answer(final InvocationOnMock invocationOnMock) throws Throwable {
+                final Object[] args = invocationOnMock.getArguments();
+                final ClassicHttpRequest requestEE = (ClassicHttpRequest) args[0];
+                requestEE.getEntity().writeTo(new ByteArrayOutputStream());
+                return response1;
+            }
+
+        });
+
+        Mockito.when(targetAuthStrategy.select(
+                Mockito.eq(ChallengeType.TARGET),
+                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);
+        protocolExec.execute(request, scope, chain);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRedirectExec.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRedirectExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRedirectExec.java
new file mode 100644
index 0000000..0346ead
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRedirectExec.java
@@ -0,0 +1,350 @@
+/*
+ * ====================================================================
+ * 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.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.RedirectException;
+import org.apache.hc.client5.http.auth.AuthExchange;
+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.config.RequestConfig;
+import org.apache.hc.client5.http.entity.EntityBuilder;
+import org.apache.hc.client5.http.impl.auth.BasicScheme;
+import org.apache.hc.client5.http.impl.auth.NTLMScheme;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.client5.http.protocol.RedirectStrategy;
+import org.apache.hc.client5.http.routing.HttpRoutePlanner;
+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.HttpResponse;
+import org.apache.hc.core5.http.ProtocolException;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SuppressWarnings({"boxing","static-access"}) // test code
+public class TestRedirectExec {
+
+    @Mock
+    private HttpRoutePlanner httpRoutePlanner;
+    @Mock
+    private RedirectStrategy redirectStrategy;
+    @Mock
+    private ExecChain chain;
+    @Mock
+    private ExecRuntime endpoint;
+
+    private RedirectExec redirectExec;
+    private HttpHost target;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        redirectExec = new RedirectExec(httpRoutePlanner, redirectStrategy);
+        target = new HttpHost("localhost", 80);
+    }
+
+    @Test
+    public void testFundamentals() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpGet request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response1 = Mockito.mock(ClassicHttpResponse.class);
+        final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
+        final HttpEntity entity1 = EntityBuilder.create()
+                .setStream(instream1)
+                .build();
+        Mockito.when(response1.getEntity()).thenReturn(entity1);
+        final ClassicHttpResponse response2 = Mockito.mock(ClassicHttpResponse.class);
+        final InputStream instream2 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
+        final HttpEntity entity2 = EntityBuilder.create()
+                .setStream(instream2)
+                .build();
+        Mockito.when(response2.getEntity()).thenReturn(entity2);
+        final URI redirect = new URI("http://localhost:80/redirect");
+
+        Mockito.when(chain.proceed(
+                Mockito.same(request),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response1);
+        Mockito.when(chain.proceed(
+                HttpRequestMatcher.matchesRequestUri(redirect),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response2);
+        Mockito.when(redirectStrategy.isRedirected(
+                Mockito.same(request),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(redirectStrategy.getLocationURI(
+                Mockito.same(request),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(redirect);
+        Mockito.when(httpRoutePlanner.determineRoute(
+                Mockito.eq(target),
+                Mockito.<HttpClientContext>any())).thenReturn(route);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        redirectExec.execute(request, scope, chain);
+
+        final ArgumentCaptor<ClassicHttpRequest> reqCaptor = ArgumentCaptor.forClass(
+                ClassicHttpRequest.class);
+        Mockito.verify(chain, Mockito.times(2)).proceed(
+                reqCaptor.capture(),
+                Mockito.same(scope));
+
+        final List<ClassicHttpRequest> allValues = reqCaptor.getAllValues();
+        Assert.assertNotNull(allValues);
+        Assert.assertEquals(2, allValues.size());
+        Assert.assertSame(request, allValues.get(0));
+
+        Mockito.verify(response1, Mockito.times(1)).close();
+        Mockito.verify(instream1, Mockito.times(1)).close();
+        Mockito.verify(response2, Mockito.never()).close();
+        Mockito.verify(instream2, Mockito.never()).close();
+    }
+
+    @Test(expected = RedirectException.class)
+    public void testMaxRedirect() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpGet request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+        final RequestConfig config = RequestConfig.custom()
+                .setRedirectsEnabled(true)
+                .setMaxRedirects(3)
+                .build();
+        context.setRequestConfig(config);
+
+        final ClassicHttpResponse response1 = Mockito.mock(ClassicHttpResponse.class);
+        final URI redirect = new URI("http://localhost:80/redirect");
+
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response1);
+        Mockito.when(redirectStrategy.isRedirected(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(redirectStrategy.getLocationURI(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpClientContext>any())).thenReturn(redirect);
+        Mockito.when(httpRoutePlanner.determineRoute(
+                Mockito.eq(target),
+                Mockito.<HttpClientContext>any())).thenReturn(route);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        redirectExec.execute(request, scope, chain);
+    }
+
+    @Test(expected = HttpException.class)
+    public void testRelativeRedirect() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpGet request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response1 = Mockito.mock(ClassicHttpResponse.class);
+        final ClassicHttpResponse response2 = Mockito.mock(ClassicHttpResponse.class);
+        final URI redirect = new URI("/redirect");
+        Mockito.when(chain.proceed(
+                Mockito.same(request),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response1);
+        Mockito.when(chain.proceed(
+                HttpRequestMatcher.matchesRequestUri(redirect),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response2);
+        Mockito.when(redirectStrategy.isRedirected(
+                Mockito.same(request),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(redirectStrategy.getLocationURI(
+                Mockito.same(request),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(redirect);
+        Mockito.when(httpRoutePlanner.determineRoute(
+                Mockito.eq(target),
+                Mockito.<HttpClientContext>any())).thenReturn(route);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        redirectExec.execute(request, scope, chain);
+    }
+
+    @Test
+    public void testCrossSiteRedirect() throws Exception {
+
+        final HttpHost proxy = new HttpHost("proxy");
+        final HttpRoute route = new HttpRoute(target, proxy);
+        final HttpGet request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final AuthExchange targetAuthExchange = new AuthExchange();
+        targetAuthExchange.setState(AuthExchange.State.SUCCESS);
+        targetAuthExchange.select(new BasicScheme());
+        final AuthExchange proxyAuthExchange = new AuthExchange();
+        proxyAuthExchange.setState(AuthExchange.State.SUCCESS);
+        proxyAuthExchange.select(new NTLMScheme());
+        context.setAuthExchange(target, targetAuthExchange);
+        context.setAuthExchange(proxy, proxyAuthExchange);
+
+        final ClassicHttpResponse response1 = Mockito.mock(ClassicHttpResponse.class);
+        final ClassicHttpResponse response2 = Mockito.mock(ClassicHttpResponse.class);
+        final HttpHost otherHost = new HttpHost("otherhost", 8888);
+        final URI redirect = new URI("http://otherhost:8888/redirect");
+        Mockito.when(chain.proceed(
+                Mockito.same(request),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response1);
+        Mockito.when(chain.proceed(
+                HttpRequestMatcher.matchesRequestUri(redirect),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response2);
+        Mockito.when(redirectStrategy.isRedirected(
+                Mockito.same(request),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.when(redirectStrategy.getLocationURI(
+                Mockito.same(request),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(redirect);
+        Mockito.when(httpRoutePlanner.determineRoute(
+                Mockito.eq(target),
+                Mockito.<HttpClientContext>any())).thenReturn(new HttpRoute(target));
+        Mockito.when(httpRoutePlanner.determineRoute(
+                Mockito.eq(otherHost),
+                Mockito.<HttpClientContext>any())).thenReturn(new HttpRoute(otherHost));
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        redirectExec.execute(request, scope, chain);
+
+        final AuthExchange authExchange1 = context.getAuthExchange(target);
+        Assert.assertNotNull(authExchange1);
+        Assert.assertEquals(AuthExchange.State.UNCHALLENGED, authExchange1.getState());
+        Assert.assertEquals(null, authExchange1.getAuthScheme());
+        final AuthExchange authExchange2 = context.getAuthExchange(proxy);
+        Assert.assertNotNull(authExchange2);
+        Assert.assertEquals(AuthExchange.State.UNCHALLENGED, authExchange2.getState());
+        Assert.assertEquals(null, authExchange2.getAuthScheme());
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testRedirectRuntimeException() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpGet request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response1 = Mockito.mock(ClassicHttpResponse.class);
+        Mockito.when(chain.proceed(
+                Mockito.same(request),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response1);
+        Mockito.when(redirectStrategy.isRedirected(
+                Mockito.same(request),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.doThrow(new RuntimeException("Oppsie")).when(redirectStrategy.getLocationURI(
+                Mockito.same(request),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any()));
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        try {
+            redirectExec.execute(request, scope, chain);
+        } catch (final Exception ex) {
+            Mockito.verify(response1).close();
+            throw ex;
+        }
+    }
+
+    @Test(expected = ProtocolException.class)
+    public void testRedirectProtocolException() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpGet request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response1 = Mockito.mock(ClassicHttpResponse.class);
+        final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3}));
+        final HttpEntity entity1 = EntityBuilder.create()
+                .setStream(instream1)
+                .build();
+        Mockito.when(response1.getEntity()).thenReturn(entity1);
+        Mockito.when(chain.proceed(
+                Mockito.same(request),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response1);
+        Mockito.when(redirectStrategy.isRedirected(
+                Mockito.same(request),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any())).thenReturn(Boolean.TRUE);
+        Mockito.doThrow(new ProtocolException("Oppsie")).when(redirectStrategy).getLocationURI(
+                Mockito.same(request),
+                Mockito.same(response1),
+                Mockito.<HttpClientContext>any());
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        try {
+            redirectExec.execute(request, scope, chain);
+        } catch (final Exception ex) {
+            Mockito.verify(instream1).close();
+            Mockito.verify(response1).close();
+            throw ex;
+        }
+    }
+
+    private static class HttpRequestMatcher implements ArgumentMatcher<ClassicHttpRequest> {
+
+        private final URI requestUri;
+
+        HttpRequestMatcher(final URI requestUri) {
+            super();
+            this.requestUri = requestUri;
+        }
+
+        @Override
+        public boolean matches(final ClassicHttpRequest argument) {
+            try {
+                return requestUri.equals(argument.getUri());
+            } catch (URISyntaxException e) {
+                return false;
+            }
+        }
+
+        static ClassicHttpRequest matchesRequestUri(final URI requestUri) {
+            return ArgumentMatchers.argThat(new HttpRequestMatcher(requestUri));
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestResponseEntityWrapper.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestResponseEntityWrapper.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestResponseEntityWrapper.java
new file mode 100644
index 0000000..46250cf
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestResponseEntityWrapper.java
@@ -0,0 +1,142 @@
+/*
+ * ====================================================================
+ * 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 java.io.OutputStream;
+import java.net.SocketException;
+
+import org.apache.hc.client5.http.classic.ExecRuntime;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+@SuppressWarnings("boxing") // test code
+public class TestResponseEntityWrapper {
+
+    private InputStream instream;
+    private HttpEntity entity;
+    private ExecRuntime execRuntime;
+    private ResponseEntityProxy wrapper;
+
+    @Before
+    public void setup() throws Exception {
+        instream = Mockito.mock(InputStream.class);
+        entity = Mockito.mock(HttpEntity.class);
+        Mockito.when(entity.getContent()).thenReturn(instream);
+        execRuntime = Mockito.mock(ExecRuntime.class);
+        wrapper = new ResponseEntityProxy(entity, execRuntime);
+    }
+
+    @Test
+    public void testReusableEntityStreamClosed() throws Exception {
+        Mockito.when(entity.isStreaming()).thenReturn(true);
+        Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true);
+        EntityUtils.consume(wrapper);
+
+        Mockito.verify(instream, Mockito.times(1)).close();
+        Mockito.verify(execRuntime).releaseConnection();
+    }
+
+    @Test
+    public void testReusableEntityStreamClosedIOError() throws Exception {
+        Mockito.when(entity.isStreaming()).thenReturn(true);
+        Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true);
+        Mockito.doThrow(new IOException()).when(instream).close();
+        try {
+            EntityUtils.consume(wrapper);
+            Assert.fail("IOException expected");
+        } catch (final IOException ex) {
+        }
+        Mockito.verify(execRuntime, Mockito.atLeast(1)).discardConnection();
+    }
+
+    @Test
+    public void testEntityStreamClosedIOErrorAlreadyReleased() throws Exception {
+        Mockito.when(entity.isStreaming()).thenReturn(true);
+        Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true);
+        Mockito.when(execRuntime.isConnectionAcquired()).thenReturn(false);
+        Mockito.doThrow(new SocketException()).when(instream).close();
+        EntityUtils.consume(wrapper);
+        Mockito.verify(execRuntime).discardConnection();
+    }
+
+    @Test
+    public void testReusableEntityWriteTo() throws Exception {
+        final OutputStream outstream = Mockito.mock(OutputStream.class);
+        Mockito.when(entity.isStreaming()).thenReturn(true);
+        Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true);
+        wrapper.writeTo(outstream);
+        Mockito.verify(execRuntime).releaseConnection();
+    }
+
+    @Test
+    public void testReusableEntityWriteToIOError() throws Exception {
+        final OutputStream outstream = Mockito.mock(OutputStream.class);
+        Mockito.when(entity.isStreaming()).thenReturn(true);
+        Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true);
+        Mockito.doThrow(new IOException()).when(entity).writeTo(outstream);
+        try {
+            wrapper.writeTo(outstream);
+            Assert.fail("IOException expected");
+        } catch (final IOException ex) {
+        }
+        Mockito.verify(execRuntime, Mockito.never()).releaseConnection();
+        Mockito.verify(execRuntime, Mockito.atLeast(1)).discardConnection();
+    }
+
+    @Test
+    public void testReusableEntityEndOfStream() throws Exception {
+        Mockito.when(instream.read()).thenReturn(-1);
+        Mockito.when(entity.isStreaming()).thenReturn(true);
+        Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true);
+        final InputStream content = wrapper.getContent();
+        Assert.assertEquals(-1, content.read());
+        Mockito.verify(instream).close();
+        Mockito.verify(execRuntime).releaseConnection();
+    }
+
+    @Test
+    public void testReusableEntityEndOfStreamIOError() throws Exception {
+        Mockito.when(instream.read()).thenReturn(-1);
+        Mockito.when(entity.isStreaming()).thenReturn(true);
+        Mockito.when(execRuntime.isConnectionReusable()).thenReturn(true);
+        Mockito.doThrow(new IOException()).when(instream).close();
+        final InputStream content = wrapper.getContent();
+        try {
+            content.read();
+            Assert.fail("IOException expected");
+        } catch (final IOException ex) {
+        }
+        Mockito.verify(execRuntime, Mockito.atLeast(1)).discardConnection();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRetryExec.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRetryExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRetryExec.java
new file mode 100644
index 0000000..d21a022
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRetryExec.java
@@ -0,0 +1,188 @@
+/*
+ * ====================================================================
+ * 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.ByteArrayOutputStream;
+import java.io.IOException;
+
+import org.apache.hc.client5.http.HttpRequestRetryHandler;
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.NonRepeatableRequestException;
+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.classic.methods.HttpPost;
+import org.apache.hc.client5.http.entity.EntityBuilder;
+import org.apache.hc.client5.http.impl.ExecSupport;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+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 TestRetryExec {
+
+    @Mock
+    private HttpRequestRetryHandler retryHandler;
+    @Mock
+    private ExecRuntime endpoint;
+    @Mock
+    private ExecChain chain;
+
+    private RetryExec retryExec;
+    private HttpHost target;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        retryExec = new RetryExec(retryHandler);
+        target = new HttpHost("localhost", 80);
+    }
+
+    @Test(expected = IOException.class)
+    public void testFundamentals() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpGet originalRequest = new HttpGet("/test");
+        originalRequest.addHeader("header", "this");
+        originalRequest.addHeader("header", "that");
+        final HttpClientContext context = HttpClientContext.create();
+
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenAnswer(new Answer<Object>() {
+
+            @Override
+            public Object answer(final InvocationOnMock invocationOnMock) throws Throwable {
+                final Object[] args = invocationOnMock.getArguments();
+                final ClassicHttpRequest wrapper = (ClassicHttpRequest) args[0];
+                final Header[] headers = wrapper.getAllHeaders();
+                Assert.assertEquals(2, headers.length);
+                Assert.assertEquals("this", headers[0].getValue());
+                Assert.assertEquals("that", headers[1].getValue());
+                wrapper.addHeader("Cookie", "monster");
+                throw new IOException("Ka-boom");
+            }
+
+        });
+        Mockito.when(retryHandler.retryRequest(
+                Mockito.<HttpRequest>any(),
+                Mockito.<IOException>any(),
+                Mockito.eq(1),
+                Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE);
+        final ExecChain.Scope scope = new ExecChain.Scope(route, originalRequest, endpoint, context);
+        final ClassicHttpRequest request = ExecSupport.copy(originalRequest);
+        try {
+            retryExec.execute(request, scope, chain);
+        } catch (final IOException ex) {
+            Mockito.verify(chain, Mockito.times(2)).proceed(
+                    Mockito.<ClassicHttpRequest>any(),
+                    Mockito.same(scope));
+            throw ex;
+        }
+    }
+
+    @Test(expected = IOException.class)
+    public void testAbortedRequest() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpGet originalRequest = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenThrow(new IOException("Ka-boom"));
+        Mockito.when(endpoint.isExecutionAborted()).thenReturn(true);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, originalRequest, endpoint, context);
+        final ClassicHttpRequest request = ExecSupport.copy(originalRequest);
+        try {
+            retryExec.execute(request, scope, chain);
+        } catch (final IOException ex) {
+            Mockito.verify(chain, Mockito.times(1)).proceed(
+                    Mockito.same(request),
+                    Mockito.same(scope));
+            Mockito.verify(retryHandler, Mockito.never()).retryRequest(
+                    Mockito.<HttpRequest>any(),
+                    Mockito.<IOException>any(),
+                    Mockito.anyInt(),
+                    Mockito.<HttpContext>any());
+
+            throw ex;
+        }
+    }
+
+    @Test(expected = NonRepeatableRequestException.class)
+    public void testNonRepeatableRequest() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpPost originalRequest = new HttpPost("/test");
+        originalRequest.setEntity(EntityBuilder.create()
+                .setStream(new ByteArrayInputStream(new byte[]{}))
+                .build());
+        final HttpClientContext context = HttpClientContext.create();
+
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenAnswer(new Answer<Object>() {
+
+            @Override
+            public Object answer(final InvocationOnMock invocationOnMock) throws Throwable {
+                final Object[] args = invocationOnMock.getArguments();
+                final ClassicHttpRequest req = (ClassicHttpRequest) args[0];
+                req.getEntity().writeTo(new ByteArrayOutputStream());
+                throw new IOException("Ka-boom");
+            }
+
+        });
+        Mockito.when(retryHandler.retryRequest(
+                Mockito.<HttpRequest>any(),
+                Mockito.<IOException>any(),
+                Mockito.eq(1),
+                Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE);
+        final ExecChain.Scope scope = new ExecChain.Scope(route, originalRequest, endpoint, context);
+        final ClassicHttpRequest request = ExecSupport.copy(originalRequest);
+        try {
+            retryExec.execute(request, scope, chain);
+        } catch (final IOException ex) {
+            Mockito.verify(chain, Mockito.times(1)).proceed(
+                    Mockito.same(request),
+                    Mockito.same(scope));
+
+            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/TestServiceUnavailableRetryExec.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestServiceUnavailableRetryExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestServiceUnavailableRetryExec.java
new file mode 100644
index 0000000..f6ab7b3
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestServiceUnavailableRetryExec.java
@@ -0,0 +1,148 @@
+/*
+ * ====================================================================
+ * 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 org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.ServiceUnavailableRetryStrategy;
+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.classic.methods.HttpPost;
+import org.apache.hc.client5.http.entity.EntityBuilder;
+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.HttpHost;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.protocol.HttpContext;
+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({"boxing","static-access"}) // test code
+public class TestServiceUnavailableRetryExec {
+
+    @Mock
+    private ServiceUnavailableRetryStrategy retryStrategy;
+    @Mock
+    private ExecChain chain;
+    @Mock
+    private ExecRuntime endpoint;
+
+    private ServiceUnavailableRetryExec retryExec;
+    private HttpHost target;
+
+    @Before
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        retryExec = new ServiceUnavailableRetryExec(retryStrategy);
+        target = new HttpHost("localhost", 80);
+    }
+
+    @Test
+    public void testFundamentals() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final HttpGet request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
+
+        Mockito.when(chain.proceed(
+                Mockito.same(request),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response);
+        Mockito.when(retryStrategy.retryRequest(
+                Mockito.<HttpResponse>any(),
+                Mockito.anyInt(),
+                Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE, Boolean.FALSE);
+        Mockito.when(retryStrategy.getRetryInterval(
+                Mockito.<HttpResponse>any(),
+                Mockito.<HttpContext>any())).thenReturn(0L);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        retryExec.execute(request, scope, chain);
+
+        Mockito.verify(chain, Mockito.times(2)).proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.same(scope));
+        Mockito.verify(response, Mockito.times(1)).close();
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void testStrategyRuntimeException() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+        final ClassicHttpRequest request = new HttpGet("/test");
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response);
+        Mockito.doThrow(new RuntimeException("Ooopsie")).when(retryStrategy).retryRequest(
+                Mockito.<HttpResponse>any(),
+                Mockito.anyInt(),
+                Mockito.<HttpContext>any());
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        try {
+            retryExec.execute(request, scope, chain);
+        } catch (final Exception ex) {
+            Mockito.verify(response).close();
+            throw ex;
+        }
+    }
+
+    @Test
+    public void testNonRepeatableEntityResponseReturnedImmediately() throws Exception {
+        final HttpRoute route = new HttpRoute(target);
+
+        final HttpPost request = new HttpPost("/test");
+        request.setEntity(EntityBuilder.create()
+                .setStream(new ByteArrayInputStream(new byte[]{}))
+                .build());
+        final HttpClientContext context = HttpClientContext.create();
+
+        final ClassicHttpResponse response = Mockito.mock(ClassicHttpResponse.class);
+        Mockito.when(chain.proceed(
+                Mockito.<ClassicHttpRequest>any(),
+                Mockito.<ExecChain.Scope>any())).thenReturn(response);
+        Mockito.when(retryStrategy.retryRequest(
+                Mockito.<HttpResponse>any(),
+                Mockito.anyInt(),
+                Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE, Boolean.FALSE);
+
+        final ExecChain.Scope scope = new ExecChain.Scope(route, request, endpoint, context);
+        final ClassicHttpResponse finalResponse = retryExec.execute(request, scope, chain);
+
+        Assert.assertSame(response, finalResponse);
+        Mockito.verify(response, Mockito.times(0)).close();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-client/blob/6d17126c/httpclient5/src/test/java/org/apache/hc/client5/http/impl/protocol/TestAuthenticationStrategy.java
----------------------------------------------------------------------
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/protocol/TestAuthenticationStrategy.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/protocol/TestAuthenticationStrategy.java
deleted file mode 100644
index 35de833..0000000
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/protocol/TestAuthenticationStrategy.java
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * ====================================================================
- * 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.protocol;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.hc.client5.http.auth.AuthChallenge;
-import org.apache.hc.client5.http.auth.AuthScheme;
-import org.apache.hc.client5.http.auth.AuthSchemeProvider;
-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.AuthSchemes;
-import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.client5.http.impl.auth.BasicScheme;
-import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory;
-import org.apache.hc.client5.http.impl.auth.DigestScheme;
-import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory;
-import org.apache.hc.client5.http.impl.sync.BasicCredentialsProvider;
-import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.core5.http.config.Registry;
-import org.apache.hc.core5.http.config.RegistryBuilder;
-import org.apache.hc.core5.http.message.BasicNameValuePair;
-import org.junit.Assert;
-import org.junit.Test;
-
-/**
- * Simple tests for {@link DefaultAuthenticationStrategy}.
- */
-@SuppressWarnings("boxing") // test code
-public class TestAuthenticationStrategy {
-
-    @Test
-    public void testSelectInvalidInput() throws Exception {
-        final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
-        final HttpClientContext context = HttpClientContext.create();
-        try {
-            authStrategy.select(null, Collections.<String, AuthChallenge>emptyMap(), context);
-            Assert.fail("IllegalArgumentException expected");
-        } catch (final IllegalArgumentException ex) {
-        }
-        try {
-            authStrategy.select(ChallengeType.TARGET, null, context);
-            Assert.fail("IllegalArgumentException expected");
-        } catch (final IllegalArgumentException ex) {
-        }
-        try {
-            authStrategy.select(ChallengeType.TARGET, Collections.<String, AuthChallenge>emptyMap(), null);
-            Assert.fail("IllegalArgumentException expected");
-        } catch (final IllegalArgumentException ex) {
-        }
-    }
-
-    @Test
-    public void testSelectNoSchemeRegistry() throws Exception {
-        final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
-        final HttpClientContext context = HttpClientContext.create();
-
-        final Map<String, AuthChallenge> challenges = new HashMap<>();
-        challenges.put("basic", new AuthChallenge(ChallengeType.TARGET, "Basic",
-                new BasicNameValuePair("realm", "test")));
-        challenges.put("digest", new AuthChallenge(ChallengeType.TARGET, "Digest",
-                new BasicNameValuePair("realm", "test"), new BasicNameValuePair("nonce", "1234")));
-
-        final List<AuthScheme> authSchemes = authStrategy.select(ChallengeType.TARGET, challenges, context);
-        Assert.assertNotNull(authSchemes);
-        Assert.assertEquals(0, authSchemes.size());
-    }
-
-    @Test
-    public void testUnsupportedScheme() throws Exception {
-        final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
-        final HttpClientContext context = HttpClientContext.create();
-
-        final Map<String, AuthChallenge> challenges = new HashMap<>();
-        challenges.put("basic", new AuthChallenge(ChallengeType.TARGET, "Basic",
-                new BasicNameValuePair("realm", "realm1")));
-        challenges.put("digest", new AuthChallenge(ChallengeType.TARGET, "Digest",
-                new BasicNameValuePair("realm", "realm2"), new BasicNameValuePair("nonce", "1234")));
-        challenges.put("whatever", new AuthChallenge(ChallengeType.TARGET, "Whatever",
-                new BasicNameValuePair("realm", "realm3")));
-
-        final Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
-            .register("basic", new BasicSchemeFactory())
-            .register("digest", new DigestSchemeFactory()).build();
-        context.setAuthSchemeRegistry(authSchemeRegistry);
-
-        final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
-        credentialsProvider.setCredentials(new AuthScope("somehost", 80),
-                new UsernamePasswordCredentials("user", "pwd".toCharArray()));
-        context.setCredentialsProvider(credentialsProvider);
-
-        final List<AuthScheme> authSchemes = authStrategy.select(ChallengeType.TARGET, challenges, context);
-        Assert.assertNotNull(authSchemes);
-        Assert.assertEquals(2, authSchemes.size());
-        final AuthScheme authScheme1 = authSchemes.get(0);
-        Assert.assertTrue(authScheme1 instanceof DigestScheme);
-        final AuthScheme authScheme2 = authSchemes.get(1);
-        Assert.assertTrue(authScheme2 instanceof BasicScheme);
-    }
-
-    @Test
-    public void testCustomAuthPreference() throws Exception {
-        final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
-        final RequestConfig config = RequestConfig.custom()
-            .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
-            .build();
-
-        final HttpClientContext context = HttpClientContext.create();
-
-        final Map<String, AuthChallenge> challenges = new HashMap<>();
-        challenges.put("basic", new AuthChallenge(ChallengeType.TARGET, "Basic",
-                new BasicNameValuePair("realm", "realm1")));
-        challenges.put("digest", new AuthChallenge(ChallengeType.TARGET, "Digest",
-                new BasicNameValuePair("realm", "realm2"), new BasicNameValuePair("nonce", "1234")));
-
-        final Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
-            .register("basic", new BasicSchemeFactory())
-            .register("digest", new DigestSchemeFactory()).build();
-        context.setAuthSchemeRegistry(authSchemeRegistry);
-        context.setRequestConfig(config);
-
-        final BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
-        credentialsProvider.setCredentials(new AuthScope("somehost", 80),
-                new UsernamePasswordCredentials("user", "pwd".toCharArray()));
-        context.setCredentialsProvider(credentialsProvider);
-
-        final List<AuthScheme> authSchemes = authStrategy.select(ChallengeType.TARGET, challenges, context);
-        Assert.assertNotNull(authSchemes);
-        Assert.assertEquals(1, authSchemes.size());
-        final AuthScheme authScheme1 = authSchemes.get(0);
-        Assert.assertTrue(authScheme1 instanceof BasicScheme);
-    }
-
-}