This is an automated email from the ASF dual-hosted git repository.
cstamas pushed a commit to branch maven-resolver-1.9.x
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git
The following commit(s) were added to refs/heads/maven-resolver-1.9.x by this
push:
new 96da9ccc6 GH-1768 Drastically simplify auth caching (#1790)
96da9ccc6 is described below
commit 96da9ccc65687a7eecbe5424abea3e24b406fe5f
Author: Tamas Cservenak <[email protected]>
AuthorDate: Thu Feb 12 12:17:41 2026 +0100
GH-1768 Drastically simplify auth caching (#1790)
As there is a reproducible issue for proxy auth cache. Seems auth cache
fall apart in the moment non-single thread flow happens. Instead to chase and
wrap around our head, I just went to drastically simplify the whole stuff,
without dropping any existing feature, and making sure that reproducer does not
reproduce anymore.
Fixes #1768
---
.../aether/transport/http/AuthSchemePool.java | 60 --------------
.../eclipse/aether/transport/http/GlobalState.java | 8 --
.../aether/transport/http/HttpTransporter.java | 23 +++++-
.../eclipse/aether/transport/http/LocalState.java | 28 -------
.../aether/transport/http/SharingAuthCache.java | 95 ----------------------
.../aether/transport/http/SharingHttpContext.java | 13 +--
6 files changed, 23 insertions(+), 204 deletions(-)
diff --git
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/AuthSchemePool.java
b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/AuthSchemePool.java
deleted file mode 100644
index 2603eae56..000000000
---
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/AuthSchemePool.java
+++ /dev/null
@@ -1,60 +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.
- */
-package org.eclipse.aether.transport.http;
-
-import java.util.LinkedList;
-
-import org.apache.http.auth.AuthScheme;
-import org.apache.http.client.config.AuthSchemes;
-import org.apache.http.impl.auth.BasicScheme;
-
-/**
- * Pool of (equivalent) auth schemes for a single host.
- */
-final class AuthSchemePool {
-
- private final LinkedList<AuthScheme> authSchemes;
-
- private String schemeName;
-
- AuthSchemePool() {
- authSchemes = new LinkedList<>();
- }
-
- public synchronized AuthScheme get() {
- AuthScheme authScheme = null;
- if (!authSchemes.isEmpty()) {
- authScheme = authSchemes.removeLast();
- } else if (AuthSchemes.BASIC.equalsIgnoreCase(schemeName)) {
- authScheme = new BasicScheme();
- }
- return authScheme;
- }
-
- public synchronized void put(AuthScheme authScheme) {
- if (authScheme == null) {
- return;
- }
- if (!authScheme.getSchemeName().equals(schemeName)) {
- schemeName = authScheme.getSchemeName();
- authSchemes.clear();
- }
- authSchemes.add(authScheme);
- }
-}
diff --git
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/GlobalState.java
b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/GlobalState.java
index 3ac06619a..ae6cc4607 100644
---
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/GlobalState.java
+++
b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/GlobalState.java
@@ -29,7 +29,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
-import org.apache.http.HttpHost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
@@ -95,8 +94,6 @@ final class GlobalState implements Closeable {
private final ConcurrentMap<CompoundKey, Object> userTokens;
- private final ConcurrentMap<HttpHost, AuthSchemePool> authSchemePools;
-
private final ConcurrentMap<CompoundKey, Boolean> expectContinues;
public static GlobalState get(RepositorySystemSession session) {
@@ -126,7 +123,6 @@ final class GlobalState implements Closeable {
private GlobalState() {
connectionManagers = new ConcurrentHashMap<>();
userTokens = new ConcurrentHashMap<>();
- authSchemePools = new ConcurrentHashMap<>();
expectContinues = new ConcurrentHashMap<>();
}
@@ -216,10 +212,6 @@ final class GlobalState implements Closeable {
}
}
- public ConcurrentMap<HttpHost, AuthSchemePool> getAuthSchemePools() {
- return authSchemePools;
- }
-
public Boolean getExpectContinue(CompoundKey key) {
return expectContinues.get(key);
}
diff --git
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java
b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java
index 0d44af1a9..85d5068a1 100644
---
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java
+++
b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/HttpTransporter.java
@@ -49,6 +49,7 @@ import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
+import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.HttpResponseException;
@@ -76,6 +77,7 @@ import org.apache.http.impl.auth.DigestSchemeFactory;
import org.apache.http.impl.auth.KerberosSchemeFactory;
import org.apache.http.impl.auth.NTLMSchemeFactory;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
+import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
@@ -99,6 +101,7 @@ import
org.eclipse.aether.transport.http.RFC9457.HttpRFC9457Exception;
import org.eclipse.aether.transport.http.RFC9457.RFC9457Reporter;
import org.eclipse.aether.util.ConfigUtils;
import org.eclipse.aether.util.FileUtils;
+import org.eclipse.aether.util.StringDigestUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -155,6 +158,10 @@ final class HttpTransporter extends AbstractTransporter {
private final boolean supportWebDav;
+ private final AuthCache authCache;
+
+ private static final Object AUTH_CACHE_MUTEX = new Object();
+
@SuppressWarnings("checkstyle:methodlength")
HttpTransporter(
Map<String, ChecksumExtractor> checksumExtractors,
@@ -358,6 +365,20 @@ final class HttpTransporter extends AbstractTransporter {
builder.setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE);
}
+ if (session.getCache() != null) {
+ String authCacheKey = getClass().getSimpleName() + "-" +
repository.getId() + "-"
+ + StringDigestUtil.sha1(repository.toString());
+ synchronized (AUTH_CACHE_MUTEX) {
+ AuthCache cache = (AuthCache) session.getCache().get(session,
authCacheKey);
+ if (cache == null) {
+ cache = new BasicAuthCache();
+ session.getCache().put(session, authCacheKey, cache);
+ }
+ this.authCache = cache;
+ }
+ } else {
+ this.authCache = new BasicAuthCache();
+ }
this.client = builder.build();
}
@@ -504,10 +525,10 @@ final class HttpTransporter extends AbstractTransporter {
private void execute(HttpUriRequest request, EntityGetter getter) throws
Exception {
try {
SharingHttpContext context = new SharingHttpContext(state);
+ context.setAuthCache(authCache);
prepare(request, context);
try (CloseableHttpResponse response = client.execute(server,
request, context)) {
try {
- context.close();
handleStatus(response);
if (getter != null) {
getter.handle(response);
diff --git
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/LocalState.java
b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/LocalState.java
index c7352bcd0..28e9fcf43 100644
---
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/LocalState.java
+++
b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/LocalState.java
@@ -19,11 +19,7 @@
package org.eclipse.aether.transport.http;
import java.io.Closeable;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import org.apache.http.HttpHost;
-import org.apache.http.auth.AuthScheme;
import org.apache.http.conn.HttpClientConnectionManager;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
@@ -48,8 +44,6 @@ final class LocalState implements Closeable {
private volatile Boolean webDav;
- private final ConcurrentMap<HttpHost, AuthSchemePool> authSchemePools;
-
LocalState(RepositorySystemSession session, RemoteRepository repo,
ConnMgrConfig connMgrConfig) {
global = GlobalState.get(session);
userToken = this;
@@ -57,12 +51,10 @@ final class LocalState implements Closeable {
connMgr = GlobalState.newConnectionManager(connMgrConfig);
userTokenKey = null;
expectContinueKey = null;
- authSchemePools = new ConcurrentHashMap<>();
} else {
connMgr = global.getConnectionManager(connMgrConfig);
userTokenKey = new CompoundKey(repo.getId(), repo.getUrl(),
repo.getAuthentication(), repo.getProxy());
expectContinueKey = new CompoundKey(repo.getUrl(),
repo.getProxy());
- authSchemePools = global.getAuthSchemePools();
}
}
@@ -107,26 +99,6 @@ final class LocalState implements Closeable {
this.webDav = webDav;
}
- public AuthScheme getAuthScheme(HttpHost host) {
- AuthSchemePool pool = authSchemePools.get(host);
- if (pool != null) {
- return pool.get();
- }
- return null;
- }
-
- public void setAuthScheme(HttpHost host, AuthScheme authScheme) {
- AuthSchemePool pool = authSchemePools.get(host);
- if (pool == null) {
- AuthSchemePool p = new AuthSchemePool();
- pool = authSchemePools.putIfAbsent(host, p);
- if (pool == null) {
- pool = p;
- }
- }
- pool.put(authScheme);
- }
-
@Override
public void close() {
if (global == null) {
diff --git
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SharingAuthCache.java
b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SharingAuthCache.java
deleted file mode 100644
index d7ed9c591..000000000
---
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SharingAuthCache.java
+++ /dev/null
@@ -1,95 +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.
- */
-package org.eclipse.aether.transport.http;
-
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.http.HttpHost;
-import org.apache.http.auth.AuthScheme;
-import org.apache.http.client.AuthCache;
-
-/**
- * Auth scheme cache that upon clearing releases all cached schemes into a
pool for future reuse by other requests,
- * thereby reducing challenge-response roundtrips.
- */
-final class SharingAuthCache implements AuthCache {
-
- private final LocalState state;
-
- private final ConcurrentHashMap<HttpHost, AuthScheme> authSchemes;
-
- SharingAuthCache(LocalState state) {
- this.state = state;
- authSchemes = new ConcurrentHashMap<>();
- }
-
- private static HttpHost toKey(HttpHost host) {
- if (host.getPort() <= 0) {
- int port = host.getSchemeName().equalsIgnoreCase("https") ? 443 :
80;
- return new HttpHost(host.getHostName(), port,
host.getSchemeName());
- }
- return host;
- }
-
- @Override
- public AuthScheme get(HttpHost host) {
- host = toKey(host);
- AuthScheme authScheme = authSchemes.get(host);
- if (authScheme == null) {
- authScheme = state.getAuthScheme(host);
- if (authScheme != null) {
- authSchemes.put(host, authScheme);
- }
- }
- return authScheme;
- }
-
- @Override
- public void put(HttpHost host, AuthScheme authScheme) {
- host = toKey(host);
- if (authScheme != null) {
- authSchemes.put(host, authScheme);
- } else {
- authSchemes.remove(host);
- }
- }
-
- @Override
- public void remove(HttpHost host) {
- authSchemes.remove(toKey(host));
- }
-
- @Override
- public void clear() {
- share();
- authSchemes.clear();
- }
-
- private void share() {
- for (Map.Entry<HttpHost, AuthScheme> entry : authSchemes.entrySet()) {
- state.setAuthScheme(entry.getKey(), entry.getValue());
- }
- }
-
- @Override
- public String toString() {
- return authSchemes.toString();
- }
-}
diff --git
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SharingHttpContext.java
b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SharingHttpContext.java
index 3a2db193c..51021c6ea 100644
---
a/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SharingHttpContext.java
+++
b/maven-resolver-transport-http/src/main/java/org/eclipse/aether/transport/http/SharingHttpContext.java
@@ -18,8 +18,6 @@
*/
package org.eclipse.aether.transport.http;
-import java.io.Closeable;
-
import org.apache.http.client.protocol.HttpClientContext;
/**
@@ -28,16 +26,12 @@ import org.apache.http.client.protocol.HttpClientContext;
* @see <a
href="http://hc.apache.org/httpcomponents-client-ga/tutorial/html/advanced.html#stateful_conn">Stateful
HTTP
* connections</a>
*/
-final class SharingHttpContext extends HttpClientContext implements Closeable {
+final class SharingHttpContext extends HttpClientContext {
private final LocalState state;
- private final SharingAuthCache authCache;
-
SharingHttpContext(LocalState state) {
this.state = state;
- authCache = new SharingAuthCache(state);
- super.setAttribute(HttpClientContext.AUTH_CACHE, authCache);
}
@Override
@@ -65,9 +59,4 @@ final class SharingHttpContext extends HttpClientContext
implements Closeable {
}
return super.removeAttribute(id);
}
-
- @Override
- public void close() {
- authCache.clear();
- }
}