This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-connection-timeout-agent.git


The following commit(s) were added to refs/heads/master by this push:
     new d6afeaa  SLING-12948 - Add JDK HttpClient support (#14)
d6afeaa is described below

commit d6afeaa281a55ebbcfadf1eb0368635a90df1410
Author: Michał Szczęśniak <[email protected]>
AuthorDate: Wed Sep 24 14:33:46 2025 +0200

    SLING-12948 - Add JDK HttpClient support (#14)
    
    Co-authored-by: Robert Munteanu <[email protected]>
---
 pom.xml                                            |  2 +-
 src/main/java/org/apache/sling/cta/impl/Agent.java |  4 +-
 .../JdkHttpClientBuilderTimeoutTransformer.java    | 55 ++++++++++++++++++++++
 .../JdkHttpRequestBuilderTimeoutTransformer.java   | 55 ++++++++++++++++++++++
 .../java/org/apache/sling/cta/impl/AgentIT.java    |  6 ++-
 .../org/apache/sling/cta/impl/AgentLauncher.java   |  2 +-
 .../org/apache/sling/cta/impl/ErrorDescriptor.java | 11 ++++-
 .../apache/sling/cta/impl/HttpClientLauncher.java  | 35 +++++++++++++-
 .../java/org/apache/sling/cta/impl/OsgiIT.java     |  2 +-
 9 files changed, 163 insertions(+), 9 deletions(-)

diff --git a/pom.xml b/pom.xml
index d6109ae..d1e882f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -188,6 +188,6 @@
     </dependencies>
     <properties>
         <pax-exam.version>4.14.0</pax-exam.version>
-        <sling.java.version>8</sling.java.version>
+        <sling.java.version>11</sling.java.version>
     </properties>
 </project>
diff --git a/src/main/java/org/apache/sling/cta/impl/Agent.java 
b/src/main/java/org/apache/sling/cta/impl/Agent.java
index 45ef00a..296448a 100644
--- a/src/main/java/org/apache/sling/cta/impl/Agent.java
+++ b/src/main/java/org/apache/sling/cta/impl/Agent.java
@@ -52,7 +52,9 @@ public class Agent {
             new JavaNetTimeoutTransformer(connectTimeout, readTimeout, 
agentInfoMBean),
             new HttpClient3TimeoutTransformer(connectTimeout, readTimeout, 
agentInfoMBean),
             new HttpClient4TimeoutTransformer(connectTimeout, readTimeout, 
agentInfoMBean),
-            new OkHttpTimeoutTransformer(connectTimeout, readTimeout, 
agentInfoMBean)
+            new OkHttpTimeoutTransformer(connectTimeout, readTimeout, 
agentInfoMBean),
+            new JdkHttpClientBuilderTimeoutTransformer(connectTimeout, 
agentInfoMBean),
+            new JdkHttpRequestBuilderTimeoutTransformer(readTimeout, 
agentInfoMBean)
         };
 
         try {
diff --git 
a/src/main/java/org/apache/sling/cta/impl/JdkHttpClientBuilderTimeoutTransformer.java
 
b/src/main/java/org/apache/sling/cta/impl/JdkHttpClientBuilderTimeoutTransformer.java
new file mode 100644
index 0000000..d23052d
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/cta/impl/JdkHttpClientBuilderTimeoutTransformer.java
@@ -0,0 +1,55 @@
+/*
+ * 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.apache.sling.cta.impl;
+
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.bytecode.Descriptor;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Sets timeouts for HTTP calls done using 
<code>java.net.http</code>/<code>java.net.http.HttpClient</code>.
+ */
+class JdkHttpClientBuilderTimeoutTransformer extends 
MBeanAwareTimeoutTransformer {
+
+    static final Set<String> CLASSES_TO_TRANSFORM = new HashSet<>();
+
+    static {
+        
CLASSES_TO_TRANSFORM.add(Descriptor.toJvmName("jdk.internal.net.http.HttpClientBuilderImpl"));
+    }
+
+    private final long connectTimeoutMillis;
+
+    public JdkHttpClientBuilderTimeoutTransformer(long connectTimeout, 
AgentInfo agentInfo) {
+        
+        super(agentInfo, CLASSES_TO_TRANSFORM);
+        
+        this.connectTimeoutMillis = connectTimeout;
+    }
+
+    protected byte[] doTransformClass(CtClass cc) throws Exception {
+
+        CtMethod buildMethod = cc.getDeclaredMethod("build");
+        buildMethod.insertBefore("if ( this.connectTimeout == null ) { 
connectTimeout(java.time.Duration.ofMillis(" + connectTimeoutMillis + "L)); }");
+        byte[] classfileBuffer = buildMethod.getDeclaringClass().toBytecode();
+        buildMethod.getDeclaringClass().detach();
+        return classfileBuffer;
+    }
+
+}
diff --git 
a/src/main/java/org/apache/sling/cta/impl/JdkHttpRequestBuilderTimeoutTransformer.java
 
b/src/main/java/org/apache/sling/cta/impl/JdkHttpRequestBuilderTimeoutTransformer.java
new file mode 100644
index 0000000..1aad78a
--- /dev/null
+++ 
b/src/main/java/org/apache/sling/cta/impl/JdkHttpRequestBuilderTimeoutTransformer.java
@@ -0,0 +1,55 @@
+/*
+ * 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.apache.sling.cta.impl;
+
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.bytecode.Descriptor;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Sets timeouts for HTTP calls done using 
<code>java.net.http</code>/<code>java.net.http.HttpClient</code>.
+ */
+class JdkHttpRequestBuilderTimeoutTransformer extends 
MBeanAwareTimeoutTransformer {
+
+    static final Set<String> CLASSES_TO_TRANSFORM = new HashSet<>();
+
+    static {
+        
CLASSES_TO_TRANSFORM.add(Descriptor.toJvmName("jdk.internal.net.http.HttpRequestBuilderImpl"));
+    }
+
+    private final long readTimeoutMillis;
+
+    public JdkHttpRequestBuilderTimeoutTransformer(long readTimeout, AgentInfo 
agentInfo) {
+        
+        super(agentInfo, CLASSES_TO_TRANSFORM);
+
+        this.readTimeoutMillis = readTimeout;
+    }
+
+    protected byte[] doTransformClass(CtClass cc) throws Exception {
+
+        CtMethod buildMethod = cc.getDeclaredMethod("build");
+        buildMethod.insertBefore("if ( this.duration == null ) { 
timeout(java.time.Duration.ofMillis(" + readTimeoutMillis + "L)); }");
+        byte[] classfileBuffer = buildMethod.getDeclaringClass().toBytecode();
+        buildMethod.getDeclaringClass().detach();
+        return classfileBuffer;
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/cta/impl/AgentIT.java 
b/src/test/java/org/apache/sling/cta/impl/AgentIT.java
index cac5b8b..bfbe690 100644
--- a/src/test/java/org/apache/sling/cta/impl/AgentIT.java
+++ b/src/test/java/org/apache/sling/cta/impl/AgentIT.java
@@ -21,6 +21,7 @@ import static java.util.Objects.requireNonNull;
 import static org.apache.sling.cta.impl.HttpClientLauncher.ClientType.HC3;
 import static org.apache.sling.cta.impl.HttpClientLauncher.ClientType.HC4;
 import static org.apache.sling.cta.impl.HttpClientLauncher.ClientType.JavaNet;
+import static 
org.apache.sling.cta.impl.HttpClientLauncher.ClientType.JdkHttpClient;
 import static org.apache.sling.cta.impl.HttpClientLauncher.ClientType.OkHttp;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTimeout;
@@ -29,6 +30,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.io.IOException;
 import java.net.SocketTimeoutException;
 import java.net.URL;
+import java.net.http.HttpConnectTimeoutException;
+import java.net.http.HttpTimeoutException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -80,6 +83,7 @@ public class AgentIT {
         errorDescriptors.put(HC4, new 
ErrorDescriptor(org.apache.http.conn.ConnectTimeoutException.class, 
                 "Connect to 127\\.0\\.0\\.1:[0-9]+ \\[.*\\] failed: 
[C|c]onnect timed out", "Read timed out"));
         errorDescriptors.put(OkHttp, new 
ErrorDescriptor(SocketTimeoutException.class, "[C|c]onnect timed out", 
"(timeout|Read timed out)"));
+        errorDescriptors.put(JdkHttpClient, new 
ErrorDescriptor(HttpConnectTimeoutException.class, "HTTP connect timed out", 
HttpTimeoutException.class, "request timed out"));
     }
 
     /**
@@ -151,7 +155,7 @@ public class AgentIT {
         RecordedThrowable error = 
assertTimeout(ofSeconds(EXECUTION_TIMEOUT_SECONDS),
            () -> runTest("http://127.0.0.1:"; + server.getLocalPort(), 
clientType, timeouts, false));
 
-        assertEquals(SocketTimeoutException.class.getName(), error.className);
+        assertEquals(ed.readTimeoutClass.getName(), error.className);
         assertTrue(error.message.matches(ed.readTimeoutRegex),
             "Actual message " + error.message + " did not match regex " + 
ed.readTimeoutRegex);
     }
diff --git a/src/test/java/org/apache/sling/cta/impl/AgentLauncher.java 
b/src/test/java/org/apache/sling/cta/impl/AgentLauncher.java
index 1b4d16d..8eaf0d5 100644
--- a/src/test/java/org/apache/sling/cta/impl/AgentLauncher.java
+++ b/src/test/java/org/apache/sling/cta/impl/AgentLauncher.java
@@ -106,4 +106,4 @@ class AgentLauncher {
         
         return String.join(File.pathSeparator, elements);
     }
-}
\ No newline at end of file
+}
diff --git a/src/test/java/org/apache/sling/cta/impl/ErrorDescriptor.java 
b/src/test/java/org/apache/sling/cta/impl/ErrorDescriptor.java
index 0a1865e..9abe58f 100644
--- a/src/test/java/org/apache/sling/cta/impl/ErrorDescriptor.java
+++ b/src/test/java/org/apache/sling/cta/impl/ErrorDescriptor.java
@@ -17,6 +17,7 @@
 package org.apache.sling.cta.impl;
 
 import java.io.IOException;
+import java.net.SocketTimeoutException;
 
 import org.apache.sling.cta.impl.HttpClientLauncher.ClientType;
 
@@ -26,12 +27,18 @@ import 
org.apache.sling.cta.impl.HttpClientLauncher.ClientType;
 class ErrorDescriptor {
     Class<? extends IOException> connectTimeoutClass;
     String connectTimeoutMessageRegex;
+    Class<? extends IOException> readTimeoutClass;
     String readTimeoutRegex;
 
+    public ErrorDescriptor(Class<? extends IOException> connectTimeoutClass, 
String connectTimeoutMessageRegex, String readTimeoutRegex) {
+        this(connectTimeoutClass, connectTimeoutMessageRegex, 
SocketTimeoutException.class, readTimeoutRegex);
+    }
+
     public ErrorDescriptor(Class<? extends IOException> connectTimeoutClass, 
String connectTimeoutMessageRegex,
-            String readTimeoutRegex) {
+                           Class<? extends IOException> readTimeoutClass, 
String readTimeoutRegex) {
         this.connectTimeoutClass = connectTimeoutClass;
         this.connectTimeoutMessageRegex = connectTimeoutMessageRegex;
+        this.readTimeoutClass = readTimeoutClass;
         this.readTimeoutRegex = readTimeoutRegex;
     }
-}
\ No newline at end of file
+}
diff --git a/src/test/java/org/apache/sling/cta/impl/HttpClientLauncher.java 
b/src/test/java/org/apache/sling/cta/impl/HttpClientLauncher.java
index b7e731d..b072d3f 100644
--- a/src/test/java/org/apache/sling/cta/impl/HttpClientLauncher.java
+++ b/src/test/java/org/apache/sling/cta/impl/HttpClientLauncher.java
@@ -21,7 +21,11 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
 import java.time.Duration;
 import java.util.EnumSet;
 import java.util.stream.Collectors;
@@ -52,10 +56,11 @@ import okhttp3.Response;
 public class HttpClientLauncher {
     
     public enum ClientType {
-        JavaNet(HttpClientLauncher::runUsingJavaNet), 
+        JavaNet(HttpClientLauncher::runUsingJavaNet),
         HC3(HttpClientLauncher::runUsingHttpClient3),
         HC4(HttpClientLauncher::runUsingHttpClient4),
-        OkHttp(HttpClientLauncher::runUsingOkHttp);
+        OkHttp(HttpClientLauncher::runUsingOkHttp),
+        JdkHttpClient(HttpClientLauncher::runUsingJdkHttpClient);
         
         private final HttpConsumer consumer;
 
@@ -136,6 +141,32 @@ public class HttpClientLauncher {
         }
     }
 
+    private static void runUsingJdkHttpClient(String targetUrl, int 
connectTimeoutMillis, int readTimeoutMillis) throws IOException, 
URISyntaxException, InterruptedException {
+
+        java.net.http.HttpClient.Builder clientBuilder = 
java.net.http.HttpClient.newBuilder();
+        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
+
+        if (connectTimeoutMillis > 0) {
+            
clientBuilder.connectTimeout(Duration.ofMillis(connectTimeoutMillis));
+        }
+        if (readTimeoutMillis > 0) {
+            requestBuilder.timeout(Duration.ofMillis(readTimeoutMillis));
+        }
+
+        requestBuilder.uri(new URI(targetUrl));
+
+        java.net.http.HttpClient client = clientBuilder.build();
+        HttpRequest request = requestBuilder.build();
+
+        log("HttpClient timeouts: connection: %d, request: %d",
+                client.connectTimeout().orElse(Duration.ZERO).toMillis(),
+                request.timeout().orElse(Duration.ZERO).toMillis());
+
+        HttpResponse<String> response = client.send(request, 
HttpResponse.BodyHandlers.ofString());
+
+        log("HttpClient response status: %s", response.statusCode());
+    }
+
 
     private static void runUsingHttpClient3(String targetUrl, int 
connectTimeoutMillis, int readTimeoutMillis) throws IOException {
         HttpClient client = new HttpClient();
diff --git a/src/test/java/org/apache/sling/cta/impl/OsgiIT.java 
b/src/test/java/org/apache/sling/cta/impl/OsgiIT.java
index 17b2752..37cef91 100644
--- a/src/test/java/org/apache/sling/cta/impl/OsgiIT.java
+++ b/src/test/java/org/apache/sling/cta/impl/OsgiIT.java
@@ -78,7 +78,7 @@ public class OsgiIT {
             mavenBundle("org.apache.httpcomponents", "httpcore-osgi", 
"4.4.12"),
             mavenBundle("org.apache.httpcomponents", "httpclient-osgi", 
"4.5.10"),
             mavenBundle("org.apache.felix", 
"org.apache.felix.http.servlet-api", "3.0.0"),
-            
mavenBundle("org.apache.felix","org.apache.felix.http.jetty","5.1.26"),
+            
mavenBundle("org.apache.felix","org.apache.felix.http.jetty","4.2.32"),
             vmOption("-javaagent:" + agentCandidates.get(0) +"=10000,1,v") // 
large connect timeout, very small read timeout
         );
     }

Reply via email to