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.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 IntegrationTestBase extends LocalAsyncServerTestBase {
|
|||
|
||||
};
|
||||
|
||||
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 IntegrationTestBase extends LocalAsyncServerTestBase {
|
|||
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.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 IntegrationTestBase {
|
|||
}
|
||||
}
|
||||
|
||||
@Test @Ignore
|
||||
@Test
|
||||
public void testRedirectWithCookie() throws Exception {
|
||||
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.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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 AsyncExecChainHandler {
|
|||
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 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.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.HttpResponse;
|
|||
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;
|
||||
|
@ -307,6 +340,26 @@ public class HttpAsyncClientBuilder {
|
|||
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.
|
||||
*/
|
||||
|
@ -462,6 +515,45 @@ public class HttpAsyncClientBuilder {
|
|||
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
|
||||
* for request execution if not explicitly set in the client execution
|
||||
|
@ -505,6 +597,22 @@ public class HttpAsyncClientBuilder {
|
|||
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
|
||||
* connection pool using a background thread.
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.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.HttpException;
|
|||
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 AbstractHttpAsyncClientBase {
|
|||
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 AbstractHttpAsyncClientBase {
|
|||
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 AbstractHttpAsyncClientBase {
|
|||
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 AbstractHttpAsyncClientBase {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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.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 ExecChainHandler {
|
|||
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 ExecChainHandler {
|
|||
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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue