AuthCache conformance to RFC 7617

This commit is contained in:
Oleg Kalnichevski 2021-09-26 11:21:13 +02:00
parent 5390aef223
commit 30c253b37b
20 changed files with 527 additions and 325 deletions

View File

@ -26,14 +26,19 @@
*/
package org.apache.hc.client5.testing.async;
import java.util.Arrays;
import java.util.Collections;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import org.apache.hc.client5.http.AuthenticationStrategy;
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.SimpleRequestBuilder;
import org.apache.hc.client5.http.auth.AuthCache;
import org.apache.hc.client5.http.auth.AuthSchemeFactory;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.CredentialsProvider;
@ -42,6 +47,7 @@
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
import org.apache.hc.client5.http.impl.auth.BasicScheme;
import org.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder;
import org.apache.hc.client5.http.protocol.HttpClientContext;
@ -52,7 +58,9 @@
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.HttpRequestInterceptor;
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.HttpVersion;
import org.apache.hc.core5.http.URIScheme;
@ -64,9 +72,12 @@
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
import org.apache.hc.core5.http.protocol.HttpCoreContext;
import org.apache.hc.core5.http.support.BasicResponseBuilder;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.http2.impl.H2Processors;
import org.apache.hc.core5.net.URIAuthority;
import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
@ -104,6 +115,10 @@ public final HttpHost start(
abstract void setTargetAuthenticationStrategy(AuthenticationStrategy targetAuthStrategy);
abstract void addResponseInterceptor(HttpResponseInterceptor responseInterceptor);
abstract void addRequestInterceptor(final HttpRequestInterceptor requestInterceptor);
@Test
public void testBasicAuthenticationNoCreds() throws Exception {
server.register("*", AsyncEchoHandler::new);
@ -270,6 +285,69 @@ public void testBasicAuthenticationCredentialsCaching() throws Exception {
Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any());
}
@Test
public void testBasicAuthenticationCredentialsCachingByPathPrefix() throws Exception {
server.register("*", AsyncEchoHandler::new);
final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy());
setTargetAuthenticationStrategy(authStrategy);
final Queue<HttpResponse> responseQueue = new ConcurrentLinkedQueue<>();
addResponseInterceptor((response, entity, context)
-> responseQueue.add(BasicResponseBuilder.copy(response).build()));
final HttpHost target = start();
final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
.add(target, "test", "test".toCharArray())
.build();
final AuthCache authCache = new BasicAuthCache();
for (final String requestPath: new String[] {"/blah/a", "/blah/b?huh", "/blah/c", "/bl%61h/%61"}) {
final HttpClientContext context = HttpClientContext.create();
context.setAuthCache(authCache);
context.setCredentialsProvider(credentialsProvider);
final Future<SimpleHttpResponse> future = httpclient.execute(SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(requestPath)
.build(), context, null);
final HttpResponse response = future.get();
Assert.assertNotNull(response);
Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
}
// There should be only single auth strategy call for all successful message exchanges
Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any());
MatcherAssert.assertThat(
responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()),
CoreMatchers.equalTo(Arrays.asList(401, 200, 200, 200, 200)));
responseQueue.clear();
authCache.clear();
Mockito.reset(authStrategy);
for (final String requestPath: new String[] {"/blah/a", "/yada/a", "/blah/blah/"}) {
final HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credentialsProvider);
context.setAuthCache(authCache);
final Future<SimpleHttpResponse> future = httpclient.execute(SimpleRequestBuilder.get()
.setHttpHost(target)
.setPath(requestPath)
.build(), context, null);
final HttpResponse response = future.get();
Assert.assertNotNull(response);
Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
}
// There should be an auth strategy call for all successful message exchanges
Mockito.verify(authStrategy, Mockito.times(3)).select(Mockito.any(), Mockito.any(), Mockito.any());
MatcherAssert.assertThat(
responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()),
CoreMatchers.equalTo(Arrays.asList(401, 200, 401, 200, 401, 200)));
}
@Test
public void testAuthenticationUserinfoInRequestSuccess() throws Exception {
server.register("*", AsyncEchoHandler::new);

View File

@ -38,6 +38,8 @@
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.client5.testing.SSLTestContexts;
import org.apache.hc.core5.http.HttpRequestInterceptor;
import org.apache.hc.core5.http.HttpResponseInterceptor;
import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http.config.Lookup;
@ -92,6 +94,16 @@ void setTargetAuthenticationStrategy(final AuthenticationStrategy targetAuthStra
clientBuilder.setTargetAuthenticationStrategy(targetAuthStrategy);
}
@Override
void addResponseInterceptor(final HttpResponseInterceptor responseInterceptor) {
clientBuilder.addResponseInterceptorLast(responseInterceptor);
}
@Override
void addRequestInterceptor(final HttpRequestInterceptor requestInterceptor) {
clientBuilder.addRequestInterceptorLast(requestInterceptor);
}
@Override
protected CloseableHttpAsyncClient createClient() throws Exception {
return clientBuilder.build();

View File

@ -51,7 +51,9 @@
import org.apache.hc.core5.http.HeaderElements;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequestInterceptor;
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.HttpVersion;
import org.apache.hc.core5.http.URIScheme;
@ -132,6 +134,16 @@ void setTargetAuthenticationStrategy(final AuthenticationStrategy targetAuthStra
clientBuilder.setTargetAuthenticationStrategy(targetAuthStrategy);
}
@Override
void addResponseInterceptor(final HttpResponseInterceptor responseInterceptor) {
clientBuilder.addResponseInterceptorLast(responseInterceptor);
}
@Override
void addRequestInterceptor(final HttpRequestInterceptor requestInterceptor) {
clientBuilder.addRequestInterceptorLast(requestInterceptor);
}
@Override
protected CloseableHttpAsyncClient createClient() throws Exception {
return clientBuilder.build();

View File

@ -30,8 +30,12 @@
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import org.apache.hc.client5.http.auth.AuthCache;
import org.apache.hc.client5.http.auth.AuthScheme;
@ -63,6 +67,7 @@
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.config.Registry;
import org.apache.hc.core5.http.config.RegistryBuilder;
@ -74,7 +79,10 @@
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http.protocol.HttpCoreContext;
import org.apache.hc.core5.http.support.BasicResponseBuilder;
import org.apache.hc.core5.net.URIAuthority;
import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
@ -267,6 +275,9 @@ public void testBasicAuthenticationCredentialsCaching() throws Exception {
this.server.registerHandler("*", new EchoHandler());
final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy());
this.clientBuilder.setTargetAuthenticationStrategy(authStrategy);
final Queue<HttpResponse> responseQueue = new ConcurrentLinkedQueue<>();
this.clientBuilder.addResponseInterceptorLast((response, entity, context)
-> responseQueue.add(BasicResponseBuilder.copy(response).build()));
final HttpHost target = start();
@ -286,6 +297,69 @@ public void testBasicAuthenticationCredentialsCaching() throws Exception {
}
Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any());
MatcherAssert.assertThat(
responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()),
CoreMatchers.equalTo(Arrays.asList(401, 200, 200, 200, 200, 200)));
}
@Test
public void testBasicAuthenticationCredentialsCachingByPathPrefix() throws Exception {
this.server.registerHandler("*", new EchoHandler());
final DefaultAuthenticationStrategy authStrategy = Mockito.spy(new DefaultAuthenticationStrategy());
this.clientBuilder.setTargetAuthenticationStrategy(authStrategy);
final Queue<HttpResponse> responseQueue = new ConcurrentLinkedQueue<>();
this.clientBuilder.addResponseInterceptorLast((response, entity, context)
-> responseQueue.add(BasicResponseBuilder.copy(response).build()));
final HttpHost target = start();
final CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
.add(target, "test", "test".toCharArray())
.build();
final AuthCache authCache = new BasicAuthCache();
final HttpClientContext context = HttpClientContext.create();
context.setAuthCache(authCache);
context.setCredentialsProvider(credentialsProvider);
for (final String requestPath: new String[] {"/blah/a", "/blah/b?huh", "/blah/c", "/bl%61h/%61"}) {
final HttpGet httpget = new HttpGet(requestPath);
try (final ClassicHttpResponse response = this.httpclient.execute(target, httpget, context)) {
final HttpEntity entity1 = response.getEntity();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertNotNull(entity1);
EntityUtils.consume(entity1);
}
}
// There should be only single auth strategy call for all successful message exchanges
Mockito.verify(authStrategy).select(Mockito.any(), Mockito.any(), Mockito.any());
MatcherAssert.assertThat(
responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()),
CoreMatchers.equalTo(Arrays.asList(401, 200, 200, 200, 200)));
responseQueue.clear();
authCache.clear();
Mockito.reset(authStrategy);
for (final String requestPath: new String[] {"/blah/a", "/yada/a", "/blah/blah/", "/buh/a"}) {
final HttpGet httpget = new HttpGet(requestPath);
try (final ClassicHttpResponse response = this.httpclient.execute(target, httpget, context)) {
final HttpEntity entity1 = response.getEntity();
Assert.assertEquals(HttpStatus.SC_OK, response.getCode());
Assert.assertNotNull(entity1);
EntityUtils.consume(entity1);
}
}
// There should be an auth strategy call for all successful message exchanges
Mockito.verify(authStrategy, Mockito.times(2)).select(Mockito.any(), Mockito.any(), Mockito.any());
MatcherAssert.assertThat(
responseQueue.stream().map(HttpResponse::getCode).collect(Collectors.toList()),
CoreMatchers.equalTo(Arrays.asList(200, 401, 200, 200, 401, 200)));
}
@Test

View File

@ -29,6 +29,7 @@
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.net.NamedEndpoint;
/**
* Strategy for default port resolution for protocol schemes.
@ -43,4 +44,13 @@ public interface SchemePortResolver {
*/
int resolve(HttpHost host);
/**
* Returns the actual port for the host based on the protocol scheme.
*
* @since 5.2
*/
default int resolve(String scheme, NamedEndpoint endpoint) {
return resolve(new HttpHost(scheme, endpoint));
}
}

View File

@ -36,12 +36,73 @@
*/
public interface AuthCache {
/**
* Stores the authentication state with the given authentication scope in the cache.
*
* @param host the authentication authority.
* @param authScheme the cacheable authentication state.
*/
void put(HttpHost host, AuthScheme authScheme);
/**
* Returns the authentication state with the given authentication scope from the cache
* if available.
*
* @param host the authentication authority.
* @return the authentication state ir {@code null} if not available in the cache.
*/
AuthScheme get(HttpHost host);
/**
* Removes the authentication state with the given authentication scope from the cache
* if found.
*
* @param host the authentication authority.
*/
void remove(HttpHost host);
void clear();
/**
* Stores the authentication state with the given authentication scope in the cache.
*
* @param host the authentication authority.
* @param pathPrefix the path prefix (the path component up to the last segment separator).
* Can be {@code null}.
* @param authScheme the cacheable authentication state.
*
* @since 5.2
*/
default void put(HttpHost host, String pathPrefix, AuthScheme authScheme) {
put(host, authScheme);
}
/**
* Returns the authentication state with the given authentication scope from the cache
* if available.
* @param host the authentication authority.
* @param pathPrefix the path prefix (the path component up to the last segment separator).
* Can be {@code null}.
* @return the authentication state ir {@code null} if not available in the cache.
*
* @since 5.2
*/
default AuthScheme get(HttpHost host, String pathPrefix) {
return get(host);
}
/**
* Removes the authentication state with the given authentication scope from the cache
* if found.
*
* @param host the authentication authority.
* @param pathPrefix the path prefix (the path component up to the last segment separator).
* Can be {@code null}.
*
* @since 5.2
*/
default void remove(HttpHost host, String pathPrefix) {
remove(host);
}
}

