Improved protocol handling in the async request execution chain; implemented cookie processing and authentication handling
git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1793567 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d43ee7177b
commit
d5039137ca
|
@ -33,6 +33,9 @@ import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
|
||||||
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
|
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
|
||||||
import org.apache.hc.core5.http.HttpHost;
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
import org.apache.hc.core5.http.URIScheme;
|
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.io.ShutdownType;
|
||||||
import org.apache.hc.core5.reactor.ListenerEndpoint;
|
import org.apache.hc.core5.reactor.ListenerEndpoint;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
@ -69,8 +72,8 @@ public abstract class IntegrationTestBase extends LocalAsyncServerTestBase {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public HttpHost start() throws Exception {
|
public HttpHost start(final HttpProcessor httpProcessor, final H1Config h1Config) throws Exception {
|
||||||
server.start();
|
server.start(httpProcessor, h1Config);
|
||||||
final ListenerEndpoint listener = server.listen(new InetSocketAddress(0));
|
final ListenerEndpoint listener = server.listen(new InetSocketAddress(0));
|
||||||
httpclient = clientBuilder.build();
|
httpclient = clientBuilder.build();
|
||||||
httpclient.start();
|
httpclient.start();
|
||||||
|
@ -79,4 +82,8 @@ public abstract class IntegrationTestBase extends LocalAsyncServerTestBase {
|
||||||
return new HttpHost("localhost", address.getPort(), scheme.name());
|
return new HttpHost("localhost", address.getPort(), scheme.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpHost start() throws Exception {
|
||||||
|
return start(HttpProcessors.server(), H1Config.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,6 @@ import org.apache.hc.core5.reactor.ListenerEndpoint;
|
||||||
import org.apache.hc.core5.testing.nio.Http2TestServer;
|
import org.apache.hc.core5.testing.nio.Http2TestServer;
|
||||||
import org.apache.hc.core5.util.TimeValue;
|
import org.apache.hc.core5.util.TimeValue;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
@ -641,7 +640,7 @@ public class TestAsyncRedirects extends IntegrationTestBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test @Ignore
|
@Test
|
||||||
public void testRedirectWithCookie() throws Exception {
|
public void testRedirectWithCookie() throws Exception {
|
||||||
server.register("*", new Supplier<AsyncServerExchangeHandler>() {
|
server.register("*", new Supplier<AsyncServerExchangeHandler>() {
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.AuthScope;
|
||||||
import org.apache.hc.client5.http.auth.CredentialsStore;
|
import org.apache.hc.client5.http.auth.CredentialsStore;
|
||||||
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
|
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
|
||||||
import org.apache.hc.client5.http.config.AuthSchemes;
|
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.net.URIAuthority;
|
||||||
import org.apache.hc.core5.util.Args;
|
import org.apache.hc.core5.util.Args;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
public class CredentialSupport {
|
public class AuthSupport {
|
||||||
|
|
||||||
public static void extractFromAuthority(
|
public static void extractFromAuthority(
|
||||||
final URIAuthority authority,
|
final URIAuthority authority,
|
||||||
|
@ -65,4 +68,20 @@ public class CredentialSupport {
|
||||||
new UsernamePasswordCredentials(userName, password));
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -27,31 +27,74 @@
|
||||||
package org.apache.hc.client5.http.impl.async;
|
package org.apache.hc.client5.http.impl.async;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.HttpRoute;
|
||||||
|
import org.apache.hc.client5.http.StandardMethods;
|
||||||
import org.apache.hc.client5.http.async.AsyncExecCallback;
|
import org.apache.hc.client5.http.async.AsyncExecCallback;
|
||||||
import org.apache.hc.client5.http.async.AsyncExecChain;
|
import org.apache.hc.client5.http.async.AsyncExecChain;
|
||||||
import org.apache.hc.client5.http.async.AsyncExecChainHandler;
|
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.CredentialsProvider;
|
||||||
import org.apache.hc.client5.http.auth.CredentialsStore;
|
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.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.EntityDetails;
|
||||||
|
import org.apache.hc.core5.http.Header;
|
||||||
import org.apache.hc.core5.http.HttpException;
|
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.HttpRequest;
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
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.AsyncDataConsumer;
|
||||||
import org.apache.hc.core5.http.nio.AsyncEntityProducer;
|
import org.apache.hc.core5.http.nio.AsyncEntityProducer;
|
||||||
import org.apache.hc.core5.http.protocol.HttpCoreContext;
|
import org.apache.hc.core5.http.protocol.HttpCoreContext;
|
||||||
import org.apache.hc.core5.http.protocol.HttpProcessor;
|
import org.apache.hc.core5.http.protocol.HttpProcessor;
|
||||||
import org.apache.hc.core5.net.URIAuthority;
|
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 {
|
class AsyncProtocolExec implements AsyncExecChainHandler {
|
||||||
|
|
||||||
private final HttpProcessor httpProcessor;
|
private final Logger log = LogManager.getLogger(getClass());
|
||||||
|
|
||||||
AsyncProtocolExec(final HttpProcessor httpProcessor) {
|
private final HttpProcessor httpProcessor;
|
||||||
this.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
|
@Override
|
||||||
|
@ -63,32 +106,121 @@ class AsyncProtocolExec implements AsyncExecChainHandler {
|
||||||
final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {
|
final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {
|
||||||
final HttpRoute route = scope.route;
|
final HttpRoute route = scope.route;
|
||||||
final HttpClientContext clientContext = scope.clientContext;
|
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();
|
final URIAuthority authority = request.getAuthority();
|
||||||
if (authority != null) {
|
if (authority != null) {
|
||||||
final CredentialsProvider credsProvider = clientContext.getCredentialsProvider();
|
final CredentialsProvider credsProvider = clientContext.getCredentialsProvider();
|
||||||
if (credsProvider instanceof CredentialsStore) {
|
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(HttpClientContext.HTTP_ROUTE, route);
|
||||||
clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
|
clientContext.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
|
||||||
httpProcessor.process(request, entityProducer, clientContext);
|
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() {
|
chain.proceed(request, entityProducer, scope, new AsyncExecCallback() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AsyncDataConsumer handleResponse(
|
public AsyncDataConsumer handleResponse(
|
||||||
final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
|
final HttpResponse response, final EntityDetails entityDetails) throws HttpException, IOException {
|
||||||
|
|
||||||
clientContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
|
clientContext.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
|
||||||
httpProcessor.process(response, entityDetails, clientContext);
|
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
|
@Override
|
||||||
public void completed() {
|
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
|
@Override
|
||||||
|
@ -99,4 +231,37 @@ class AsyncProtocolExec implements AsyncExecChainHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,25 +37,46 @@ import java.util.List;
|
||||||
|
|
||||||
import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
|
import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
|
||||||
import org.apache.hc.client5.http.SchemePortResolver;
|
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.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.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.DefaultConnectionKeepAliveStrategy;
|
||||||
import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
|
import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
|
||||||
import org.apache.hc.client5.http.impl.DefaultUserTokenHandler;
|
import org.apache.hc.client5.http.impl.DefaultUserTokenHandler;
|
||||||
import org.apache.hc.client5.http.impl.IdleConnectionEvictor;
|
import org.apache.hc.client5.http.impl.IdleConnectionEvictor;
|
||||||
import org.apache.hc.client5.http.impl.NamedElementChain;
|
import org.apache.hc.client5.http.impl.NamedElementChain;
|
||||||
import org.apache.hc.client5.http.impl.NoopUserTokenHandler;
|
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.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.protocol.DefaultRedirectStrategy;
|
||||||
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
|
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.DefaultRoutePlanner;
|
||||||
import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
|
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.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.impl.sync.DefaultHttpRequestRetryHandler;
|
||||||
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
|
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.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.RequestDefaultHeaders;
|
||||||
import org.apache.hc.client5.http.protocol.RequestExpectContinue;
|
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.protocol.UserTokenHandler;
|
||||||
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
|
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
|
||||||
import org.apache.hc.client5.http.sync.HttpRequestRetryHandler;
|
import org.apache.hc.client5.http.sync.HttpRequestRetryHandler;
|
||||||
|
@ -72,11 +93,14 @@ import org.apache.hc.core5.http.HttpResponse;
|
||||||
import org.apache.hc.core5.http.HttpResponseInterceptor;
|
import org.apache.hc.core5.http.HttpResponseInterceptor;
|
||||||
import org.apache.hc.core5.http.config.CharCodingConfig;
|
import org.apache.hc.core5.http.config.CharCodingConfig;
|
||||||
import org.apache.hc.core5.http.config.H1Config;
|
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.impl.DefaultConnectionReuseStrategy;
|
||||||
import org.apache.hc.core5.http.nio.AsyncPushConsumer;
|
import org.apache.hc.core5.http.nio.AsyncPushConsumer;
|
||||||
import org.apache.hc.core5.http.nio.HandlerFactory;
|
import org.apache.hc.core5.http.nio.HandlerFactory;
|
||||||
import org.apache.hc.core5.http.nio.command.ShutdownCommand;
|
import org.apache.hc.core5.http.nio.command.ShutdownCommand;
|
||||||
import org.apache.hc.core5.http.protocol.HttpContext;
|
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.HttpProcessorBuilder;
|
||||||
import org.apache.hc.core5.http.protocol.RequestUserAgent;
|
import org.apache.hc.core5.http.protocol.RequestUserAgent;
|
||||||
import org.apache.hc.core5.http2.HttpVersionPolicy;
|
import org.apache.hc.core5.http2.HttpVersionPolicy;
|
||||||
|
@ -179,6 +203,8 @@ public class HttpAsyncClientBuilder {
|
||||||
private SchemePortResolver schemePortResolver;
|
private SchemePortResolver schemePortResolver;
|
||||||
private ConnectionKeepAliveStrategy keepAliveStrategy;
|
private ConnectionKeepAliveStrategy keepAliveStrategy;
|
||||||
private UserTokenHandler userTokenHandler;
|
private UserTokenHandler userTokenHandler;
|
||||||
|
private AuthenticationStrategy targetAuthStrategy;
|
||||||
|
private AuthenticationStrategy proxyAuthStrategy;
|
||||||
|
|
||||||
private LinkedList<RequestInterceptorEntry> requestInterceptors;
|
private LinkedList<RequestInterceptorEntry> requestInterceptors;
|
||||||
private LinkedList<ResponseInterceptorEntry> responseInterceptors;
|
private LinkedList<ResponseInterceptorEntry> responseInterceptors;
|
||||||
|
@ -190,6 +216,11 @@ public class HttpAsyncClientBuilder {
|
||||||
|
|
||||||
private ConnectionReuseStrategy reuseStrategy;
|
private ConnectionReuseStrategy reuseStrategy;
|
||||||
|
|
||||||
|
private Lookup<AuthSchemeProvider> authSchemeRegistry;
|
||||||
|
private Lookup<CookieSpecProvider> cookieSpecRegistry;
|
||||||
|
private CookieStore cookieStore;
|
||||||
|
private CredentialsProvider credentialsProvider;
|
||||||
|
|
||||||
private String userAgent;
|
private String userAgent;
|
||||||
private HttpHost proxy;
|
private HttpHost proxy;
|
||||||
private Collection<? extends Header> defaultHeaders;
|
private Collection<? extends Header> defaultHeaders;
|
||||||
|
@ -201,6 +232,8 @@ public class HttpAsyncClientBuilder {
|
||||||
private boolean systemProperties;
|
private boolean systemProperties;
|
||||||
private boolean automaticRetriesDisabled;
|
private boolean automaticRetriesDisabled;
|
||||||
private boolean redirectHandlingDisabled;
|
private boolean redirectHandlingDisabled;
|
||||||
|
private boolean cookieManagementDisabled;
|
||||||
|
private boolean authCachingDisabled;
|
||||||
private boolean connectionStateDisabled;
|
private boolean connectionStateDisabled;
|
||||||
|
|
||||||
private List<Closeable> closeables;
|
private List<Closeable> closeables;
|
||||||
|
@ -307,6 +340,26 @@ public class HttpAsyncClientBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Adds this protocol interceptor to the head of the protocol processing list.
|
||||||
*/
|
*/
|
||||||
|
@ -462,6 +515,45 @@ public class HttpAsyncClientBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Assigns default {@link RequestConfig} instance which will be used
|
||||||
* for request execution if not explicitly set in the client execution
|
* for request execution if not explicitly set in the client execution
|
||||||
|
@ -505,6 +597,22 @@ public class HttpAsyncClientBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Makes this instance of HttpClient proactively evict expired connections from the
|
||||||
* connection pool using a background thread.
|
* connection pool using a background thread.
|
||||||
|
@ -596,6 +704,15 @@ public class HttpAsyncClientBuilder {
|
||||||
new AsyncMainClientExec(keepAliveStrategyCopy, userTokenHandlerCopy),
|
new AsyncMainClientExec(keepAliveStrategyCopy, userTokenHandlerCopy),
|
||||||
ChainElements.MAIN_TRANSPORT.name());
|
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;
|
String userAgentCopy = this.userAgent;
|
||||||
if (userAgentCopy == null) {
|
if (userAgentCopy == null) {
|
||||||
if (systemProperties) {
|
if (systemProperties) {
|
||||||
|
@ -629,6 +746,15 @@ public class HttpAsyncClientBuilder {
|
||||||
new H2RequestConnControl(),
|
new H2RequestConnControl(),
|
||||||
new RequestUserAgent(userAgentCopy),
|
new RequestUserAgent(userAgentCopy),
|
||||||
new RequestExpectContinue());
|
new RequestExpectContinue());
|
||||||
|
if (!cookieManagementDisabled) {
|
||||||
|
b.add(new RequestAddCookies());
|
||||||
|
}
|
||||||
|
if (!authCachingDisabled) {
|
||||||
|
b.add(new RequestAuthCache());
|
||||||
|
}
|
||||||
|
if (!cookieManagementDisabled) {
|
||||||
|
b.add(new ResponseProcessCookies());
|
||||||
|
}
|
||||||
if (requestInterceptors != null) {
|
if (requestInterceptors != null) {
|
||||||
for (final RequestInterceptorEntry entry: requestInterceptors) {
|
for (final RequestInterceptorEntry entry: requestInterceptors) {
|
||||||
if (entry.postion == RequestInterceptorEntry.Postion.LAST) {
|
if (entry.postion == RequestInterceptorEntry.Postion.LAST) {
|
||||||
|
@ -644,8 +770,9 @@ public class HttpAsyncClientBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final HttpProcessor httpProcessor = b.build();
|
||||||
execChainDefinition.addFirst(
|
execChainDefinition.addFirst(
|
||||||
new AsyncProtocolExec(b.build()),
|
new AsyncProtocolExec(httpProcessor, targetAuthStrategyCopy, proxyAuthStrategyCopy),
|
||||||
ChainElements.PROTOCOL.name());
|
ChainElements.PROTOCOL.name());
|
||||||
|
|
||||||
// Add request retry executor, if not disabled
|
// Add request retry executor, if not disabled
|
||||||
|
@ -783,6 +910,36 @@ public class HttpAsyncClientBuilder {
|
||||||
current = current.getPrevious();
|
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(
|
return new InternalHttpAsyncClient(
|
||||||
ioReactor,
|
ioReactor,
|
||||||
execChain,
|
execChain,
|
||||||
|
@ -791,6 +948,10 @@ public class HttpAsyncClientBuilder {
|
||||||
connManagerCopy,
|
connManagerCopy,
|
||||||
routePlannerCopy,
|
routePlannerCopy,
|
||||||
versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
|
versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
|
||||||
|
cookieSpecRegistryCopy,
|
||||||
|
authSchemeRegistryCopy,
|
||||||
|
cookieStoreCopy,
|
||||||
|
credentialsProviderCopy,
|
||||||
defaultRequestConfig,
|
defaultRequestConfig,
|
||||||
closeablesCopy);
|
closeablesCopy);
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,12 @@ import org.apache.hc.client5.http.HttpRoute;
|
||||||
import org.apache.hc.client5.http.async.AsyncExecCallback;
|
import org.apache.hc.client5.http.async.AsyncExecCallback;
|
||||||
import org.apache.hc.client5.http.async.AsyncExecChain;
|
import org.apache.hc.client5.http.async.AsyncExecChain;
|
||||||
import org.apache.hc.client5.http.async.AsyncExecRuntime;
|
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.Configurable;
|
||||||
import org.apache.hc.client5.http.config.RequestConfig;
|
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.impl.ExecSupport;
|
||||||
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
|
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
|
||||||
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
import org.apache.hc.client5.http.protocol.HttpClientContext;
|
||||||
|
@ -49,6 +53,7 @@ import org.apache.hc.core5.http.HttpException;
|
||||||
import org.apache.hc.core5.http.HttpHost;
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
import org.apache.hc.core5.http.HttpRequest;
|
import org.apache.hc.core5.http.HttpRequest;
|
||||||
import org.apache.hc.core5.http.HttpResponse;
|
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.AsyncClientExchangeHandler;
|
||||||
import org.apache.hc.core5.http.nio.AsyncDataConsumer;
|
import org.apache.hc.core5.http.nio.AsyncDataConsumer;
|
||||||
import org.apache.hc.core5.http.nio.AsyncRequestProducer;
|
import org.apache.hc.core5.http.nio.AsyncRequestProducer;
|
||||||
|
@ -65,6 +70,10 @@ class InternalHttpAsyncClient extends AbstractHttpAsyncClientBase {
|
||||||
private final AsyncExecChainElement execChain;
|
private final AsyncExecChainElement execChain;
|
||||||
private final HttpRoutePlanner routePlanner;
|
private final HttpRoutePlanner routePlanner;
|
||||||
private final HttpVersionPolicy versionPolicy;
|
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 RequestConfig defaultConfig;
|
||||||
private final List<Closeable> closeables;
|
private final List<Closeable> closeables;
|
||||||
|
|
||||||
|
@ -76,6 +85,10 @@ class InternalHttpAsyncClient extends AbstractHttpAsyncClientBase {
|
||||||
final AsyncClientConnectionManager connmgr,
|
final AsyncClientConnectionManager connmgr,
|
||||||
final HttpRoutePlanner routePlanner,
|
final HttpRoutePlanner routePlanner,
|
||||||
final HttpVersionPolicy versionPolicy,
|
final HttpVersionPolicy versionPolicy,
|
||||||
|
final Lookup<CookieSpecProvider> cookieSpecRegistry,
|
||||||
|
final Lookup<AuthSchemeProvider> authSchemeRegistry,
|
||||||
|
final CookieStore cookieStore,
|
||||||
|
final CredentialsProvider credentialsProvider,
|
||||||
final RequestConfig defaultConfig,
|
final RequestConfig defaultConfig,
|
||||||
final List<Closeable> closeables) {
|
final List<Closeable> closeables) {
|
||||||
super(ioReactor, pushConsumerRegistry, threadFactory);
|
super(ioReactor, pushConsumerRegistry, threadFactory);
|
||||||
|
@ -83,6 +96,10 @@ class InternalHttpAsyncClient extends AbstractHttpAsyncClientBase {
|
||||||
this.execChain = execChain;
|
this.execChain = execChain;
|
||||||
this.routePlanner = routePlanner;
|
this.routePlanner = routePlanner;
|
||||||
this.versionPolicy = versionPolicy;
|
this.versionPolicy = versionPolicy;
|
||||||
|
this.cookieSpecRegistry = cookieSpecRegistry;
|
||||||
|
this.authSchemeRegistry = authSchemeRegistry;
|
||||||
|
this.cookieStore = cookieStore;
|
||||||
|
this.credentialsProvider = credentialsProvider;
|
||||||
this.defaultConfig = defaultConfig;
|
this.defaultConfig = defaultConfig;
|
||||||
this.closeables = closeables;
|
this.closeables = closeables;
|
||||||
}
|
}
|
||||||
|
@ -102,6 +119,18 @@ class InternalHttpAsyncClient extends AbstractHttpAsyncClientBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupContext(final HttpClientContext context) {
|
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) {
|
if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
|
||||||
context.setAttribute(HttpClientContext.REQUEST_CONFIG, defaultConfig);
|
context.setAttribute(HttpClientContext.REQUEST_CONFIG, defaultConfig);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ import org.apache.hc.client5.http.auth.AuthExchange;
|
||||||
import org.apache.hc.client5.http.auth.ChallengeType;
|
import org.apache.hc.client5.http.auth.ChallengeType;
|
||||||
import org.apache.hc.client5.http.auth.CredentialsProvider;
|
import org.apache.hc.client5.http.auth.CredentialsProvider;
|
||||||
import org.apache.hc.client5.http.auth.CredentialsStore;
|
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.config.RequestConfig;
|
||||||
import org.apache.hc.client5.http.impl.auth.HttpAuthenticator;
|
import org.apache.hc.client5.http.impl.auth.HttpAuthenticator;
|
||||||
import org.apache.hc.client5.http.protocol.AuthenticationStrategy;
|
import org.apache.hc.client5.http.protocol.AuthenticationStrategy;
|
||||||
|
@ -131,7 +131,7 @@ final class ProtocolExec implements ExecChainHandler {
|
||||||
if (authority != null) {
|
if (authority != null) {
|
||||||
final CredentialsProvider credsProvider = context.getCredentialsProvider();
|
final CredentialsProvider credsProvider = context.getCredentialsProvider();
|
||||||
if (credsProvider instanceof CredentialsStore) {
|
if (credsProvider instanceof CredentialsStore) {
|
||||||
CredentialSupport.extractFromAuthority(authority, (CredentialsStore) credsProvider);
|
AuthSupport.extractFromAuthority(authority, (CredentialsStore) credsProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,15 +222,7 @@ final class ProtocolExec implements ExecChainHandler {
|
||||||
final HttpClientContext context) {
|
final HttpClientContext context) {
|
||||||
final RequestConfig config = context.getRequestConfig();
|
final RequestConfig config = context.getRequestConfig();
|
||||||
if (config.isAuthenticationEnabled()) {
|
if (config.isAuthenticationEnabled()) {
|
||||||
final URIAuthority authority = request.getAuthority();
|
final HttpHost target = AuthSupport.resolveAuthTarget(request, route);
|
||||||
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 boolean targetAuthRequested = authenticator.isChallenged(
|
final boolean targetAuthRequested = authenticator.isChallenged(
|
||||||
target, ChallengeType.TARGET, response, targetAuthExchange, context);
|
target, ChallengeType.TARGET, response, targetAuthExchange, context);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue