You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2017/05/02 18:22:59 UTC

svn commit: r1793567 - in /httpcomponents/httpclient/trunk: httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/ httpclient5/src/examples/org/apache/hc/client5/http/examples/ httpclient5/src/main/java/org/apache/hc/client5/http/auth/u...

Author: olegk
Date: Tue May  2 18:22:59 2017
New Revision: 1793567

URL: http://svn.apache.org/viewvc?rev=1793567&view=rev
Log:
Improved protocol handling in the async request execution chain; implemented cookie processing and authentication handling

Added:
    httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestClientAuthentication.java   (with props)
    httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientAuthentication.java   (with props)
    httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/AuthSupport.java   (contents, props changed)
      - copied, changed from r1793325, httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/auth/util/CredentialSupport.java
Removed:
    httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/auth/util/CredentialSupport.java
Modified:
    httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java
    httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java
    httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java
    httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
    httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncClient.java
    httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ProtocolExec.java

Modified: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java?rev=1793567&r1=1793566&r2=1793567&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/IntegrationTestBase.java Tue May  2 18:22:59 2017
@@ -33,6 +33,9 @@ import org.apache.hc.client5.http.impl.a
 import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.config.H1Config;
+import org.apache.hc.core5.http.impl.HttpProcessors;
+import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.io.ShutdownType;
 import org.apache.hc.core5.reactor.ListenerEndpoint;
 import org.junit.Rule;
@@ -69,8 +72,8 @@ public abstract class IntegrationTestBas
 
     };
 
-    public HttpHost start() throws Exception {
-        server.start();
+    public HttpHost start(final HttpProcessor httpProcessor, final H1Config h1Config) throws Exception {
+        server.start(httpProcessor, h1Config);
         final ListenerEndpoint listener = server.listen(new InetSocketAddress(0));
         httpclient = clientBuilder.build();
         httpclient.start();
@@ -79,4 +82,8 @@ public abstract class IntegrationTestBas
         return new HttpHost("localhost", address.getPort(), scheme.name());
     }
 
+    public HttpHost start() throws Exception {
+        return start(HttpProcessors.server(), H1Config.DEFAULT);
+    }
+
 }

Modified: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java?rev=1793567&r1=1793566&r2=1793567&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncRedirects.java Tue May  2 18:22:59 2017
@@ -70,7 +70,6 @@ import org.apache.hc.core5.reactor.Liste
 import org.apache.hc.core5.testing.nio.Http2TestServer;
 import org.apache.hc.core5.util.TimeValue;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -641,7 +640,7 @@ public class TestAsyncRedirects extends
         }
     }
 
