diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java
index b0288c75c..4f83962ee 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java
@@ -33,6 +33,7 @@ import java.io.InterruptedIOException;
import org.apache.hc.client5.http.AuthenticationStrategy;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.RouteTracker;
+import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.async.AsyncExecCallback;
import org.apache.hc.client5.http.async.AsyncExecChain;
import org.apache.hc.client5.http.async.AsyncExecChainHandler;
@@ -41,6 +42,7 @@ import org.apache.hc.client5.http.auth.AuthExchange;
import org.apache.hc.client5.http.auth.ChallengeType;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.TunnelRefusedException;
+import org.apache.hc.client5.http.impl.auth.AuthCacheKeeper;
import org.apache.hc.client5.http.impl.auth.HttpAuthenticator;
import org.apache.hc.client5.http.impl.routing.BasicRouteDirector;
import org.apache.hc.client5.http.protocol.HttpClientContext;
@@ -84,17 +86,21 @@ public final class AsyncConnectExec implements AsyncExecChainHandler {
private final HttpProcessor proxyHttpProcessor;
private final AuthenticationStrategy proxyAuthStrategy;
private final HttpAuthenticator authenticator;
+ private final AuthCacheKeeper authCacheKeeper;
private final HttpRouteDirector routeDirector;
public AsyncConnectExec(
final HttpProcessor proxyHttpProcessor,
- final AuthenticationStrategy proxyAuthStrategy) {
+ final AuthenticationStrategy proxyAuthStrategy,
+ final SchemePortResolver schemePortResolver,
+ final boolean authCachingDisabled) {
Args.notNull(proxyHttpProcessor, "Proxy HTTP processor");
Args.notNull(proxyAuthStrategy, "Proxy authentication strategy");
this.proxyHttpProcessor = proxyHttpProcessor;
this.proxyAuthStrategy = proxyAuthStrategy;
- this.authenticator = new HttpAuthenticator(LOG);
- this.routeDirector = new BasicRouteDirector();
+ this.authenticator = new HttpAuthenticator();
+ this.authCacheKeeper = authCachingDisabled ? null : new AuthCacheKeeper(schemePortResolver);
+ this.routeDirector = new BasicRouteDirector();
}
static class State {
@@ -372,6 +378,10 @@ public final class AsyncConnectExec implements AsyncExecChainHandler {
final AuthExchange proxyAuthExchange = proxy != null ? clientContext.getAuthExchange(proxy) : new AuthExchange();
+ if (authCacheKeeper != null) {
+ authCacheKeeper.loadPreemptively(proxy, proxyAuthExchange, clientContext);
+ }
+
final HttpRequest connect = new BasicHttpRequest(Method.CONNECT, nextHop, nextHop.toHostString());
connect.setVersion(HttpVersion.HTTP_1_1);
@@ -431,9 +441,24 @@ public final class AsyncConnectExec implements AsyncExecChainHandler {
final RequestConfig config = context.getRequestConfig();
if (config.isAuthenticationEnabled()) {
final boolean proxyAuthRequested = authenticator.isChallenged(proxy, ChallengeType.PROXY, response, proxyAuthExchange, context);
+
+ if (authCacheKeeper != null) {
+ if (proxyAuthRequested) {
+ authCacheKeeper.updateOnChallenge(proxy, proxyAuthExchange, context);
+ } else {
+ authCacheKeeper.updateOnNoChallenge(proxy, proxyAuthExchange, context);
+ }
+ }
+
if (proxyAuthRequested) {
- return authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
+ final boolean updated = authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
proxyAuthStrategy, proxyAuthExchange, context);
+
+ if (authCacheKeeper != null) {
+ authCacheKeeper.updateOnResponse(proxy, proxyAuthExchange, context);
+ }
+
+ return updated;
}
}
return false;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java
index 880d9e5f8..4fe36378b 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java
@@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.hc.client5.http.AuthenticationStrategy;
import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.async.AsyncExecCallback;
import org.apache.hc.client5.http.async.AsyncExecChain;
import org.apache.hc.client5.http.async.AsyncExecChainHandler;
@@ -42,6 +43,7 @@ import org.apache.hc.client5.http.auth.CredentialsProvider;
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.auth.AuthCacheKeeper;
import org.apache.hc.client5.http.impl.auth.HttpAuthenticator;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.annotation.Contract;
@@ -87,15 +89,19 @@ public final class AsyncProtocolExec implements AsyncExecChainHandler {
private final AuthenticationStrategy targetAuthStrategy;
private final AuthenticationStrategy proxyAuthStrategy;
private final HttpAuthenticator authenticator;
+ private final AuthCacheKeeper authCacheKeeper;
AsyncProtocolExec(
final HttpProcessor httpProcessor,
final AuthenticationStrategy targetAuthStrategy,
- final AuthenticationStrategy proxyAuthStrategy) {
+ final AuthenticationStrategy proxyAuthStrategy,
+ final SchemePortResolver schemePortResolver,
+ final boolean authCachingDisabled) {
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(LOG);
+ this.authenticator = new HttpAuthenticator();
+ this.authCacheKeeper = authCachingDisabled ? null : new AuthCacheKeeper(schemePortResolver);
}
@Override
@@ -141,11 +147,26 @@ public final class AsyncProtocolExec implements AsyncExecChainHandler {
AuthSupport.extractFromAuthority(request.getScheme(), authority, (CredentialsStore) credsProvider);
}
+ final HttpHost target = new HttpHost(request.getScheme(), request.getAuthority());
+ final AuthExchange targetAuthExchange = clientContext.getAuthExchange(target);
+ final AuthExchange proxyAuthExchange = proxy != null ? clientContext.getAuthExchange(proxy) : new AuthExchange();
+
+ if (authCacheKeeper != null) {
+ authCacheKeeper.loadPreemptively(target, targetAuthExchange, clientContext);
+ if (proxy != null) {
+ authCacheKeeper.loadPreemptively(proxy, proxyAuthExchange, clientContext);
+ }
+ }
+
final AtomicBoolean challenged = new AtomicBoolean(false);
- internalExecute(challenged, request, entityProducer, scope, chain, asyncExecCallback);
+ internalExecute(target, targetAuthExchange, proxyAuthExchange,
+ challenged, request, entityProducer, scope, chain, asyncExecCallback);
}
private void internalExecute(
+ final HttpHost target,
+ final AuthExchange targetAuthExchange,
+ final AuthExchange proxyAuthExchange,
final AtomicBoolean challenged,
final HttpRequest request,
final AsyncEntityProducer entityProducer,
@@ -158,10 +179,6 @@ public final class AsyncProtocolExec implements AsyncExecChainHandler {
final AsyncExecRuntime execRuntime = scope.execRuntime;
final HttpHost proxy = route.getProxyHost();
- final HttpHost target = new HttpHost(request.getScheme(), request.getAuthority());
-
- 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);
@@ -194,7 +211,13 @@ public final class AsyncProtocolExec implements AsyncExecChainHandler {
// Do not perform authentication for TRACE request
return asyncExecCallback.handleResponse(response, entityDetails);
}
- if (needAuthentication(targetAuthExchange, proxyAuthExchange, route, request, response, clientContext)) {
+ if (needAuthentication(
+ targetAuthExchange,
+ proxyAuthExchange,
+ proxy != null ? proxy : target,
+ target,
+ response,
+ clientContext)) {
challenged.set(true);
return null;
}
@@ -244,7 +267,8 @@ public final class AsyncProtocolExec implements AsyncExecChainHandler {
if (entityProducer != null) {
entityProducer.releaseResources();
}
- internalExecute(challenged, request, entityProducer, scope, chain, asyncExecCallback);
+ internalExecute(target, targetAuthExchange, proxyAuthExchange,
+ challenged, request, entityProducer, scope, chain, asyncExecCallback);
} catch (final HttpException | IOException ex) {
asyncExecCallback.failed(ex);
}
@@ -272,31 +296,53 @@ public final class AsyncProtocolExec implements AsyncExecChainHandler {
private boolean needAuthentication(
final AuthExchange targetAuthExchange,
final AuthExchange proxyAuthExchange,
- final HttpRoute route,
- final HttpRequest request,
+ final HttpHost proxy,
+ final HttpHost target,
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();
+ if (authCacheKeeper != null) {
+ if (targetAuthRequested) {
+ authCacheKeeper.updateOnChallenge(target, targetAuthExchange, context);
+ } else {
+ authCacheKeeper.updateOnNoChallenge(target, targetAuthExchange, context);
+ }
}
+
final boolean proxyAuthRequested = authenticator.isChallenged(
proxy, ChallengeType.PROXY, response, proxyAuthExchange, context);
+ if (authCacheKeeper != null) {
+ if (proxyAuthRequested) {
+ authCacheKeeper.updateOnChallenge(proxy, proxyAuthExchange, context);
+ } else {
+ authCacheKeeper.updateOnNoChallenge(proxy, proxyAuthExchange, context);
+ }
+ }
+
if (targetAuthRequested) {
- return authenticator.updateAuthState(target, ChallengeType.TARGET, response,
+ final boolean updated = authenticator.updateAuthState(target, ChallengeType.TARGET, response,
targetAuthStrategy, targetAuthExchange, context);
+
+ if (authCacheKeeper != null) {
+ authCacheKeeper.updateOnResponse(target, targetAuthExchange, context);
+ }
+
+ return updated;
}
if (proxyAuthRequested) {
- return authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
+ final boolean updated = authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
proxyAuthStrategy, proxyAuthExchange, context);
+
+ if (authCacheKeeper != null) {
+ authCacheKeeper.updateOnResponse(proxy, proxyAuthExchange, context);
+ }
+
+ return updated;
}
}
return false;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java
index 494e925c3..098acddee 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java
@@ -67,7 +67,6 @@ import org.apache.hc.client5.http.impl.nio.MultihomeConnectionInitiator;
import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner;
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;
@@ -638,7 +637,9 @@ public class H2AsyncClientBuilder {
execChainDefinition.addFirst(
new AsyncConnectExec(
new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
- proxyAuthStrategyCopy),
+ proxyAuthStrategyCopy,
+ schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
+ authCachingDisabled),
ChainElement.CONNECT.name());
final HttpProcessorBuilder b = HttpProcessorBuilder.create();
@@ -663,9 +664,6 @@ public class H2AsyncClientBuilder {
if (!cookieManagementDisabled) {
b.add(new RequestAddCookies());
}
- if (!authCachingDisabled) {
- b.add(new RequestAuthCache());
- }
if (!cookieManagementDisabled) {
b.add(new ResponseProcessCookies());
}
@@ -686,7 +684,12 @@ public class H2AsyncClientBuilder {
final HttpProcessor httpProcessor = b.build();
execChainDefinition.addFirst(
- new AsyncProtocolExec(httpProcessor, targetAuthStrategyCopy, proxyAuthStrategyCopy),
+ new AsyncProtocolExec(
+ httpProcessor,
+ targetAuthStrategyCopy,
+ proxyAuthStrategyCopy,
+ schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
+ authCachingDisabled),
ChainElement.PROTOCOL.name());
// Add request retry executor, if not disabled
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
index 44a675908..6127ce08e 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
@@ -75,7 +75,6 @@ import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
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;
@@ -778,7 +777,9 @@ public class HttpAsyncClientBuilder {
execChainDefinition.addFirst(
new AsyncConnectExec(
new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
- proxyAuthStrategyCopy),
+ proxyAuthStrategyCopy,
+ schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
+ authCachingDisabled),
ChainElement.CONNECT.name());
final HttpProcessorBuilder b = HttpProcessorBuilder.create();
@@ -803,9 +804,6 @@ public class HttpAsyncClientBuilder {
if (!cookieManagementDisabled) {
b.add(new RequestAddCookies());
}
- if (!authCachingDisabled) {
- b.add(new RequestAuthCache());
- }
if (!cookieManagementDisabled) {
b.add(new ResponseProcessCookies());
}
@@ -826,7 +824,12 @@ public class HttpAsyncClientBuilder {
final HttpProcessor httpProcessor = b.build();
execChainDefinition.addFirst(
- new AsyncProtocolExec(httpProcessor, targetAuthStrategyCopy, proxyAuthStrategyCopy),
+ new AsyncProtocolExec(
+ httpProcessor,
+ targetAuthStrategyCopy,
+ proxyAuthStrategyCopy,
+ schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
+ authCachingDisabled),
ChainElement.PROTOCOL.name());
// Add request retry executor, if not disabled
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/AuthCacheKeeper.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/AuthCacheKeeper.java
new file mode 100644
index 000000000..01f959a8d
--- /dev/null
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/AuthCacheKeeper.java
@@ -0,0 +1,140 @@
+/*
+ * ====================================================================
+ * 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
+ *
+ * Please note that since version 5.2 this class no longer updated the authentication cache
+ * bound to the execution context.
*
* @since 4.3
*/
@@ -77,15 +77,9 @@ public final class HttpAuthenticator {
private final Logger log;
private final AuthChallengeParser parser;
- @Internal
- public HttpAuthenticator(final Logger log) {
- super();
- this.log = log != null ? log : DEFAULT_LOGGER;
- this.parser = new AuthChallengeParser();
- }
-
public HttpAuthenticator() {
- this(null);
+ this.log = DEFAULT_LOGGER;
+ this.parser = new AuthChallengeParser();
}
/**
@@ -124,9 +118,6 @@ public final class HttpAuthenticator {
if (log.isDebugEnabled()) {
log.debug("{} Authentication required", exchangeId);
}
- if (authExchange.getState() == AuthExchange.State.SUCCESS) {
- clearCache(host, clientContext);
- }
return true;
}
switch (authExchange.getState()) {
@@ -136,7 +127,6 @@ public final class HttpAuthenticator {
log.debug("{} Authentication succeeded", exchangeId);
}
authExchange.setState(AuthExchange.State.SUCCESS);
- updateCache(host, authExchange.getAuthScheme(), clientContext);
break;
case SUCCESS:
break;
@@ -213,7 +203,6 @@ public final class HttpAuthenticator {
if (log.isDebugEnabled()) {
log.debug("{} Response contains no valid authentication challenges", exchangeId);
}
- clearCache(host, clientContext);
authExchange.reset();
return false;
}
@@ -242,15 +231,14 @@ public final class HttpAuthenticator {
if (log.isWarnEnabled()) {
log.warn("{} {}", exchangeId, ex.getMessage());
}
- clearCache(host, clientContext);
authExchange.reset();
+ authExchange.setState(AuthExchange.State.FAILURE);
return false;
}
if (authScheme.isChallengeComplete()) {
if (log.isDebugEnabled()) {
log.debug("{} Authentication failed", exchangeId);
}
- clearCache(host, clientContext);
authExchange.reset();
authExchange.setState(AuthExchange.State.FAILURE);
return false;
@@ -376,32 +364,4 @@ public final class HttpAuthenticator {
}
}
- private void updateCache(final HttpHost host, final AuthScheme authScheme, final HttpClientContext clientContext) {
- final boolean cacheable = authScheme.getClass().getAnnotation(AuthStateCacheable.class) != null;
- if (cacheable) {
- AuthCache authCache = clientContext.getAuthCache();
- if (authCache == null) {
- authCache = new BasicAuthCache();
- clientContext.setAuthCache(authCache);
- }
- if (log.isDebugEnabled()) {
- final String exchangeId = clientContext.getExchangeId();
- log.debug("{} Caching '{}' auth scheme for {}", exchangeId, authScheme.getName(), host);
- }
- authCache.put(host, authScheme);
- }
- }
-
- private void clearCache(final HttpHost host, 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);
- }
- authCache.remove(host);
- }
- }
-
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java
index 357915fcb..5b80e21e8 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java
@@ -32,6 +32,7 @@ import java.io.IOException;
import org.apache.hc.client5.http.AuthenticationStrategy;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.RouteTracker;
+import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.auth.AuthExchange;
import org.apache.hc.client5.http.auth.ChallengeType;
import org.apache.hc.client5.http.classic.ExecChain;
@@ -39,6 +40,7 @@ import org.apache.hc.client5.http.classic.ExecChainHandler;
import org.apache.hc.client5.http.classic.ExecRuntime;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.TunnelRefusedException;
+import org.apache.hc.client5.http.impl.auth.AuthCacheKeeper;
import org.apache.hc.client5.http.impl.auth.HttpAuthenticator;
import org.apache.hc.client5.http.impl.routing.BasicRouteDirector;
import org.apache.hc.client5.http.protocol.HttpClientContext;
@@ -82,20 +84,24 @@ public final class ConnectExec implements ExecChainHandler {
private final HttpProcessor proxyHttpProcessor;
private final AuthenticationStrategy proxyAuthStrategy;
private final HttpAuthenticator authenticator;
+ private final AuthCacheKeeper authCacheKeeper;
private final HttpRouteDirector routeDirector;
public ConnectExec(
final ConnectionReuseStrategy reuseStrategy,
final HttpProcessor proxyHttpProcessor,
- final AuthenticationStrategy proxyAuthStrategy) {
+ final AuthenticationStrategy proxyAuthStrategy,
+ final SchemePortResolver schemePortResolver,
+ final boolean authCachingDisabled) {
Args.notNull(reuseStrategy, "Connection reuse strategy");
Args.notNull(proxyHttpProcessor, "Proxy HTTP processor");
Args.notNull(proxyAuthStrategy, "Proxy authentication strategy");
- this.reuseStrategy = reuseStrategy;
+ this.reuseStrategy = reuseStrategy;
this.proxyHttpProcessor = proxyHttpProcessor;
- this.proxyAuthStrategy = proxyAuthStrategy;
- this.authenticator = new HttpAuthenticator(LOG);
- this.routeDirector = new BasicRouteDirector();
+ this.proxyAuthStrategy = proxyAuthStrategy;
+ this.authenticator = new HttpAuthenticator();
+ this.authCacheKeeper = authCachingDisabled ? null : new AuthCacheKeeper(schemePortResolver);
+ this.routeDirector = new BasicRouteDirector();
}
@Override
@@ -207,6 +213,11 @@ public final class ConnectExec implements ExecChainHandler {
final HttpHost target = route.getTargetHost();
final HttpHost proxy = route.getProxyHost();
final AuthExchange proxyAuthExchange = context.getAuthExchange(proxy);
+
+ if (authCacheKeeper != null) {
+ authCacheKeeper.loadPreemptively(proxy, proxyAuthExchange, context);
+ }
+
ClassicHttpResponse response = null;
final String authority = target.toHostString();
@@ -239,10 +250,24 @@ public final class ConnectExec implements ExecChainHandler {
}
if (config.isAuthenticationEnabled()) {
- if (this.authenticator.isChallenged(proxy, ChallengeType.PROXY, response,
- proxyAuthExchange, context)) {
- if (this.authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
- this.proxyAuthStrategy, proxyAuthExchange, context)) {
+ final boolean proxyAuthRequested = authenticator.isChallenged(proxy, ChallengeType.PROXY, response, proxyAuthExchange, context);
+
+ if (authCacheKeeper != null) {
+ if (proxyAuthRequested) {
+ authCacheKeeper.updateOnChallenge(proxy, proxyAuthExchange, context);
+ } else {
+ authCacheKeeper.updateOnNoChallenge(proxy, proxyAuthExchange, context);
+ }
+ }
+
+ if (proxyAuthRequested) {
+ final boolean updated = authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
+ proxyAuthStrategy, proxyAuthExchange, context);
+
+ if (authCacheKeeper != null) {
+ authCacheKeeper.updateOnResponse(proxy, proxyAuthExchange, context);
+ }
+ if (updated) {
// Retry request
response = null;
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java
index 13e7b3bc2..d40d077c3 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java
@@ -77,7 +77,6 @@ import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
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.RequestClientConnControl;
import org.apache.hc.client5.http.protocol.RequestDefaultHeaders;
import org.apache.hc.client5.http.protocol.RequestExpectContinue;
@@ -791,7 +790,9 @@ public class HttpClientBuilder {
new ConnectExec(
reuseStrategyCopy,
new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
- proxyAuthStrategyCopy),
+ proxyAuthStrategyCopy,
+ schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
+ authCachingDisabled),
ChainElement.CONNECT.name());
final HttpProcessorBuilder b = HttpProcessorBuilder.create();
@@ -819,9 +820,6 @@ public class HttpClientBuilder {
if (!cookieManagementDisabled) {
b.add(new RequestAddCookies());
}
- if (!authCachingDisabled) {
- b.add(new RequestAuthCache());
- }
if (!cookieManagementDisabled) {
b.add(new ResponseProcessCookies());
}
@@ -841,7 +839,12 @@ public class HttpClientBuilder {
}
final HttpProcessor httpProcessor = b.build();
execChainDefinition.addFirst(
- new ProtocolExec(httpProcessor, targetAuthStrategyCopy, proxyAuthStrategyCopy),
+ new ProtocolExec(
+ httpProcessor,
+ targetAuthStrategyCopy,
+ proxyAuthStrategyCopy,
+ schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
+ authCachingDisabled),
ChainElement.PROTOCOL.name());
// Add request retry executor, if not disabled
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProtocolExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProtocolExec.java
index 14fcbfb5c..556f7a6ed 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProtocolExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProtocolExec.java
@@ -32,6 +32,7 @@ import java.util.Iterator;
import org.apache.hc.client5.http.AuthenticationStrategy;
import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.auth.AuthExchange;
import org.apache.hc.client5.http.auth.ChallengeType;
import org.apache.hc.client5.http.auth.CredentialsProvider;
@@ -41,6 +42,7 @@ import org.apache.hc.client5.http.classic.ExecChainHandler;
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.auth.AuthCacheKeeper;
import org.apache.hc.client5.http.impl.auth.HttpAuthenticator;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.annotation.Contract;
@@ -86,15 +88,19 @@ public final class ProtocolExec implements ExecChainHandler {
private final AuthenticationStrategy targetAuthStrategy;
private final AuthenticationStrategy proxyAuthStrategy;
private final HttpAuthenticator authenticator;
+ private final AuthCacheKeeper authCacheKeeper;
public ProtocolExec(
final HttpProcessor httpProcessor,
final AuthenticationStrategy targetAuthStrategy,
- final AuthenticationStrategy proxyAuthStrategy) {
+ final AuthenticationStrategy proxyAuthStrategy,
+ final SchemePortResolver schemePortResolver,
+ final boolean authCachingDisabled) {
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();
+ this.authCacheKeeper = authCachingDisabled ? null : new AuthCacheKeeper(schemePortResolver);
}
@Override
@@ -149,6 +155,13 @@ public final class ProtocolExec implements ExecChainHandler {
final AuthExchange targetAuthExchange = context.getAuthExchange(target);
final AuthExchange proxyAuthExchange = proxy != null ? context.getAuthExchange(proxy) : new AuthExchange();
+ if (authCacheKeeper != null) {
+ authCacheKeeper.loadPreemptively(target, targetAuthExchange, context);
+ if (proxy != null) {
+ authCacheKeeper.loadPreemptively(proxy, proxyAuthExchange, context);
+ }
+ }
+
RequestEntityProxy.enhance(request);
for (;;) {
@@ -188,7 +201,13 @@ public final class ProtocolExec implements ExecChainHandler {
}
return response;
}
- if (needAuthentication(targetAuthExchange, proxyAuthExchange, route, request, response, context)) {
+ if (needAuthentication(
+ targetAuthExchange,
+ proxyAuthExchange,
+ proxy != null ? proxy : target,
+ target,
+ response,
+ context)) {
// Make sure the response body is fully consumed, if present
final HttpEntity responseEntity = response.getEntity();
if (execRuntime.isConnectionReusable()) {
@@ -238,31 +257,53 @@ public final class ProtocolExec implements ExecChainHandler {
private boolean needAuthentication(
final AuthExchange targetAuthExchange,
final AuthExchange proxyAuthExchange,
- final HttpRoute route,
- final ClassicHttpRequest request,
+ final HttpHost proxy,
+ final HttpHost target,
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();
+ if (authCacheKeeper != null) {
+ if (targetAuthRequested) {
+ authCacheKeeper.updateOnChallenge(target, targetAuthExchange, context);
+ } else {
+ authCacheKeeper.updateOnNoChallenge(target, targetAuthExchange, context);
+ }
}
+
final boolean proxyAuthRequested = authenticator.isChallenged(
proxy, ChallengeType.PROXY, response, proxyAuthExchange, context);
+ if (authCacheKeeper != null) {
+ if (proxyAuthRequested) {
+ authCacheKeeper.updateOnChallenge(proxy, proxyAuthExchange, context);
+ } else {
+ authCacheKeeper.updateOnNoChallenge(proxy, proxyAuthExchange, context);
+ }
+ }
+
if (targetAuthRequested) {
- return authenticator.updateAuthState(target, ChallengeType.TARGET, response,
+ final boolean updated = authenticator.updateAuthState(target, ChallengeType.TARGET, response,
targetAuthStrategy, targetAuthExchange, context);
+
+ if (authCacheKeeper != null) {
+ authCacheKeeper.updateOnResponse(target, targetAuthExchange, context);
+ }
+
+ return updated;
}
if (proxyAuthRequested) {
- return authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
+ final boolean updated = authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
proxyAuthStrategy, proxyAuthExchange, context);
+
+ if (authCacheKeeper != null) {
+ authCacheKeeper.updateOnResponse(proxy, proxyAuthExchange, context);
+ }
+
+ return updated;
}
}
return false;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestAuthCache.java b/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestAuthCache.java
index 597bb21a0..608357daa 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestAuthCache.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/protocol/RequestAuthCache.java
@@ -34,6 +34,7 @@ import org.apache.hc.client5.http.auth.AuthCache;
import org.apache.hc.client5.http.auth.AuthExchange;
import org.apache.hc.client5.http.auth.AuthScheme;
import org.apache.hc.client5.http.auth.CredentialsProvider;
+import org.apache.hc.client5.http.impl.RequestSupport;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.EntityDetails;
@@ -42,7 +43,6 @@ 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.protocol.HttpContext;
-import org.apache.hc.core5.net.URIAuthority;
import org.apache.hc.core5.util.Args;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -53,7 +53,10 @@ import org.slf4j.LoggerFactory;
* {@link AuthCache} associated with the given target or proxy host.
*
* @since 4.1
+ *
+ * @deprecated Do not use.
*/
+@Deprecated
@Contract(threading = ThreadingBehavior.STATELESS)
public class RequestAuthCache implements HttpRequestInterceptor {
@@ -96,19 +99,11 @@ public class RequestAuthCache implements HttpRequestInterceptor {
return;
}
- final URIAuthority authority = request.getAuthority();
- final HttpHost target;
- if (authority != null) {
- target = new HttpHost(
- request.getScheme(),
- authority.getHostName(),
- authority.getPort() >= 0 ? authority.getPort() : route.getTargetHost().getPort());
- } else {
- target = route.getTargetHost();
- }
+ final HttpHost target = new HttpHost(request.getScheme(), request.getAuthority());
final AuthExchange targetAuthExchange = clientContext.getAuthExchange(target);
if (targetAuthExchange.getState() == AuthExchange.State.UNCHALLENGED) {
- final AuthScheme authScheme = authCache.get(target);
+ final String pathPrefix = RequestSupport.extractPathPrefix(request);
+ final AuthScheme authScheme = authCache.get(target, pathPrefix);
if (authScheme != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} Re-using cached '{}' auth scheme for {}", exchangeId, authScheme.getName(), target);
@@ -121,7 +116,7 @@ public class RequestAuthCache implements HttpRequestInterceptor {
if (proxy != null) {
final AuthExchange proxyAuthExchange = clientContext.getAuthExchange(proxy);
if (proxyAuthExchange.getState() == AuthExchange.State.UNCHALLENGED) {
- final AuthScheme authScheme = authCache.get(proxy);
+ final AuthScheme authScheme = authCache.get(proxy, null);
if (authScheme != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} Re-using cached '{}' auth scheme for {}", exchangeId, authScheme.getName(), proxy);
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestHttpAuthenticator.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestHttpAuthenticator.java
index fd2489446..10e6946e8 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestHttpAuthenticator.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestHttpAuthenticator.java
@@ -29,7 +29,6 @@ package org.apache.hc.client5.http.impl.auth;
import java.util.LinkedList;
import java.util.Queue;
-import org.apache.hc.client5.http.auth.AuthCache;
import org.apache.hc.client5.http.auth.AuthExchange;
import org.apache.hc.client5.http.auth.AuthScheme;
import org.apache.hc.client5.http.auth.AuthSchemeFactory;
@@ -80,7 +79,6 @@ public class TestHttpAuthenticator {
private HttpHost defaultHost;
private CredentialsProvider credentialsProvider;
private Lookup