You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2019/06/07 09:33:46 UTC

[sling-whiteboard] branch feature/url-connection-agent-hc4 created (now e6d8e3b)

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

rombert pushed a change to branch feature/url-connection-agent-hc4
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git.


      at e6d8e3b  Implement default timeouts for httpclient 4.x

This branch includes the following new commits:

     new e6d8e3b  Implement default timeouts for httpclient 4.x

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[sling-whiteboard] 01/01: Implement default timeouts for httpclient 4.x

Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to branch feature/url-connection-agent-hc4
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git

commit e6d8e3bb71ae8b44ffecdbb8eb084a217901dd8b
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Fri Jun 7 11:33:04 2019 +0200

    Implement default timeouts for httpclient 4.x
---
 url-connection-agent/README.md                     |  5 +-
 url-connection-agent/pom.xml                       |  6 ++
 .../main/java/org/apache/sling/uca/impl/Agent.java |  3 +-
 .../uca/impl/HttpClient4TimeoutTransformer.java    | 70 ++++++++++++++++++++++
 .../java/org/apache/sling/uca/impl/AgentIT.java    | 37 +++++++++---
 .../test/java/org/apache/sling/uca/impl/Main.java  | 29 ++++++++-
 6 files changed, 139 insertions(+), 11 deletions(-)

diff --git a/url-connection-agent/README.md b/url-connection-agent/README.md
index d39b235..fcc5d06 100644
--- a/url-connection-agent/README.md
+++ b/url-connection-agent/README.md
@@ -15,7 +15,7 @@ Build the project with `mvn clean package` and then run a simple connection test
  - `<connect-timeout>` - connection timeout in milliseconds
  - `<read-timeout>`- read timeout in milliseconds
  - `<url>` - the URL to access
- - `<client-type>` - the client type, either `JavaNet` for java.net.URL-based connections or `HC3` for commons-httpclient 3.x
+ - `<client-type>` - the client type, either `JavaNet` for java.net.URL-based connections ,`HC3` for Apache Commons HttpClient 3.x or `HC4` for Apache Commons HttpClient 4.x
  
  
  For a test that always fails, set one of the timeouts to 1. Both executions listed below will typically fail:
@@ -35,4 +35,5 @@ java -javaagent:target/url-connection-agent-0.0.1-SNAPSHOT-jar-with-dependencies
 
 * openjdk version "1.8.0_212"
 * openjdk version "11.0.2" 2019-01-15
-* commons-httpclient 3.1
\ No newline at end of file
+* commons-httpclient 3.1
+* httpclient 4.5.4
\ No newline at end of file
diff --git a/url-connection-agent/pom.xml b/url-connection-agent/pom.xml
index b020df3..c148a64 100644
--- a/url-connection-agent/pom.xml
+++ b/url-connection-agent/pom.xml
@@ -127,5 +127,11 @@
             <version>1.7.25</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>4.5.4</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Agent.java b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Agent.java
index 4ed0067..c35565f 100644
--- a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Agent.java
+++ b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Agent.java
@@ -37,7 +37,8 @@ public class Agent {
 
         ClassFileTransformer[] transformers = new ClassFileTransformer[] {
             new JavaNetTimeoutTransformer(connectTimeout, readTimeout),
-            new HttpClient3TimeoutTransformer(connectTimeout, readTimeout)
+            new HttpClient3TimeoutTransformer(connectTimeout, readTimeout),
+            new HttpClient4TimeoutTransformer(connectTimeout, readTimeout)
         };
         
         for ( ClassFileTransformer transformer : transformers )
diff --git a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/HttpClient4TimeoutTransformer.java b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/HttpClient4TimeoutTransformer.java
new file mode 100644
index 0000000..ee62458
--- /dev/null
+++ b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/HttpClient4TimeoutTransformer.java
@@ -0,0 +1,70 @@
+/*
+ * 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.uca.impl;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtConstructor;
+import javassist.CtField;
+import javassist.bytecode.Descriptor;
+
+public class HttpClient4TimeoutTransformer implements ClassFileTransformer {
+
+    // org.apache.http.client.config.RequestConfig.Builder
+    
+    private static final String REQUEST_CONFIG_BUILDER_CLASS_NAME = Descriptor.toJvmName("org.apache.http.client.config.RequestConfig$Builder");
+    
+    private final long connectTimeoutMillis;
+    private final long readTimeoutMillis;
+    
+    public HttpClient4TimeoutTransformer(long connectTimeoutMillis, long readTimeoutMillis) {
+        this.connectTimeoutMillis = connectTimeoutMillis;
+        this.readTimeoutMillis = readTimeoutMillis;
+    }
+
+    @Override
+    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
+            ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+        try {
+            if ( REQUEST_CONFIG_BUILDER_CLASS_NAME.equals(className) ) {
+                System.out.println("[AGENT] Asked to transform " + className);
+                
+                ClassPool defaultPool = ClassPool.getDefault();
+                CtClass cc = defaultPool.get(Descriptor.toJavaName(className));
+                
+                // TODO - access the default constructor explicitly in case it changes
+                CtConstructor noArgCtor =  cc.getConstructors()[0];
+                CtField connectTimeout = cc.getDeclaredField("connectTimeout");
+                CtField socketTimeout = cc.getDeclaredField("socketTimeout");
+                noArgCtor.insertAfter("this." + connectTimeout.getName() + " = " + connectTimeoutMillis + ";");
+                noArgCtor.insertAfter("this." + socketTimeout.getName() + " = " + readTimeoutMillis + ";");
+                
+                classfileBuffer = cc.toBytecode();
+                cc.detach();
+            }
+            return classfileBuffer;
+        } catch (Exception e) {
+            e.printStackTrace(); // ensure _something_ is printed
+            throw new RuntimeException("[AGENT] Transformation failed", e);
+        }
+    }
+
+}
diff --git a/url-connection-agent/src/test/java/org/apache/sling/uca/impl/AgentIT.java b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/AgentIT.java
index 65bd7af..97f0c4c 100644
--- a/url-connection-agent/src/test/java/org/apache/sling/uca/impl/AgentIT.java
+++ b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/AgentIT.java
@@ -19,6 +19,7 @@ package org.apache.sling.uca.impl;
 import static java.time.Duration.ofSeconds;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertTimeout;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
@@ -75,11 +76,29 @@ public class AgentIT {
 
         RecordedThrowable error = assertTimeout(ofSeconds(5),  () -> runTest("http://repo1.maven.org:81", clientType));
         
-        Class<?> expectedClass = clientType == ClientType.HC3 ? ConnectTimeoutException.class : SocketTimeoutException.class;
-        String expectedMessage = clientType == ClientType.HC3 ? "The host did not accept the connection within timeout of 3000 ms" : "connect timed out";
+        Class<?> expectedClass;
+        String expectedMessageRegex;
+        
+        switch ( clientType ) {
+            case JavaNet:
+                expectedClass= SocketTimeoutException.class;
+                expectedMessageRegex = "connect timed out";
+                break;
+            case HC3:
+                expectedClass = ConnectTimeoutException.class;
+                expectedMessageRegex = "The host did not accept the connection within timeout of 3000 ms";
+                break;
+            case HC4:
+                expectedClass = org.apache.http.conn.ConnectTimeoutException.class;
+                expectedMessageRegex = "Connect to repo1.maven.org:81 \\[.*\\] failed: connect timed out";
+                break;
+            default:
+                throw new AssertionError("Unhandled clientType " + clientType);
+        }
         
         assertEquals(expectedClass.getName(), error.className);
-        assertEquals(expectedMessage, error.message);
+        assertTrue(error.message.matches(expectedMessageRegex), 
+            "Actual message " + error.message + " did not match regex " + expectedMessageRegex);
     }
 
     /**
@@ -169,7 +188,9 @@ public class AgentIT {
                     || p.getFileName().toString().equals("commons-codec.jar")
                     || p.getFileName().toString().equals("slf4j-simple.jar")
                     || p.getFileName().toString().equals("slf4j-api.jar")
-                    || p.getFileName().toString().equals("jcl-over-slf4j.jar"))
+                    || p.getFileName().toString().equals("jcl-over-slf4j.jar")
+                    || p.getFileName().toString().contentEquals("httpclient.jar")
+                    || p.getFileName().toString().contentEquals("httpcore.jar") )
             .forEach( p -> elements.add(p.toString()));
         
         return String.join(File.pathSeparator, elements);
@@ -177,10 +198,12 @@ public class AgentIT {
 
     private RecordedThrowable newRecordedThrowable(String string) {
      
-        string = string.replace("Exception in thread \"main\"", "");
-        String[] parts = string.split(":");
+        string = string.replace("Exception in thread \"main\" ", "");
+
+        String className = string.substring(0, string.indexOf(':'));
+        String message = string.substring(string.indexOf(':') + 2); // ignore ':' and leading ' '
 
-        return new RecordedThrowable(parts[0].trim(), parts[1].trim());
+        return new RecordedThrowable(className, message);
     }
     
     /**
diff --git a/url-connection-agent/src/test/java/org/apache/sling/uca/impl/Main.java b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/Main.java
index de07a23..aee0725 100644
--- a/url-connection-agent/src/test/java/org/apache/sling/uca/impl/Main.java
+++ b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/Main.java
@@ -33,12 +33,18 @@ import org.apache.commons.httpclient.HttpMethod;
 import org.apache.commons.httpclient.methods.GetMethod;
 import org.apache.commons.httpclient.params.HttpClientParams;
 import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
 
 public class Main {
     
     // TODO - write help messages with the values from this enum
     public enum ClientType {
-        JavaNet, HC3
+        JavaNet, HC3, HC4
     }
 
     public static void main(String[] args) throws MalformedURLException, IOException {
@@ -55,6 +61,9 @@ public class Main {
             case "HC3":
                 runUsingHttpClient3(args[0]);
                 break;
+            case "HC4":
+                runUsingHttpClient4(args[0]);
+                break;
             default:
                 throw new IllegalArgumentException("Usage: java -cp ... " + Main.class.getName() + " <URL> JavaNet|HC3|HC4");
         }
@@ -104,4 +113,22 @@ public class Main {
             }
         }
     }
+    
+    private static void runUsingHttpClient4(String targetUrl) throws IOException {
+        // disable retries, to make sure that we get equivalent behaviour with other implementations
+        try ( CloseableHttpClient client = HttpClients.custom().disableAutomaticRetries().build() ) {
+            HttpGet get = new HttpGet(targetUrl);
+            try ( CloseableHttpResponse response = client.execute(get)) {
+                System.out.println("[WEB] " + response.getStatusLine());
+                for ( org.apache.http.Header header : response.getAllHeaders() )
+                    System.out.println("[WEB] " + header);
+                
+                HttpEntity entity = response.getEntity();
+                // TODO - print response body
+                EntityUtils.consume(entity);
+            }
+            
+        }
+    }
+
 }