From c4489a55a90b039d04db4c5ac5949e7fab4ffa2d Mon Sep 17 00:00:00 2001 From: Rene Schwietzke Date: Fri, 5 Sep 2025 22:52:50 +0200 Subject: [PATCH 1/2] Properly synchronize access to httpClientBuilder_ --- src/main/java/org/htmlunit/HttpWebConnection.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/htmlunit/HttpWebConnection.java b/src/main/java/org/htmlunit/HttpWebConnection.java index f2b2420d72..dd93838ca2 100644 --- a/src/main/java/org/htmlunit/HttpWebConnection.java +++ b/src/main/java/org/htmlunit/HttpWebConnection.java @@ -137,7 +137,7 @@ public class HttpWebConnection implements WebConnection { private static final String HACKED_COOKIE_POLICY = "mine"; // have one per thread because this is (re)configured for every call (see configureHttpProcessorBuilder) - // do not use a ThreadLocal because this in only accessed form this class + // do not use a ThreadLocal because this in only accessed form this class, but we still need it synchronized private final Map httpClientBuilder_ = new WeakHashMap<>(); private final WebClient webClient_; @@ -211,7 +211,9 @@ public WebResponse getResponse(final WebRequest webRequest) throws IOException { // Calling code may catch the StackOverflowError, but due to the leak, the httpClient_ may // come out of connections and throw a ConnectionPoolTimeoutException. // => best solution, discard the HttpClient instance. - httpClientBuilder_.remove(Thread.currentThread()); + synchronized (httpClientBuilder_) { + httpClientBuilder_.remove(Thread.currentThread()); + } throw e; } } @@ -530,7 +532,7 @@ private static HttpRequestBase buildHttpMethod(final HttpMethod submitMethod, fi * * @return the initialized HTTP client */ - protected HttpClientBuilder getHttpClientBuilder() { + protected synchronized HttpClientBuilder getHttpClientBuilder() { final Thread currentThread = Thread.currentThread(); HttpClientBuilder builder = httpClientBuilder_.get(currentThread); if (builder == null) { @@ -1288,7 +1290,9 @@ public synchronized String toString() { */ @Override public void close() { - httpClientBuilder_.clear(); + synchronized (httpClientBuilder_) { + httpClientBuilder_.clear(); + } sharedAuthCache_.clear(); httpClientContextByThread_.clear(); From 8da827b0ff3b2c54ebfdf4bbc6f3f97680f499e4 Mon Sep 17 00:00:00 2001 From: Rene Schwietzke Date: Sat, 6 Sep 2025 12:24:42 +0200 Subject: [PATCH 2/2] Make the synchronization nicer --- .../java/org/htmlunit/HttpWebConnection.java | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/htmlunit/HttpWebConnection.java b/src/main/java/org/htmlunit/HttpWebConnection.java index dd93838ca2..24474b14f0 100644 --- a/src/main/java/org/htmlunit/HttpWebConnection.java +++ b/src/main/java/org/htmlunit/HttpWebConnection.java @@ -532,25 +532,28 @@ private static HttpRequestBase buildHttpMethod(final HttpMethod submitMethod, fi * * @return the initialized HTTP client */ - protected synchronized HttpClientBuilder getHttpClientBuilder() { - final Thread currentThread = Thread.currentThread(); - HttpClientBuilder builder = httpClientBuilder_.get(currentThread); - if (builder == null) { - builder = createHttpClientBuilder(); - - // this factory is required later - // to be sure this is done, we do it outside the createHttpClient() call - final RegistryBuilder registeryBuilder - = RegistryBuilder.create() - .register(HACKED_COOKIE_POLICY, htmlUnitCookieSpecProvider_); - builder.setDefaultCookieSpecRegistry(registeryBuilder.build()); - - builder.setDefaultCookieStore(new HtmlUnitCookieStore(webClient_.getCookieManager())); - builder.setUserAgent(webClient_.getBrowserVersion().getUserAgent()); - httpClientBuilder_.put(currentThread, builder); + protected HttpClientBuilder getHttpClientBuilder() { + synchronized (httpClientBuilder_) + { + final Thread currentThread = Thread.currentThread(); + HttpClientBuilder builder = httpClientBuilder_.get(currentThread); + if (builder == null) { + builder = createHttpClientBuilder(); + + // this factory is required later + // to be sure this is done, we do it outside the createHttpClient() call + final RegistryBuilder registeryBuilder + = RegistryBuilder.create() + .register(HACKED_COOKIE_POLICY, htmlUnitCookieSpecProvider_); + builder.setDefaultCookieSpecRegistry(registeryBuilder.build()); + + builder.setDefaultCookieStore(new HtmlUnitCookieStore(webClient_.getCookieManager())); + builder.setUserAgent(webClient_.getBrowserVersion().getUserAgent()); + httpClientBuilder_.put(currentThread, builder); + } + + return builder; } - - return builder; } /**