-    @Test @Ignore
+    @Test
     public void testRedirectWithCookie() throws Exception {
         server.register("*", new Supplier<AsyncServerExchangeHandler>() {
 

Added: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestClientAuthentication.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestClientAuthentication.java?rev=1793567&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestClientAuthentication.java (added)
+++ httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestClientAuthentication.java Tue May  2 18:22:59 2017
@@ -0,0 +1,627 @@
+/*
+ * ====================================================================
+ * 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.testing.async;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.async.methods.SimpleRequestProducer;
+import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer;
+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.Credentials;
+import org.apache.hc.client5.http.auth.CredentialsStore;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.impl.auth.BasicScheme;
+import org.apache.hc.client5.http.impl.protocol.DefaultAuthenticationStrategy;
+import org.apache.hc.client5.http.impl.sync.BasicCredentialsProvider;
+import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.client5.http.sync.methods.HttpGet;
+import org.apache.hc.client5.testing.auth.RequestBasicAuth;
+import org.apache.hc.client5.testing.auth.ResponseBasicUnauthorized;
+import org.apache.hc.core5.function.Supplier;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.EntityDetails;
+import org.apache.hc.core5.http.HeaderElements;
+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.HttpResponseInterceptor;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.config.H1Config;
+import org.apache.hc.core5.http.config.Registry;
+import org.apache.hc.core5.http.config.RegistryBuilder;
+import org.apache.hc.core5.http.impl.HttpProcessors;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.apache.hc.core5.http.nio.AsyncResponseProducer;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
+import org.apache.hc.core5.http.nio.support.BasicAsyncResponseProducer;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TestClientAuthentication extends IntegrationTestBase {
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> protocols() {
+        return Arrays.asList(new Object[][]{
+                {URIScheme.HTTP},
+                {URIScheme.HTTPS},
+        });
+    }
+
+    public TestClientAuthentication(final URIScheme scheme) {
+        super(scheme);
+    }
+
+    @Override
+    public HttpHost start() throws Exception {
+        return super.start(HttpProcessors.customServer(null)
+                .add(new RequestBasicAuth())
+                .add(new ResponseBasicUnauthorized())
+                .build(),
+                H1Config.DEFAULT);
+    }
+
+    static class AuthHandler extends AbstractSimpleServerExchangeHandler {
+
+        private final boolean keepAlive;
+
+        AuthHandler(final boolean keepAlive) {
+            super();
+            this.keepAlive = keepAlive;
+        }
+
+        AuthHandler() {
+            this(true);
+        }
+
+        @Override
+        protected SimpleHttpResponse handle(
+                final SimpleHttpRequest request,
+                final HttpCoreContext context) throws HttpException {
+            final String creds = (String) context.getAttribute("creds");
+            final SimpleHttpResponse response;
+            if (creds == null || !creds.equals("test:test")) {
+                response = new SimpleHttpResponse(HttpStatus.SC_UNAUTHORIZED);
+            } else {
+                response = new SimpleHttpResponse(HttpStatus.SC_OK, "success", ContentType.TEXT_PLAIN);
+            }
+            response.setHeader(HttpHeaders.CONNECTION, this.keepAlive ? HeaderElements.KEEP_ALIVE : HeaderElements.CLOSE);
+            return response;
+        }
+
+    }
+
+    static class TestCredentialsProvider implements CredentialsStore {
+
+        private final Credentials creds;
+        private AuthScope authscope;
+
+        TestCredentialsProvider(final Credentials creds) {
+            super();
+            this.creds = creds;
+        }
+
+        @Override
+        public void clear() {
+        }
+
+        @Override
+        public Credentials getCredentials(final AuthScope authscope, final HttpContext context) {
+            this.authscope = authscope;
+            return this.creds;
+        }
+
+        @Override
+        public void setCredentials(final AuthScope authscope, final Credentials credentials) {
+        }
+
+        public AuthScope getAuthScope() {
+            return this.authscope;
+        }
+
+    }
+
+    @Test
+    public void testBasicAuthenticationNoCreds() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AuthHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(null);
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationFailure() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AuthHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccess() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AuthHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationSuccessNonPersistentConnection() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AuthHandler(false);
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationExpectationFailure() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AuthHandler() {
+
+                    @Override
+                    protected AsyncResponseProducer verify(
+                            final HttpRequest request,
+                            final HttpContext context) throws IOException, HttpException {
+
+                        final String creds = (String) context.getAttribute("creds");
+                        if (creds == null || !creds.equals("test:test")) {
+                            return new BasicAsyncResponseProducer(new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED),
+                                    new StringAsyncEntityProducer("Unauthorized"));
+                        } else {
+                            return null;
+                        }
+                    }
+
+                };
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "all-wrong".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.put(target, "/", "Some important stuff", ContentType.TEXT_PLAIN), context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
+    }
+
+    @Test
+    public void testBasicAuthenticationExpectationSuccess() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AuthHandler() {
+
+                    @Override
+                    protected AsyncResponseProducer verify(
+                            final HttpRequest request,
+                            final HttpContext context) throws IOException, HttpException {
+
+                        final String creds = (String) context.getAttribute("creds");
+                        if (creds == null || !creds.equals("test:test")) {
+                            return new BasicAsyncResponseProducer(new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED),
+                                    new StringAsyncEntityProducer("Unauthorized"));
+                        } else {
+                            return null;
+                        }
+                    }
+
+                };
+            }
+
+        });
+        final HttpHost target = start();
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.put(target, "/", "Some important stuff", ContentType.TEXT_PLAIN), context, null);
+        final HttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+    @Test
+    public void testBasicAuthenticationCredentialsCaching() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AuthHandler();
+            }
+
+        });
+
+        final AtomicLong count = new AtomicLong(0);
+        this.clientBuilder.setTargetAuthenticationStrategy(new DefaultAuthenticationStrategy() {
+
+            @Override
+            public List<AuthScheme> select(
+                    final ChallengeType challengeType,
+                    final Map<String, AuthChallenge> challenges,
+                    final HttpContext context) {
+                count.incrementAndGet();
+                return super.select(challengeType, challenges, context);
+            }
+        });
+        final HttpHost target = start();
+
+        final BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
+        credsProvider.setCredentials(AuthScope.ANY,
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future1 = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response1 = future1.get();
+        Assert.assertNotNull(response1);
+        Assert.assertEquals(HttpStatus.SC_OK, response1.getCode());
+
+        final Future<SimpleHttpResponse> future2 = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final HttpResponse response2 = future2.get();
+        Assert.assertNotNull(response2);
+        Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
+
+        Assert.assertEquals(1, count.get());
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRequestSuccess() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AuthHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target.getSchemeName() + "://test:test@" +  target.toHostString() + "/"), context, null);
+        final SimpleHttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("success", response.getBody());
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRequestFailure() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AuthHandler();
+            }
+
+        });
+        final HttpHost target = start();
+
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target.getSchemeName() + "://test:all-worng@" +  target.toHostString() + "/"), context, null);
+        final SimpleHttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getCode());
+    }
+
+    @Test
+    public void testAuthenticationUserinfoInRedirectSuccess() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AuthHandler();
+            }
+
+        });
+        final HttpHost target = start();
+        server.register("/thatway", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+                return new AbstractSimpleServerExchangeHandler() {
+
+                    @Override
+                    protected SimpleHttpResponse handle(
+                            final SimpleHttpRequest request, final HttpCoreContext context) throws HttpException {
+                        final SimpleHttpResponse response = new SimpleHttpResponse(HttpStatus.SC_MOVED_PERMANENTLY);
+                        response.addHeader(new BasicHeader("Location", target.getSchemeName() + "://test:test@" + target.toHostString() + "/"));
+                        return response;
+                    }
+                };
+            }
+
+        });
+
+        final HttpClientContext context = HttpClientContext.create();
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                SimpleHttpRequest.get(target.getSchemeName() + "://test:test@" +  target.toHostString() + "/thatway"), context, null);
+        final SimpleHttpResponse response = future.get();
+
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("success", response.getBody());
+    }
+
+    @Test
+    public void testReauthentication() throws Exception {
+        final AtomicLong count = new AtomicLong(0);
+
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+
+                return new AbstractSimpleServerExchangeHandler() {
+
+                    @Override
+                    protected SimpleHttpResponse handle(
+                            final SimpleHttpRequest request,
+                            final HttpCoreContext context) throws HttpException {
+                        final String creds = (String) context.getAttribute("creds");
+                        if (creds == null || !creds.equals("test:test")) {
+                            return new SimpleHttpResponse(HttpStatus.SC_UNAUTHORIZED);
+                        } else {
+                            // Make client re-authenticate on each fourth request
+                            if (count.incrementAndGet() % 4 == 0) {
+                                return new SimpleHttpResponse(HttpStatus.SC_UNAUTHORIZED);
+                            } else {
+                                return new SimpleHttpResponse(HttpStatus.SC_OK, "success", ContentType.TEXT_PLAIN);
+                            }
+                        }
+                    }
+
+                };
+            }
+
+        });
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+
+        final Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
+                .register("MyBasic", new AuthSchemeProvider() {
+
+                    @Override
+                    public AuthScheme create(final HttpContext context) {
+                        return new BasicScheme() {
+
+                            @Override
+                            public String getName() {
+                                return "MyBasic";
+                            }
+
+                        };
+                    }
+
+                })
+                .build();
+        this.clientBuilder.setDefaultAuthSchemeRegistry(authSchemeRegistry);
+
+        final HttpHost target = start(HttpProcessors.customServer(null)
+                        .add(new RequestBasicAuth())
+                        .add(new HttpResponseInterceptor() {
+
+                            @Override
+                            public void process(
+                                    final HttpResponse response,
+                                    final EntityDetails entityDetails,
+                                    final HttpContext context) throws HttpException, IOException {
+                                if (response.getCode() == HttpStatus.SC_UNAUTHORIZED) {
+                                    response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "MyBasic realm=\"test realm\"");
+                                }
+                            }
+
+                        })
+                        .build(),
+                H1Config.DEFAULT);
+
+        final RequestConfig config = RequestConfig.custom()
+                .setTargetPreferredAuthSchemes(Arrays.asList("MyBasic"))
+                .build();
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        for (int i = 0; i < 10; i++) {
+            final HttpGet httpget = new HttpGet("/");
+            httpget.setConfig(config);
+            final Future<SimpleHttpResponse> future = httpclient.execute(
+                    new SimpleRequestProducer(SimpleHttpRequest.get(target, "/"), config),
+                    new SimpleResponseConsumer(),
+                    context, null);
+            final SimpleHttpResponse response = future.get();
+            Assert.assertNotNull(response);
+            Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        }
+    }
+
+    @Test
+    public void testAuthenticationFallback() throws Exception {
+        server.register("*", new Supplier<AsyncServerExchangeHandler>() {
+
+            @Override
+            public AsyncServerExchangeHandler get() {
+
+                return new AuthHandler();
+            }
+
+        });
+
+        final HttpHost target = start(HttpProcessors.customServer(null)
+                        .add(new RequestBasicAuth())
+                        .add(new HttpResponseInterceptor() {
+
+                            @Override
+                            public void process(
+                                    final HttpResponse response,
+                                    final EntityDetails entityDetails,
+                                    final HttpContext context) throws HttpException, IOException {
+                                if (response.getCode() == HttpStatus.SC_UNAUTHORIZED) {
+                                    response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Digest realm=\"test realm\" invalid");
+                                    response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"test realm\"");
+                                }
+                            }
+
+                        })
+                        .build(),
+                H1Config.DEFAULT);
+
+
+        final TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+                new UsernamePasswordCredentials("test", "test".toCharArray()));
+        final HttpClientContext context = HttpClientContext.create();
+        context.setCredentialsProvider(credsProvider);
+
+        final Future<SimpleHttpResponse> future = httpclient.execute(SimpleHttpRequest.get(target, "/"), context, null);
+        final SimpleHttpResponse response = future.get();
+        Assert.assertNotNull(response);
+        Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
+        Assert.assertEquals("success", response.getBody());
+        final AuthScope authscope = credsProvider.getAuthScope();
+        Assert.assertNotNull(authscope);
+        Assert.assertEquals("test realm", authscope.getRealm());
+    }
+
+}
\ No newline at end of file

Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestClientAuthentication.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestClientAuthentication.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestClientAuthentication.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientAuthentication.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientAuthentication.java?rev=1793567&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientAuthentication.java (added)
+++ httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientAuthentication.java Tue May  2 18:22:59 2017
@@ -0,0 +1,88 @@
+/*
+ * ====================================================================
+ * 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.examples;
+
+import java.util.concurrent.Future;
+
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.auth.AuthScope;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
+import org.apache.hc.client5.http.impl.sync.BasicCredentialsProvider;
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.io.ShutdownType;
+
+/**
+ * A simple example that uses HttpClient to execute an HTTP request against
+ * a target site that requires user authentication.
+ */
+public class AsyncClientAuthentication {
+
+    public static void main(String[] args) throws Exception {
+        BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
+        credsProvider.setCredentials(
+                new AuthScope("httpbin.org", 80),
+                new UsernamePasswordCredentials("user", "passwd".toCharArray()));
+        CloseableHttpAsyncClient httpclient = HttpAsyncClients.custom()
+                .setDefaultCredentialsProvider(credsProvider)
+                .build();
+        httpclient.start();
+
+        final String requestUri = "http://httpbin.org/basic-auth/user/passwd";
+        SimpleHttpRequest httpget = SimpleHttpRequest.get(requestUri);
+
+        System.out.println("Executing request " + requestUri);
+        final Future<SimpleHttpResponse> future = httpclient.execute(
+                httpget,
+                new FutureCallback<SimpleHttpResponse>() {
+
+                    @Override
+                    public void completed(final SimpleHttpResponse response) {
+                        System.out.println(requestUri + "->" + response.getCode());
+                        System.out.println(response.getBody());
+                    }
+
+                    @Override
+                    public void failed(final Exception ex) {
+                        System.out.println(requestUri + "->" + ex);
+                    }
+
+                    @Override
+                    public void cancelled() {
+                        System.out.println(requestUri + " cancelled");
+                    }
+
+                });
+        future.get();
+
+        System.out.println("Shutting down");
+        httpclient.shutdown(ShutdownType.GRACEFUL);
+
+    }
+}

Propchange: httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientAuthentication.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientAuthentication.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient5/src/examples/org/apache/hc/client5/http/examples/AsyncClientAuthentication.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Copied: httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/AuthSupport.java (from r1793325, httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/auth/util/CredentialSupport.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/AuthSupport.java?p2=httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/AuthSupport.java&p1=httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/auth/util/CredentialSupport.java&r1=1793325&r2=1793567&rev=1793567&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/auth/util/CredentialSupport.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/AuthSupport.java Tue May  2 18:22:59 2017
@@ -25,19 +25,22 @@
  *
  */
 
-package org.apache.hc.client5.http.auth.util;
+package org.apache.hc.client5.http.impl;
 
+import org.apache.hc.client5.http.HttpRoute;
 import org.apache.hc.client5.http.auth.AuthScope;
 import org.apache.hc.client5.http.auth.CredentialsStore;
 import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
 import org.apache.hc.client5.http.config.AuthSchemes;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.net.URIAuthority;
 import org.apache.hc.core5.util.Args;
 
 /**
  * @since 5.0
  */
-public class CredentialSupport {
+public class AuthSupport {
 
     public static void extractFromAuthority(
             final URIAuthority authority,
@@ -65,4 +68,20 @@ public class CredentialSupport {
                 new UsernamePasswordCredentials(userName, password));
     }
 
+    public static HttpHost resolveAuthTarget(final HttpRequest request, final HttpRoute route) {
+        Args.notNull(request, "Request");
+        Args.notNull(route, "Route");
+        final URIAuthority authority = request.getAuthority();
+        final String scheme = request.getScheme();
+        final HttpHost target = authority != null ? new HttpHost(authority, scheme) : route.getTargetHost();;
+        if (target.getPort() < 0) {
+            return new HttpHost(
+                    target.getHostName(),
+                    route.getTargetHost().getPort(),
+                    target.getSchemeName());
+        } else {
+            return target;
+        }
+    }
+
 }

Propchange: httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/AuthSupport.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/AuthSupport.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/AuthSupport.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java?rev=1793567&r1=1793566&r2=1793567&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java Tue May  2 18:22:59 2017
@@ -27,31 +27,74 @@
 package org.apache.hc.client5.http.impl.async;
 
 import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.StandardMethods;
 import org.apache.hc.client5.http.async.AsyncExecCallback;
 import org.apache.hc.client5.http.async.AsyncExecChain;
 import org.apache.hc.client5.http.async.AsyncExecChainHandler;
+import org.apache.hc.client5.http.async.AsyncExecRuntime;
+import org.apache.hc.client5.http.auth.AuthExchange;
+import org.apache.hc.client5.http.auth.ChallengeType;
 import org.apache.hc.client5.http.auth.CredentialsProvider;
 import org.apache.hc.client5.http.auth.CredentialsStore;
-import org.apache.hc.client5.http.auth.util.CredentialSupport;
+import org.apache.hc.client5.http.impl.AuthSupport;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.impl.auth.HttpAuthenticator;
+import org.apache.hc.client5.http.protocol.AuthenticationStrategy;
 import org.apache.hc.client5.http.protocol.HttpClientContext;
+import org.apache.hc.client5.http.utils.URIUtils;
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.EntityDetails;
+import org.apache.hc.core5.http.Header;
 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.ProtocolException;
 import org.apache.hc.core5.http.nio.AsyncDataConsumer;
 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.net.URIAuthority;
-
+import org.apache.hc.core5.util.Args;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+/**
+ * Request executor in the request execution chain that is responsible
+ * for implementation of HTTP specification requirements.
+ * <p>
+ * Further responsibilities such as communication with the opposite
+ * endpoint is delegated to the next executor in the request execution
+ * chain.
+ *
+ * @since 5.0
+ */
+@Contract(threading = ThreadingBehavior.IMMUTABLE)
 class AsyncProtocolExec implements AsyncExecChainHandler {
 
-    private final HttpProcessor httpProcessor;
+    private final Logger log = LogManager.getLogger(getClass());
 
-    AsyncProtocolExec(final HttpProcessor httpProcessor) {
-        this.httpProcessor = httpProcessor;
+    private final HttpProcessor httpProcessor;
+    private final AuthenticationStrategy targetAuthStrategy;
+    private final AuthenticationStrategy proxyAuthStrategy;
+    private final HttpAuthenticator authenticator;
+
+    AsyncProtocolExec(
+            final HttpProcessor httpProcessor,
+            final AuthenticationStrategy targetAuthStrategy,
+            final AuthenticationStrategy proxyAuthStrategy) {
+        this.httpProcessor = Args.notNull(httpProcessor, "HTTP protocol processor");
+        this.targetAuthStrategy = Args.notNull(targetAuthStrategy, "Target authentication strategy");
+        this.proxyAuthStrategy = Args.notNull(proxyAuthStrategy, "Proxy authentication strategy");
+        this.authenticator = new HttpAuthenticator();
     }
 
     @Override
@@ -63,32 +106,121 @@ class AsyncProtocolExec implements Async
             final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {
         final HttpRoute route = scope.route;
         final HttpClientContext clientContext = scope.clientContext;
+
+        if (route.getProxyHost() != null && !route.isTunnelled()) {
+            try {
+                URI uri = request.getUri();
+                if (!uri.isAbsolute()) {
+                    uri = URIUtils.rewriteURI(uri, route.getTargetHost(), true);
+                } else {
+                    uri = URIUtils.rewriteURI(uri);
+                }
+                request.setPath(uri.toASCIIString());
+            } catch (final URISyntaxException ex) {
+                throw new ProtocolException("Invalid request URI: " + request.getRequestUri(), ex);
+            }
+        }
+
         final URIAuthority authority = request.getAuthority();
         if (authority != null) {
             final CredentialsProvider credsProvider = clientContext.getCredentialsProvider();
             if (credsProvider instanceof CredentialsStore) {
-                CredentialSupport.extractFromAuthority(authority, (CredentialsStore) credsProvider);
+                AuthSupport.extractFromAuthority(authority, (CredentialsStore) credsProvider);
             }
         }
 
+        final AtomicBoolean challenged = new AtomicBoolean(false);
+        internalExecute(challenged, request, entityProducer, scope, chain, asyncExecCallback);
+    }
+
+    private void internalExecute(
+            final AtomicBoolean challenged,
+            final HttpRequest request,
+            final AsyncEntityProducer entityProducer,
+            final AsyncExecChain.Scope scope,
+            final AsyncExecChain chain,
+            final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {
+        final HttpRoute route = scope.route;
+        final HttpClientContext clientContext = scope.clientContext;
+        final AsyncExecRuntime execRuntime = scope.execRuntime;
+
+        final HttpHost target = route.getTargetHost();
+        final HttpHost proxy = route.getProxyHost();
+
+        final AuthExchange targetAuthExchange = clientContext.getAuthExchange(target);
+        final AuthExchange proxyAuthExchange = proxy != null ? clientContext.getAuthExchange(proxy) : new AuthExchange();
+
         clientContext.setAttribute(HttpClientContext.HTTP_ROUTE, route);
         clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
         httpProcessor.process(request, entityProducer, clientContext);
 
+        if (!request.containsHeader(HttpHeaders.AUTHORIZATION)) {
+            if (log.isDebugEnabled()) {
+                log.debug("Target auth state: " + targetAuthExchange.getState());
+            }
+            authenticator.addAuthResponse(target, ChallengeType.TARGET, request, targetAuthExchange, clientContext);
+        }
+        if (!request.containsHeader(HttpHeaders.PROXY_AUTHORIZATION) && !route.isTunnelled()) {
+            if (log.isDebugEnabled()) {
+                log.debug("Proxy auth state: " + proxyAuthExchange.getState());
+            }
+            authenticator.addAuthResponse(proxy, ChallengeType.PROXY, request, proxyAuthExchange, clientContext);
+        }
+
         chain.proceed(request, entityProducer, scope, new AsyncExecCallback() {
 
             @Override
             public AsyncDataConsumer handleResponse(
                     final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
+
                 clientContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
                 httpProcessor.process(response, entityDetails, clientContext);
 
-                return asyncExecCallback.handleResponse(response, entityDetails);
+                if (request.getMethod().equalsIgnoreCase(StandardMethods.TRACE.name())) {
+                    // Do not perform authentication for TRACE request
+                    return asyncExecCallback.handleResponse(response, entityDetails);
+                }
+                if (needAuthentication(targetAuthExchange, proxyAuthExchange, route, request, response, clientContext)) {
+                    challenged.set(true);
+                    return null;
+                } else {
+                    challenged.set(false);
+                    return asyncExecCallback.handleResponse(response, entityDetails);
+                }
             }
 
             @Override
             public void completed() {
-                asyncExecCallback.completed();
+                if (execRuntime.isConnected()) {
+                    if (proxyAuthExchange.getState() == AuthExchange.State.SUCCESS
+                            && proxyAuthExchange.getAuthScheme() != null
+                            && proxyAuthExchange.getAuthScheme().isConnectionBased()) {
+                        log.debug("Resetting proxy auth state");
+                        proxyAuthExchange.reset();
+                    }
+                    if (targetAuthExchange.getState() == AuthExchange.State.SUCCESS
+                            && targetAuthExchange.getAuthScheme() != null
+                            && targetAuthExchange.getAuthScheme().isConnectionBased()) {
+                        log.debug("Resetting target auth state");
+                        targetAuthExchange.reset();
+                    }
+                }
+
+                if (challenged.get()) {
+                    // Reset request headers
+                    final HttpRequest original = scope.originalRequest;
+                    request.setHeaders();
+                    for (final Iterator<Header> it = original.headerIterator(); it.hasNext(); ) {
+                        request.addHeader(it.next());
+                    }
+                    try {
+                        internalExecute(challenged, request, entityProducer, scope, chain, asyncExecCallback);
+                    } catch (final HttpException | IOException ex) {
+                        asyncExecCallback.failed(ex);
+                    }
+                } else {
+                    asyncExecCallback.completed();
+                }
             }
 
             @Override
@@ -99,4 +231,37 @@ class AsyncProtocolExec implements Async
         });
     }
 
+    private boolean needAuthentication(
+            final AuthExchange targetAuthExchange,
+            final AuthExchange proxyAuthExchange,
+            final HttpRoute route,
+            final HttpRequest request,
+            final HttpResponse response,
+            final HttpClientContext context) {
+        final RequestConfig config = context.getRequestConfig();
+        if (config.isAuthenticationEnabled()) {
+            final HttpHost target = AuthSupport.resolveAuthTarget(request, route);
+            final boolean targetAuthRequested = authenticator.isChallenged(
+                    target, ChallengeType.TARGET, response, targetAuthExchange, context);
+
+            HttpHost proxy = route.getProxyHost();
+            // if proxy is not set use target host instead
+            if (proxy == null) {
+                proxy = route.getTargetHost();
+            }
+            final boolean proxyAuthRequested = authenticator.isChallenged(
+                    proxy, ChallengeType.PROXY, response, proxyAuthExchange, context);
+
+            if (targetAuthRequested) {
+                return authenticator.prepareAuthResponse(target, ChallengeType.TARGET, response,
+                        targetAuthStrategy, targetAuthExchange, context);
+            }
+            if (proxyAuthRequested) {
+                return authenticator.prepareAuthResponse(proxy, ChallengeType.PROXY, response,
+                        proxyAuthStrategy, proxyAuthExchange, context);
+            }
+        }
+        return false;
+    }
+
 }

Modified: httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java?rev=1793567&r1=1793566&r2=1793567&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java Tue May  2 18:22:59 2017
@@ -37,25 +37,46 @@ import java.util.List;
 
 import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
 import org.apache.hc.client5.http.SchemePortResolver;
+import org.apache.hc.client5.http.SystemDefaultDnsResolver;
 import org.apache.hc.client5.http.async.AsyncExecChainHandler;
+import org.apache.hc.client5.http.auth.AuthSchemeProvider;
+import org.apache.hc.client5.http.auth.CredentialsProvider;
+import org.apache.hc.client5.http.config.AuthSchemes;
 import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.cookie.BasicCookieStore;
+import org.apache.hc.client5.http.cookie.CookieSpecProvider;
+import org.apache.hc.client5.http.cookie.CookieStore;
 import org.apache.hc.client5.http.impl.DefaultConnectionKeepAliveStrategy;
 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
 import org.apache.hc.client5.http.impl.DefaultUserTokenHandler;
 import org.apache.hc.client5.http.impl.IdleConnectionEvictor;
 import org.apache.hc.client5.http.impl.NamedElementChain;
 import org.apache.hc.client5.http.impl.NoopUserTokenHandler;
+import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory;
+import org.apache.hc.client5.http.impl.auth.CredSspSchemeFactory;
+import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory;
+import org.apache.hc.client5.http.impl.auth.KerberosSchemeFactory;
+import org.apache.hc.client5.http.impl.auth.NTLMSchemeFactory;
+import org.apache.hc.client5.http.impl.auth.SPNegoSchemeFactory;
+import org.apache.hc.client5.http.impl.auth.SystemDefaultCredentialsProvider;
 import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.impl.protocol.DefaultAuthenticationStrategy;
 import org.apache.hc.client5.http.impl.protocol.DefaultRedirectStrategy;
 import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
 import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner;
 import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
+import org.apache.hc.client5.http.impl.sync.BasicCredentialsProvider;
 import org.apache.hc.client5.http.impl.sync.ChainElements;
+import org.apache.hc.client5.http.impl.sync.CookieSpecRegistries;
 import org.apache.hc.client5.http.impl.sync.DefaultHttpRequestRetryHandler;
 import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
+import org.apache.hc.client5.http.protocol.AuthenticationStrategy;
 import org.apache.hc.client5.http.protocol.RedirectStrategy;
+import org.apache.hc.client5.http.protocol.RequestAddCookies;
+import org.apache.hc.client5.http.protocol.RequestAuthCache;
 import org.apache.hc.client5.http.protocol.RequestDefaultHeaders;
 import org.apache.hc.client5.http.protocol.RequestExpectContinue;
+import org.apache.hc.client5.http.protocol.ResponseProcessCookies;
 import org.apache.hc.client5.http.protocol.UserTokenHandler;
 import org.apache.hc.client5.http.routing.HttpRoutePlanner;
 import org.apache.hc.client5.http.sync.HttpRequestRetryHandler;
@@ -72,11 +93,14 @@ import org.apache.hc.core5.http.HttpResp
 import org.apache.hc.core5.http.HttpResponseInterceptor;
 import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.config.H1Config;
+import org.apache.hc.core5.http.config.Lookup;
+import org.apache.hc.core5.http.config.RegistryBuilder;
 import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
 import org.apache.hc.core5.http.nio.AsyncPushConsumer;
 import org.apache.hc.core5.http.nio.HandlerFactory;
 import org.apache.hc.core5.http.nio.command.ShutdownCommand;
 import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.http.protocol.HttpProcessorBuilder;
 import org.apache.hc.core5.http.protocol.RequestUserAgent;
 import org.apache.hc.core5.http2.HttpVersionPolicy;
@@ -179,6 +203,8 @@ public class HttpAsyncClientBuilder {
     private SchemePortResolver schemePortResolver;
     private ConnectionKeepAliveStrategy keepAliveStrategy;
     private UserTokenHandler userTokenHandler;
+    private AuthenticationStrategy targetAuthStrategy;
+    private AuthenticationStrategy proxyAuthStrategy;
 
     private LinkedList<RequestInterceptorEntry> requestInterceptors;
     private LinkedList<ResponseInterceptorEntry> responseInterceptors;
@@ -190,6 +216,11 @@ public class HttpAsyncClientBuilder {
 
     private ConnectionReuseStrategy reuseStrategy;
 
+    private Lookup<AuthSchemeProvider> authSchemeRegistry;
+    private Lookup<CookieSpecProvider> cookieSpecRegistry;
+    private CookieStore cookieStore;
+    private CredentialsProvider credentialsProvider;
+
     private String userAgent;
     private HttpHost proxy;
     private Collection<? extends Header> defaultHeaders;
@@ -201,6 +232,8 @@ public class HttpAsyncClientBuilder {
     private boolean systemProperties;
     private boolean automaticRetriesDisabled;
     private boolean redirectHandlingDisabled;
+    private boolean cookieManagementDisabled;
+    private boolean authCachingDisabled;
     private boolean connectionStateDisabled;
 
     private List<Closeable> closeables;
@@ -308,6 +341,26 @@ public class HttpAsyncClientBuilder {
     }
 
     /**
+     * Assigns {@link AuthenticationStrategy} instance for target
+     * host authentication.
+     */
+    public final HttpAsyncClientBuilder setTargetAuthenticationStrategy(
+            final AuthenticationStrategy targetAuthStrategy) {
+        this.targetAuthStrategy = targetAuthStrategy;
+        return this;
+    }
+
+    /**
+     * Assigns {@link AuthenticationStrategy} instance for proxy
+     * authentication.
+     */
+    public final HttpAsyncClientBuilder setProxyAuthenticationStrategy(
+            final AuthenticationStrategy proxyAuthStrategy) {
+        this.proxyAuthStrategy = proxyAuthStrategy;
+        return this;
+    }
+
+    /**
      * Adds this protocol interceptor to the head of the protocol processing list.
      */
     public final HttpAsyncClientBuilder addRequestInterceptorFirst(final HttpResponseInterceptor interceptor) {
@@ -463,6 +516,45 @@ public class HttpAsyncClientBuilder {
     }
 
     /**
+     * Assigns default {@link CredentialsProvider} instance which will be used
+     * for request execution if not explicitly set in the client execution
+     * context.
+     */
+    public final HttpAsyncClientBuilder setDefaultCredentialsProvider(final CredentialsProvider credentialsProvider) {
+        this.credentialsProvider = credentialsProvider;
+        return this;
+    }
+
+    /**
+     * Assigns default {@link org.apache.hc.client5.http.auth.AuthScheme} registry which will
+     * be used for request execution if not explicitly set in the client execution
+     * context.
+     */
+    public final HttpAsyncClientBuilder setDefaultAuthSchemeRegistry(final Lookup<AuthSchemeProvider> authSchemeRegistry) {
+        this.authSchemeRegistry = authSchemeRegistry;
+        return this;
+    }
+
+    /**
+     * Assigns default {@link org.apache.hc.client5.http.cookie.CookieSpec} registry
+     * which will be used for request execution if not explicitly set in the client
+     * execution context.
+     */
+    public final HttpAsyncClientBuilder setDefaultCookieSpecRegistry(final Lookup<CookieSpecProvider> cookieSpecRegistry) {
+        this.cookieSpecRegistry = cookieSpecRegistry;
+        return this;
+    }
+
+    /**
+     * Assigns default {@link CookieStore} instance which will be used for
+     * request execution if not explicitly set in the client execution context.
+     */
+    public final HttpAsyncClientBuilder setDefaultCookieStore(final CookieStore cookieStore) {
+        this.cookieStore = cookieStore;
+        return this;
+    }
+
+    /**
      * Assigns default {@link RequestConfig} instance which will be used
      * for request execution if not explicitly set in the client execution
      * context.
@@ -506,6 +598,22 @@ public class HttpAsyncClientBuilder {
     }
 
     /**
+     * Disables state (cookie) management.
+     */
+    public final HttpAsyncClientBuilder disableCookieManagement() {
+        this.cookieManagementDisabled = true;
+        return this;
+    }
+
+    /**
+     * Disables authentication scheme caching.
+     */
+    public final HttpAsyncClientBuilder disableAuthCaching() {
+        this.authCachingDisabled = true;
+        return this;
+    }
+
+    /**
      * Makes this instance of HttpClient proactively evict expired connections from the
      * connection pool using a background thread.
      * <p>
@@ -596,6 +704,15 @@ public class HttpAsyncClientBuilder {
                 new AsyncMainClientExec(keepAliveStrategyCopy, userTokenHandlerCopy),
                 ChainElements.MAIN_TRANSPORT.name());
 
+        AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy;
+        if (targetAuthStrategyCopy == null) {
+            targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
+        }
+        AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy;
+        if (proxyAuthStrategyCopy == null) {
+            proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
+        }
+
         String userAgentCopy = this.userAgent;
         if (userAgentCopy == null) {
             if (systemProperties) {
@@ -629,6 +746,15 @@ public class HttpAsyncClientBuilder {
                 new H2RequestConnControl(),
                 new RequestUserAgent(userAgentCopy),
                 new RequestExpectContinue());
+        if (!cookieManagementDisabled) {
+            b.add(new RequestAddCookies());
+        }
+        if (!authCachingDisabled) {
+            b.add(new RequestAuthCache());
+        }
+        if (!cookieManagementDisabled) {
+            b.add(new ResponseProcessCookies());
+        }
         if (requestInterceptors != null) {
             for (final RequestInterceptorEntry entry: requestInterceptors) {
                 if (entry.postion == RequestInterceptorEntry.Postion.LAST) {
@@ -644,8 +770,9 @@ public class HttpAsyncClientBuilder {
             }
         }
 
+        final HttpProcessor httpProcessor = b.build();
         execChainDefinition.addFirst(
-                new AsyncProtocolExec(b.build()),
+                new AsyncProtocolExec(httpProcessor, targetAuthStrategyCopy, proxyAuthStrategyCopy),
                 ChainElements.PROTOCOL.name());
 
         // Add request retry executor, if not disabled
@@ -783,6 +910,36 @@ public class HttpAsyncClientBuilder {
             current = current.getPrevious();
         }
 
+        Lookup<AuthSchemeProvider> authSchemeRegistryCopy = this.authSchemeRegistry;
+        if (authSchemeRegistryCopy == null) {
+            authSchemeRegistryCopy = RegistryBuilder.<AuthSchemeProvider>create()
+                    .register(AuthSchemes.BASIC, new BasicSchemeFactory())
+                    .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
+                    .register(AuthSchemes.CREDSSP, new CredSspSchemeFactory())
+                    .register(AuthSchemes.NTLM, new NTLMSchemeFactory())
+                    .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(SystemDefaultDnsResolver.INSTANCE, true, true))
+                    .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory(SystemDefaultDnsResolver.INSTANCE, true, true))
+                    .build();
+        }
+        Lookup<CookieSpecProvider> cookieSpecRegistryCopy = this.cookieSpecRegistry;
+        if (cookieSpecRegistryCopy == null) {
+            cookieSpecRegistryCopy = CookieSpecRegistries.createDefault();
+        }
+
+        CookieStore cookieStoreCopy = this.cookieStore;
+        if (cookieStoreCopy == null) {
+            cookieStoreCopy = new BasicCookieStore();
+        }
+
+        CredentialsProvider credentialsProviderCopy = this.credentialsProvider;
+        if (credentialsProviderCopy == null) {
+            if (systemProperties) {
+                credentialsProviderCopy = new SystemDefaultCredentialsProvider();
+            } else {
+                credentialsProviderCopy = new BasicCredentialsProvider();
+            }
+        }
+
         return new InternalHttpAsyncClient(
                 ioReactor,
                 execChain,
@@ -791,6 +948,10 @@ public class HttpAsyncClientBuilder {
                 connManagerCopy,
                 routePlannerCopy,
                 versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
+                cookieSpecRegistryCopy,
+                authSchemeRegistryCopy,
+                cookieStoreCopy,
+                credentialsProviderCopy,
                 defaultRequestConfig,
                 closeablesCopy);
     }

Modified: httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncClient.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncClient.java?rev=1793567&r1=1793566&r2=1793567&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncClient.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncClient.java Tue May  2 18:22:59 2017
@@ -36,8 +36,12 @@ import org.apache.hc.client5.http.HttpRo
 import org.apache.hc.client5.http.async.AsyncExecCallback;
 import org.apache.hc.client5.http.async.AsyncExecChain;
 import org.apache.hc.client5.http.async.AsyncExecRuntime;
+import org.apache.hc.client5.http.auth.AuthSchemeProvider;
+import org.apache.hc.client5.http.auth.CredentialsProvider;
 import org.apache.hc.client5.http.config.Configurable;
 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.impl.ExecSupport;
 import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
 import org.apache.hc.client5.http.protocol.HttpClientContext;
@@ -49,6 +53,7 @@ import org.apache.hc.core5.http.HttpExce
 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.config.Lookup;
 import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
 import org.apache.hc.core5.http.nio.AsyncDataConsumer;
 import org.apache.hc.core5.http.nio.AsyncRequestProducer;
@@ -65,6 +70,10 @@ class InternalHttpAsyncClient extends Ab
     private final AsyncExecChainElement execChain;
     private final HttpRoutePlanner routePlanner;
     private final HttpVersionPolicy versionPolicy;
+    private final Lookup<CookieSpecProvider> cookieSpecRegistry;
+    private final Lookup<AuthSchemeProvider> authSchemeRegistry;
+    private final CookieStore cookieStore;
+    private final CredentialsProvider credentialsProvider;
     private final RequestConfig defaultConfig;
     private final List<Closeable> closeables;
 
@@ -76,6 +85,10 @@ class InternalHttpAsyncClient extends Ab
             final AsyncClientConnectionManager connmgr,
             final HttpRoutePlanner routePlanner,
             final HttpVersionPolicy versionPolicy,
+            final Lookup<CookieSpecProvider> cookieSpecRegistry,
+            final Lookup<AuthSchemeProvider> authSchemeRegistry,
+            final CookieStore cookieStore,
+            final CredentialsProvider credentialsProvider,
             final RequestConfig defaultConfig,
             final List<Closeable> closeables) {
         super(ioReactor, pushConsumerRegistry, threadFactory);
@@ -83,6 +96,10 @@ class InternalHttpAsyncClient extends Ab
         this.execChain = execChain;
         this.routePlanner = routePlanner;
         this.versionPolicy = versionPolicy;
+        this.cookieSpecRegistry = cookieSpecRegistry;
+        this.authSchemeRegistry = authSchemeRegistry;
+        this.cookieStore = cookieStore;
+        this.credentialsProvider = credentialsProvider;
         this.defaultConfig = defaultConfig;
         this.closeables = closeables;
     }
@@ -102,6 +119,18 @@ class InternalHttpAsyncClient extends Ab
     }
 
     private void setupContext(final HttpClientContext context) {
+        if (context.getAttribute(HttpClientContext.AUTHSCHEME_REGISTRY) == null) {
+            context.setAttribute(HttpClientContext.AUTHSCHEME_REGISTRY, authSchemeRegistry);
+        }
+        if (context.getAttribute(HttpClientContext.COOKIESPEC_REGISTRY) == null) {
+            context.setAttribute(HttpClientContext.COOKIESPEC_REGISTRY, cookieSpecRegistry);
+        }
+        if (context.getAttribute(HttpClientContext.COOKIE_STORE) == null) {
+            context.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
+        }
+        if (context.getAttribute(HttpClientContext.CREDS_PROVIDER) == null) {
+            context.setAttribute(HttpClientContext.CREDS_PROVIDER, credentialsProvider);
+        }
         if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
             context.setAttribute(HttpClientContext.REQUEST_CONFIG, defaultConfig);
         }

Modified: httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ProtocolExec.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ProtocolExec.java?rev=1793567&r1=1793566&r2=1793567&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ProtocolExec.java (original)
+++ httpcomponents/httpclient/trunk/httpclient5/src/main/java/org/apache/hc/client5/http/impl/sync/ProtocolExec.java Tue May  2 18:22:59 2017
@@ -38,7 +38,7 @@ import org.apache.hc.client5.http.auth.A
 import org.apache.hc.client5.http.auth.ChallengeType;
 import org.apache.hc.client5.http.auth.CredentialsProvider;
 import org.apache.hc.client5.http.auth.CredentialsStore;
-import org.apache.hc.client5.http.auth.util.CredentialSupport;
+import org.apache.hc.client5.http.impl.AuthSupport;
 import org.apache.hc.client5.http.config.RequestConfig;
 import org.apache.hc.client5.http.impl.auth.HttpAuthenticator;
 import org.apache.hc.client5.http.protocol.AuthenticationStrategy;
@@ -131,7 +131,7 @@ final class ProtocolExec implements Exec
             if (authority != null) {
                 final CredentialsProvider credsProvider = context.getCredentialsProvider();
                 if (credsProvider instanceof CredentialsStore) {
-                    CredentialSupport.extractFromAuthority(authority, (CredentialsStore) credsProvider);
+                    AuthSupport.extractFromAuthority(authority, (CredentialsStore) credsProvider);
                 }
             }
 
@@ -222,15 +222,7 @@ final class ProtocolExec implements Exec
             final HttpClientContext context) {
         final RequestConfig config = context.getRequestConfig();
         if (config.isAuthenticationEnabled()) {
-            final URIAuthority authority = request.getAuthority();
-            final String scheme = request.getScheme();
-            HttpHost target = authority != null ? new HttpHost(authority, scheme) : route.getTargetHost();;
-            if (target.getPort() < 0) {
-                target = new HttpHost(
-                        target.getHostName(),
-                        route.getTargetHost().getPort(),
-                        target.getSchemeName());
-            }
+            final HttpHost target = AuthSupport.resolveAuthTarget(request, route);
             final boolean targetAuthRequested = authenticator.isChallenged(
                     target, ChallengeType.TARGET, response, targetAuthExchange, context);