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);