View File

@ -47,6 +47,7 @@ public enum State {
private State state;
private AuthScheme authScheme;
private Queue<AuthScheme> authOptions;
private String pathPrefix;
public AuthExchange() {
super();
@ -57,6 +58,7 @@ public void reset() {
this.state = State.UNCHALLENGED;
this.authOptions = null;
this.authScheme = null;
this.pathPrefix = null;
}
public State getState() {
@ -81,6 +83,20 @@ public boolean isConnectionBased() {
return this.authScheme != null && this.authScheme.isConnectionBased();
}
/**
* @since 5.2
*/
public String getPathPrefix() {
return pathPrefix;
}
/**
* @since 5.2
*/
public void setPathPrefix(final String pathPrefix) {
this.pathPrefix = pathPrefix;
}
/**
* Resets the auth state with {@link AuthScheme} and clears auth options.
*

View File

@ -39,8 +39,8 @@
import org.apache.hc.client5.http.auth.AuthChallenge;
import org.apache.hc.client5.http.auth.AuthScheme;
import org.apache.hc.client5.http.auth.AuthSchemeFactory;
import org.apache.hc.client5.http.auth.StandardAuthScheme;
import org.apache.hc.client5.http.auth.ChallengeType;
import org.apache.hc.client5.http.auth.StandardAuthScheme;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.annotation.Contract;
@ -117,7 +117,7 @@ public List<AuthScheme> select(
options.add(authScheme);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("{}, Challenge for {} authentication scheme not available", exchangeId, schemeName);
LOG.debug("{} Challenge for {} authentication scheme not available", exchangeId, schemeName);
}
}
}

View File

@ -31,6 +31,7 @@
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.net.NamedEndpoint;
import org.apache.hc.core5.util.Args;
/**
@ -46,14 +47,19 @@ public class DefaultSchemePortResolver implements SchemePortResolver {
@Override
public int resolve(final HttpHost host) {
Args.notNull(host, "HTTP host");
final int port = host.getPort();
return resolve(host.getSchemeName(), host);
}
@Override
public int resolve(final String scheme, final NamedEndpoint endpoint) {
Args.notNull(endpoint, "Endpoint");
final int port = endpoint.getPort();
if (port > 0) {
return port;
}
final String name = host.getSchemeName();
if (URIScheme.HTTP.same(name)) {
if (URIScheme.HTTP.same(scheme)) {
return 80;
} else if (URIScheme.HTTPS.same(name)) {
} else if (URIScheme.HTTPS.same(scheme)) {
return 443;
} else {
return -1;

View File

@ -26,45 +26,48 @@
*/
package org.apache.hc.client5.http.impl;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.apache.hc.core5.annotation.Internal;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.net.URIAuthority;
import org.apache.hc.core5.net.PercentCodec;
import org.apache.hc.core5.net.URIBuilder;
/**
* Protocol support methods.
* Protocol support methods. For internal use only.
*
* @since 5.1
* @since 5.2
*/
@Internal
public final class ProtocolSupport {
public final class RequestSupport {
public static String getRequestUri(final HttpRequest request) {
final URIAuthority authority = request.getAuthority();
if (authority != null) {
final StringBuilder buf = new StringBuilder();
final String scheme = request.getScheme();
buf.append(scheme != null ? scheme : URIScheme.HTTP.id);
buf.append("://");
if (authority.getUserInfo() != null) {
buf.append(authority.getUserInfo());
buf.append("@");
public static String extractPathPrefix(final HttpRequest request) {
final String path = request.getPath();
try {
final URIBuilder uriBuilder = new URIBuilder(path);
uriBuilder.setFragment(null);
uriBuilder.clearParameters();
uriBuilder.normalizeSyntax();
final List<String> pathSegments = uriBuilder.getPathSegments();
if (!pathSegments.isEmpty()) {
pathSegments.remove(pathSegments.size() - 1);
}
buf.append(authority.getHostName());
if (authority.getPort() != -1) {
buf.append(":");
buf.append(authority.getPort());
if (pathSegments.isEmpty()) {
return "/";
} else {
final StringBuilder buf = new StringBuilder();
buf.append('/');
for (final String pathSegment : pathSegments) {
PercentCodec.encode(buf, pathSegment, StandardCharsets.US_ASCII);
buf.append('/');
}
return buf.toString();
}
final String path = request.getPath();
if (path == null || !path.startsWith("/")) {
buf.append("/");
}
if (path != null) {
buf.append(path);
}
return buf.toString();
} else {
return request.getPath();
} catch (final URISyntaxException ex) {
return path;
}
}

View File

@ -379,7 +379,7 @@ private void createTunnel(
final AuthExchange proxyAuthExchange = proxy != null ? clientContext.getAuthExchange(proxy) : new AuthExchange();
if (authCacheKeeper != null) {
authCacheKeeper.loadPreemptively(proxy, proxyAuthExchange, clientContext);
authCacheKeeper.loadPreemptively(proxy, null, proxyAuthExchange, clientContext);
}
final HttpRequest connect = new BasicHttpRequest(Method.CONNECT, nextHop, nextHop.toHostString());
@ -444,9 +444,9 @@ private boolean needAuthentication(
if (authCacheKeeper != null) {
if (proxyAuthRequested) {
authCacheKeeper.updateOnChallenge(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnChallenge(proxy, null, proxyAuthExchange, context);
} else {
authCacheKeeper.updateOnNoChallenge(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnNoChallenge(proxy, null, proxyAuthExchange, context);
}
}
@ -455,7 +455,7 @@ private boolean needAuthentication(
proxyAuthStrategy, proxyAuthExchange, context);
if (authCacheKeeper != null) {
authCacheKeeper.updateOnResponse(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnResponse(proxy, null, proxyAuthExchange, context);
}
return updated;

View File

@ -43,6 +43,7 @@
import org.apache.hc.client5.http.auth.CredentialsStore;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.AuthSupport;
import org.apache.hc.client5.http.impl.RequestSupport;
import org.apache.hc.client5.http.impl.auth.AuthCacheKeeper;
import org.apache.hc.client5.http.impl.auth.HttpAuthenticator;
import org.apache.hc.client5.http.protocol.HttpClientContext;
@ -148,23 +149,36 @@ public void execute(
}
final HttpHost target = new HttpHost(request.getScheme(), request.getAuthority());
final String pathPrefix = RequestSupport.extractPathPrefix(request);
final AuthExchange targetAuthExchange = clientContext.getAuthExchange(target);
final AuthExchange proxyAuthExchange = proxy != null ? clientContext.getAuthExchange(proxy) : new AuthExchange();
if (!targetAuthExchange.isConnectionBased() &&
targetAuthExchange.getPathPrefix() != null &&
!pathPrefix.startsWith(targetAuthExchange.getPathPrefix())) {
// force re-authentication if the current path prefix does not match
// that of the previous authentication exchange.
targetAuthExchange.reset();
}
if (targetAuthExchange.getPathPrefix() == null) {
targetAuthExchange.setPathPrefix(pathPrefix);
}
if (authCacheKeeper != null) {
authCacheKeeper.loadPreemptively(target, targetAuthExchange, clientContext);
authCacheKeeper.loadPreemptively(target, pathPrefix, targetAuthExchange, clientContext);
if (proxy != null) {
authCacheKeeper.loadPreemptively(proxy, proxyAuthExchange, clientContext);
authCacheKeeper.loadPreemptively(proxy, null, proxyAuthExchange, clientContext);
}
}
final AtomicBoolean challenged = new AtomicBoolean(false);
internalExecute(target, targetAuthExchange, proxyAuthExchange,
internalExecute(target, pathPrefix, targetAuthExchange, proxyAuthExchange,
challenged, request, entityProducer, scope, chain, asyncExecCallback);
}
private void internalExecute(
final HttpHost target,
final String pathPrefix,
final AuthExchange targetAuthExchange,
final AuthExchange proxyAuthExchange,
final AtomicBoolean challenged,
@ -216,6 +230,7 @@ public AsyncDataConsumer handleResponse(
proxyAuthExchange,
proxy != null ? proxy : target,
target,
pathPrefix,
response,
clientContext)) {
challenged.set(true);
@ -267,7 +282,7 @@ public void completed() {
if (entityProducer != null) {
entityProducer.releaseResources();
}
internalExecute(target, targetAuthExchange, proxyAuthExchange,
internalExecute(target, pathPrefix, targetAuthExchange, proxyAuthExchange,
challenged, request, entityProducer, scope, chain, asyncExecCallback);
} catch (final HttpException | IOException ex) {
asyncExecCallback.failed(ex);
@ -298,6 +313,7 @@ private boolean needAuthentication(
final AuthExchange proxyAuthExchange,
final HttpHost proxy,
final HttpHost target,
final String pathPrefix,
final HttpResponse response,
final HttpClientContext context) {
final RequestConfig config = context.getRequestConfig();
@ -307,9 +323,9 @@ private boolean needAuthentication(
if (authCacheKeeper != null) {
if (targetAuthRequested) {
authCacheKeeper.updateOnChallenge(target, targetAuthExchange, context);
authCacheKeeper.updateOnChallenge(target, pathPrefix, targetAuthExchange, context);
} else {
authCacheKeeper.updateOnNoChallenge(target, targetAuthExchange, context);
authCacheKeeper.updateOnNoChallenge(target, pathPrefix, targetAuthExchange, context);
}
}
@ -318,9 +334,9 @@ private boolean needAuthentication(
if (authCacheKeeper != null) {
if (proxyAuthRequested) {
authCacheKeeper.updateOnChallenge(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnChallenge(proxy, null, proxyAuthExchange, context);
} else {
authCacheKeeper.updateOnNoChallenge(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnNoChallenge(proxy, null, proxyAuthExchange, context);
}
}
@ -329,7 +345,7 @@ private boolean needAuthentication(
targetAuthStrategy, targetAuthExchange, context);
if (authCacheKeeper != null) {
authCacheKeeper.updateOnResponse(target, targetAuthExchange, context);
authCacheKeeper.updateOnResponse(target, pathPrefix, targetAuthExchange, context);
}
return updated;
@ -339,7 +355,7 @@ private boolean needAuthentication(
proxyAuthStrategy, proxyAuthExchange, context);
if (authCacheKeeper != null) {
authCacheKeeper.updateOnResponse(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnResponse(proxy, null, proxyAuthExchange, context);
}
return updated;

View File

@ -59,32 +59,39 @@ public AuthCacheKeeper(final SchemePortResolver schemePortResolver) {
}
public void updateOnChallenge(final HttpHost host,
final String pathPrefix,
final AuthExchange authExchange,
final HttpContext context) {
clearCache(host, HttpClientContext.adapt(context));
clearCache(host, pathPrefix, HttpClientContext.adapt(context));
}
public void updateOnNoChallenge(final HttpHost host,
final String pathPrefix,
final AuthExchange authExchange,
final HttpContext context) {
if (authExchange.getState() == AuthExchange.State.SUCCESS) {
updateCache(host, authExchange.getAuthScheme(), HttpClientContext.adapt(context));
updateCache(host, pathPrefix, authExchange.getAuthScheme(), HttpClientContext.adapt(context));
}
}
public void updateOnResponse(final HttpHost host,
final String pathPrefix,
final AuthExchange authExchange,
final HttpContext context) {
if (authExchange.getState() == AuthExchange.State.FAILURE) {
clearCache(host, HttpClientContext.adapt(context));
clearCache(host, pathPrefix, HttpClientContext.adapt(context));
}
}
public void loadPreemptively(final HttpHost host,
final String pathPrefix,
final AuthExchange authExchange,
final HttpContext context) {
if (authExchange.getState() == AuthExchange.State.UNCHALLENGED) {
final AuthScheme authScheme = loadFromCache(host, HttpClientContext.adapt(context));
AuthScheme authScheme = loadFromCache(host, pathPrefix, HttpClientContext.adapt(context));
if (authScheme == null && pathPrefix != null) {
authScheme = loadFromCache(host, null, HttpClientContext.adapt(context));
}
if (authScheme != null) {
authExchange.select(authScheme);
}
@ -92,14 +99,16 @@ public void loadPreemptively(final HttpHost host,
}
private AuthScheme loadFromCache(final HttpHost host,
final String pathPrefix,
final HttpClientContext clientContext) {
final AuthCache authCache = clientContext.getAuthCache();
if (authCache != null) {
final AuthScheme authScheme = authCache.get(host);
final AuthScheme authScheme = authCache.get(host, pathPrefix);
if (authScheme != null) {
if (LOG.isDebugEnabled()) {
final String exchangeId = clientContext.getExchangeId();
LOG.debug("{} Re-using cached '{}' auth scheme for {}", exchangeId, authScheme.getName(), host);
LOG.debug("{} Re-using cached '{}' auth scheme for {}{}", exchangeId, authScheme.getName(), host,
pathPrefix != null ? pathPrefix : "");
}
return authScheme;
}
@ -108,6 +117,7 @@ private AuthScheme loadFromCache(final HttpHost host,
}
private void updateCache(final HttpHost host,
final String pathPrefix,
final AuthScheme authScheme,
final HttpClientContext clientContext) {
final boolean cacheable = authScheme.getClass().getAnnotation(AuthStateCacheable.class) != null;
@ -119,21 +129,24 @@ private void updateCache(final HttpHost host,
}
if (LOG.isDebugEnabled()) {
final String exchangeId = clientContext.getExchangeId();
LOG.debug("{} Caching '{}' auth scheme for {}", exchangeId, authScheme.getName(), host);
LOG.debug("{} Caching '{}' auth scheme for {}{}", exchangeId, authScheme.getName(), host,
pathPrefix != null ? pathPrefix : "");
}
authCache.put(host, authScheme);
authCache.put(host, pathPrefix, authScheme);
}
}
private void clearCache(final HttpHost host,
final String pathPrefix,
final HttpClientContext clientContext) {
final AuthCache authCache = clientContext.getAuthCache();
if (authCache != null) {
if (LOG.isDebugEnabled()) {
final String exchangeId = clientContext.getExchangeId();
LOG.debug("{} Clearing cached auth scheme for {}", exchangeId, host);
LOG.debug("{} Clearing cached auth scheme for {}{}", exchangeId, host,
pathPrefix != null ? pathPrefix : "");
}
authCache.remove(host);
authCache.remove(host, pathPrefix);
}
}

View File

@ -32,6 +32,7 @@
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -39,11 +40,12 @@
import org.apache.hc.client5.http.auth.AuthCache;
import org.apache.hc.client5.http.auth.AuthScheme;
import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
import org.apache.hc.client5.http.routing.RoutingSupport;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.net.NamedEndpoint;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.LangUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -62,7 +64,65 @@ public class BasicAuthCache implements AuthCache {
private static final Logger LOG = LoggerFactory.getLogger(BasicAuthCache.class);
private final Map<HttpHost, byte[]> map;
static class Key {
final String scheme;
final String host;
final int port;
final String pathPrefix;
Key(final String scheme, final String host, final int port, final String pathPrefix) {
Args.notBlank(scheme, "Scheme");
Args.notBlank(host, "Scheme");
this.scheme = scheme.toLowerCase(Locale.ROOT);
this.host = host.toLowerCase(Locale.ROOT);
this.port = port;
this.pathPrefix = pathPrefix;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Key) {
final Key that = (Key) obj;
return this.scheme.equals(that.scheme) &&
this.host.equals(that.host) &&
this.port == that.port &&
LangUtils.equals(this.pathPrefix, that.pathPrefix);
}
return false;
}
@Override
public int hashCode() {
int hash = LangUtils.HASH_SEED;
hash = LangUtils.hashCode(hash, this.scheme);
hash = LangUtils.hashCode(hash, this.host);
hash = LangUtils.hashCode(hash, this.port);
hash = LangUtils.hashCode(hash, this.pathPrefix);
return hash;
}
@Override
public String toString() {
final StringBuilder buf = new StringBuilder();
buf.append(scheme).append("://").append(host);
if (port >= 0) {
buf.append(":").append(port);
}
if (pathPrefix != null) {
if (!pathPrefix.startsWith("/")) {
buf.append("/");
}
buf.append(pathPrefix);
}
return buf.toString();
}
}
private final Map<Key, byte[]> map;
private final SchemePortResolver schemePortResolver;
/**
@ -80,8 +140,27 @@ public BasicAuthCache() {
this(null);
}
private Key key(final String scheme, final NamedEndpoint authority, final String pathPrefix) {
return new Key(scheme, authority.getHostName(), schemePortResolver.resolve(scheme, authority), pathPrefix);
}
@Override
public void put(final HttpHost host, final AuthScheme authScheme) {
put(host, null, authScheme);
}
@Override
public AuthScheme get(final HttpHost host) {
return get(host, null);
}
@Override
public void remove(final HttpHost host) {
remove(host, null);
}
@Override
public void put(final HttpHost host, final String pathPrefix, final AuthScheme authScheme) {
Args.notNull(host, "HTTP host");
if (authScheme == null) {
return;
@ -92,8 +171,7 @@ public void put(final HttpHost host, final AuthScheme authScheme) {
try (final ObjectOutputStream out = new ObjectOutputStream(buf)) {
out.writeObject(authScheme);
}
final HttpHost key = RoutingSupport.normalize(host, schemePortResolver);
this.map.put(key, buf.toByteArray());
this.map.put(key(host.getSchemeName(), host, pathPrefix), buf.toByteArray());
} catch (final IOException ex) {
if (LOG.isWarnEnabled()) {
LOG.warn("Unexpected I/O error while serializing auth scheme", ex);
@ -107,10 +185,9 @@ public void put(final HttpHost host, final AuthScheme authScheme) {
}
@Override
public AuthScheme get(final HttpHost host) {
public AuthScheme get(final HttpHost host, final String pathPrefix) {
Args.notNull(host, "HTTP host");
final HttpHost key = RoutingSupport.normalize(host, schemePortResolver);
final byte[] bytes = this.map.get(key);
final byte[] bytes = this.map.get(key(host.getSchemeName(), host, pathPrefix));
if (bytes != null) {
try {
final ByteArrayInputStream buf = new ByteArrayInputStream(bytes);
@ -131,10 +208,9 @@ public AuthScheme get(final HttpHost host) {
}
@Override
public void remove(final HttpHost host) {
public void remove(final HttpHost host, final String pathPrefix) {
Args.notNull(host, "HTTP host");
final HttpHost key = RoutingSupport.normalize(host, schemePortResolver);
this.map.remove(key);
this.map.remove(key(host.getSchemeName(), host, pathPrefix));
}
@Override

View File

@ -215,7 +215,7 @@ private boolean createTunnelToTarget(
final AuthExchange proxyAuthExchange = context.getAuthExchange(proxy);
if (authCacheKeeper != null) {
authCacheKeeper.loadPreemptively(proxy, proxyAuthExchange, context);
authCacheKeeper.loadPreemptively(proxy, null, proxyAuthExchange, context);
}
ClassicHttpResponse response = null;
@ -254,9 +254,9 @@ private boolean createTunnelToTarget(
if (authCacheKeeper != null) {
if (proxyAuthRequested) {
authCacheKeeper.updateOnChallenge(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnChallenge(proxy, null, proxyAuthExchange, context);
} else {
authCacheKeeper.updateOnNoChallenge(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnNoChallenge(proxy, null, proxyAuthExchange, context);
}
}
@ -265,7 +265,7 @@ private boolean createTunnelToTarget(
proxyAuthStrategy, proxyAuthExchange, context);
if (authCacheKeeper != null) {
authCacheKeeper.updateOnResponse(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnResponse(proxy, null, proxyAuthExchange, context);
}
if (updated) {
// Retry request

View File

@ -42,6 +42,7 @@
import org.apache.hc.client5.http.classic.ExecRuntime;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.AuthSupport;
import org.apache.hc.client5.http.impl.RequestSupport;
import org.apache.hc.client5.http.impl.auth.AuthCacheKeeper;
import org.apache.hc.client5.http.impl.auth.HttpAuthenticator;
import org.apache.hc.client5.http.protocol.HttpClientContext;
@ -151,14 +152,26 @@ public ClassicHttpResponse execute(
}
final HttpHost target = new HttpHost(request.getScheme(), request.getAuthority());
final String pathPrefix = RequestSupport.extractPathPrefix(request);
final AuthExchange targetAuthExchange = context.getAuthExchange(target);
final AuthExchange proxyAuthExchange = proxy != null ? context.getAuthExchange(proxy) : new AuthExchange();
if (!targetAuthExchange.isConnectionBased() &&
targetAuthExchange.getPathPrefix() != null &&
!pathPrefix.startsWith(targetAuthExchange.getPathPrefix())) {
// force re-authentication if the current path prefix does not match
// that of the previous authentication exchange.
targetAuthExchange.reset();
}
if (targetAuthExchange.getPathPrefix() == null) {
targetAuthExchange.setPathPrefix(pathPrefix);
}
if (authCacheKeeper != null) {
authCacheKeeper.loadPreemptively(target, targetAuthExchange, context);
authCacheKeeper.loadPreemptively(target, pathPrefix, targetAuthExchange, context);
if (proxy != null) {
authCacheKeeper.loadPreemptively(proxy, proxyAuthExchange, context);
authCacheKeeper.loadPreemptively(proxy, null, proxyAuthExchange, context);
}
}
@ -206,6 +219,7 @@ public ClassicHttpResponse execute(
proxyAuthExchange,
proxy != null ? proxy : target,
target,
pathPrefix,
response,
context)) {
// Make sure the response body is fully consumed, if present
@ -259,6 +273,7 @@ private boolean needAuthentication(
final AuthExchange proxyAuthExchange,
final HttpHost proxy,
final HttpHost target,
final String pathPrefix,
final HttpResponse response,
final HttpClientContext context) {
final RequestConfig config = context.getRequestConfig();
@ -268,9 +283,9 @@ private boolean needAuthentication(
if (authCacheKeeper != null) {
if (targetAuthRequested) {
authCacheKeeper.updateOnChallenge(target, targetAuthExchange, context);
authCacheKeeper.updateOnChallenge(target, pathPrefix, targetAuthExchange, context);
} else {
authCacheKeeper.updateOnNoChallenge(target, targetAuthExchange, context);
authCacheKeeper.updateOnNoChallenge(target, pathPrefix, targetAuthExchange, context);
}
}
@ -279,9 +294,9 @@ private boolean needAuthentication(
if (authCacheKeeper != null) {
if (proxyAuthRequested) {
authCacheKeeper.updateOnChallenge(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnChallenge(proxy, null, proxyAuthExchange, context);
} else {
authCacheKeeper.updateOnNoChallenge(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnNoChallenge(proxy, null, proxyAuthExchange, context);
}
}
@ -290,7 +305,7 @@ private boolean needAuthentication(
targetAuthStrategy, targetAuthExchange, context);
if (authCacheKeeper != null) {
authCacheKeeper.updateOnResponse(target, targetAuthExchange, context);
authCacheKeeper.updateOnResponse(target, pathPrefix, targetAuthExchange, context);
}
return updated;
@ -300,7 +315,7 @@ private boolean needAuthentication(
proxyAuthStrategy, proxyAuthExchange, context);
if (authCacheKeeper != null) {
authCacheKeeper.updateOnResponse(proxy, proxyAuthExchange, context);
authCacheKeeper.updateOnResponse(proxy, null, proxyAuthExchange, context);
}
return updated;

View File

@ -1,55 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.impl;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.Method;
import org.apache.hc.core5.http.message.BasicHttpRequest;
import org.apache.hc.core5.net.URIAuthority;
import org.hamcrest.CoreMatchers;
import org.hamcrest.MatcherAssert;
import org.junit.Test;
/**
* Simple tests for {@link ProtocolSupport}.
*/
public class TestProtocolSupport {
@Test
public void testGetRequestUri() {
final HttpRequest request = new BasicHttpRequest(Method.GET, "");
MatcherAssert.assertThat(ProtocolSupport.getRequestUri(request), CoreMatchers.equalTo("/"));
request.setAuthority(new URIAuthority("testUser", "localhost", 8080));
MatcherAssert.assertThat(ProtocolSupport.getRequestUri(request), CoreMatchers.equalTo("http://testUser@localhost:8080/"));
request.setScheme("https");
MatcherAssert.assertThat(ProtocolSupport.getRequestUri(request), CoreMatchers.equalTo("https://testUser@localhost:8080/"));
request.setPath("blah");
MatcherAssert.assertThat(ProtocolSupport.getRequestUri(request), CoreMatchers.equalTo("https://testUser@localhost:8080/blah"));
request.setPath("/blah/blah");
MatcherAssert.assertThat(ProtocolSupport.getRequestUri(request), CoreMatchers.equalTo("https://testUser@localhost:8080/blah/blah"));
}
}

View File

@ -0,0 +1,53 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.impl;
import org.apache.hc.core5.http.message.BasicHttpRequest;
import org.junit.Assert;
import org.junit.Test;
/**
* Simple tests for {@link RequestSupport}.
*/
public class TestRequestSupport {
@Test
public void testPathPrefixExtraction() {
Assert.assertEquals("/aaaa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/bbbb")));
Assert.assertEquals("/aaaa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/")));
Assert.assertEquals("/aaaa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/../aaaa/")));
Assert.assertEquals("/aaaa/bbbb/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/bbbb/cccc")));
Assert.assertEquals("/aaaa/bbbb/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/bbbb/")));
Assert.assertEquals("/aaaa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa/bbbb?////")));
Assert.assertEquals("/aa%2Faa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aa%2faa/bbbb")));
Assert.assertEquals("/aa%2Faa/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/a%61%2fa%61/bbbb")));
Assert.assertEquals("/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/")));
Assert.assertEquals("/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "/aaaa")));
Assert.assertEquals("/", RequestSupport.extractPathPrefix(new BasicHttpRequest("GET", "")));
}
}

View File

@ -67,7 +67,7 @@ public void testNullAuthScheme() throws Exception {
}
@Test
public void testStoreNonserializable() throws Exception {
public void testStoreNonSerializable() throws Exception {
final BasicAuthCache cache = new BasicAuthCache();
final AuthScheme authScheme = new NTLMScheme();
cache.put(new HttpHost("localhost", 80), authScheme);

View File

@ -1,188 +0,0 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.impl.auth;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.auth.AuthCache;
import org.apache.hc.client5.http.auth.AuthExchange;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.Credentials;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.protocol.RequestAuthCache;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpRequestInterceptor;
import org.apache.hc.core5.http.message.BasicHttpRequest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class TestRequestAuthCache {
private HttpHost target;
private HttpHost proxy;
private Credentials creds1;
private Credentials creds2;
private AuthScope authscope1;
private AuthScope authscope2;
private BasicScheme authscheme1;
private BasicScheme authscheme2;
private CredentialsProvider credProvider;
@Before
public void setUp() {
this.target = new HttpHost("localhost", 80);
this.proxy = new HttpHost("localhost", 8080);
this.creds1 = new UsernamePasswordCredentials("user1", "secret1".toCharArray());
this.creds2 = new UsernamePasswordCredentials("user2", "secret2".toCharArray());
this.authscope1 = new AuthScope(this.target);
this.authscope2 = new AuthScope(this.proxy);
this.authscheme1 = new BasicScheme();
this.authscheme2 = new BasicScheme();
this.credProvider = CredentialsProviderBuilder.create()
.add(this.authscope1, this.creds1)
.add(this.authscope2, this.creds2)
.build();
}
@Test
public void testRequestParameterCheck() throws Exception {
final HttpClientContext context = HttpClientContext.create();
final HttpRequestInterceptor interceptor = new RequestAuthCache();
Assert.assertThrows(NullPointerException.class, () ->
interceptor.process(null, null, context));
}
@Test
public void testContextParameterCheck() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/");
final HttpRequestInterceptor interceptor = new RequestAuthCache();
Assert.assertThrows(NullPointerException.class, () ->
interceptor.process(request, null, null));
}
@Test
public void testPreemptiveTargetAndProxyAuth() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/");
final HttpClientContext context = HttpClientContext.create();
context.setAttribute(HttpClientContext.CREDS_PROVIDER, this.credProvider);
context.setAttribute(HttpClientContext.HTTP_ROUTE, new HttpRoute(this.target, null, this.proxy, false));
final AuthCache authCache = new BasicAuthCache();
authCache.put(this.target, this.authscheme1);
authCache.put(this.proxy, this.authscheme2);
context.setAttribute(HttpClientContext.AUTH_CACHE, authCache);
final HttpRequestInterceptor interceptor = new RequestAuthCache();
interceptor.process(request, null, context);
final AuthExchange targetAuthExchange = context.getAuthExchange(this.target);
final AuthExchange proxyAuthExchange = context.getAuthExchange(this.proxy);
Assert.assertNotNull(targetAuthExchange);
Assert.assertNotNull(targetAuthExchange.getAuthScheme());
Assert.assertNotNull(proxyAuthExchange);
Assert.assertNotNull(proxyAuthExchange.getAuthScheme());
}
@Test
public void testCredentialsProviderNotSet() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/");
final HttpClientContext context = HttpClientContext.create();
context.setAttribute(HttpClientContext.CREDS_PROVIDER, null);
context.setAttribute(HttpClientContext.HTTP_ROUTE, new HttpRoute(this.target, null, this.proxy, false));
final AuthCache authCache = new BasicAuthCache();
authCache.put(this.target, this.authscheme1);
authCache.put(this.proxy, this.authscheme2);
context.setAttribute(HttpClientContext.AUTH_CACHE, authCache);
final HttpRequestInterceptor interceptor = new RequestAuthCache();
interceptor.process(request, null, context);
final AuthExchange targetAuthExchange = context.getAuthExchange(this.target);
final AuthExchange proxyAuthExchange = context.getAuthExchange(this.proxy);
Assert.assertNotNull(targetAuthExchange);
Assert.assertNull(targetAuthExchange.getAuthScheme());
Assert.assertNotNull(proxyAuthExchange);
Assert.assertNull(proxyAuthExchange.getAuthScheme());
}
@Test
public void testAuthCacheNotSet() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/");
final HttpClientContext context = HttpClientContext.create();
context.setAttribute(HttpClientContext.CREDS_PROVIDER, this.credProvider);
context.setAttribute(HttpClientContext.HTTP_ROUTE, new HttpRoute(this.target, null, this.proxy, false));
context.setAttribute(HttpClientContext.AUTH_CACHE, null);
final HttpRequestInterceptor interceptor = new RequestAuthCache();
interceptor.process(request, null, context);
final AuthExchange targetAuthExchange = context.getAuthExchange(this.target);
final AuthExchange proxyAuthExchange = context.getAuthExchange(this.proxy);
Assert.assertNotNull(targetAuthExchange);
Assert.assertNull(targetAuthExchange.getAuthScheme());
Assert.assertNotNull(proxyAuthExchange);
Assert.assertNull(proxyAuthExchange.getAuthScheme());
}
@Test
public void testAuthCacheEmpty() throws Exception {
final HttpRequest request = new BasicHttpRequest("GET", "/");
final HttpClientContext context = HttpClientContext.create();
context.setAttribute(HttpClientContext.CREDS_PROVIDER, this.credProvider);
context.setAttribute(HttpClientContext.HTTP_ROUTE, new HttpRoute(this.target, null, this.proxy, false));
final AuthCache authCache = new BasicAuthCache();
context.setAttribute(HttpClientContext.AUTH_CACHE, authCache);
final HttpRequestInterceptor interceptor = new RequestAuthCache();
interceptor.process(request, null, context);
final AuthExchange targetAuthExchange = context.getAuthExchange(this.target);
final AuthExchange proxyAuthExchange = context.getAuthExchange(this.proxy);
Assert.assertNotNull(targetAuthExchange);
Assert.assertNull(targetAuthExchange.getAuthScheme());
Assert.assertNotNull(proxyAuthExchange);
Assert.assertNull(proxyAuthExchange.getAuthScheme());
}
}