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/05/23 15:54:24 UTC

[sling-whiteboard] 03/08: POC for a Java Agent to set URL connection timeout defaults

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

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

commit 7da88ff098521199065c765236e4442b60b84798
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Thu May 23 17:42:48 2019 +0200

    POC for a Java Agent to set URL connection timeout defaults
    
    Completed initial implementation
---
 url-connection-agent/pom.xml                       |  4 ++
 .../main/java/org/apache/sling/uca/impl/Agent.java | 68 ++++++--------------
 .../main/java/org/apache/sling/uca/impl/Main.java  | 21 +++++-
 .../sling/uca/impl/URLTimeoutTransformer.java      | 74 ++++++++++++++++++++++
 .../sling/uca/impl/UrlTimeoutTransformerTest.java  | 31 +++++++++
 5 files changed, 146 insertions(+), 52 deletions(-)

diff --git a/url-connection-agent/pom.xml b/url-connection-agent/pom.xml
index cdaf3e1..6945823 100644
--- a/url-connection-agent/pom.xml
+++ b/url-connection-agent/pom.xml
@@ -47,5 +47,9 @@
             <artifactId>javassist</artifactId>
             <version>3.24.0-GA</version>
         </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </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 1391182..36f7c65 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
@@ -1,63 +1,31 @@
 package org.apache.sling.uca.impl;
 
-import java.io.IOException;
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.instrument.IllegalClassFormatException;
 import java.lang.instrument.Instrumentation;
-import java.security.ProtectionDomain;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
-import javassist.CannotCompileException;
-import javassist.ClassPool;
-import javassist.CtClass;
-import javassist.CtMethod;
-import javassist.NotFoundException;
+import java.util.concurrent.TimeUnit;
 
 public class Agent {
 
     public static void premain(String args, Instrumentation inst) {
 
-        System.out.println("Loading agent...");
-        inst.addTransformer(new URLTimeoutTransformer(), true);
-        System.out.println("Loaded agent!");
+        System.out.println("[AGENT] Loading agent...");
+        String[] parsedArgs = args.split(",");
+        long connectTimeout =  TimeUnit.MINUTES.toMillis(1);
+        long readTimeout = TimeUnit.MINUTES.toMillis(1);
+        if ( parsedArgs.length > 0 )
+            connectTimeout = Long.parseLong(parsedArgs[0]);
+        if ( parsedArgs.length > 1 )
+            readTimeout = Long.parseLong(parsedArgs[1]);
+        
+        System.out.format("[AGENT] Set connectTimeout : %d, readTimeout: %d%n", connectTimeout, readTimeout);
+
+        URLTimeoutTransformer transformer = new URLTimeoutTransformer(connectTimeout, readTimeout);
+        
+        inst.addTransformer(transformer, true);
+        System.out.println("[AGENT] Loaded agent!");
     }
-    
+
     public static void agentmain(String args, Instrumentation inst) {
         premain(args, inst);
     }
-
-    static class URLTimeoutTransformer implements ClassFileTransformer {
-        
-        private static final Set<String> CLASSES_TO_TRANSFORM = new HashSet<>();
-        
-        static {
-            CLASSES_TO_TRANSFORM.add("sun.net.www.protocol.http.HttpURLConnection".replace('.', '/'));
-            CLASSES_TO_TRANSFORM.add("sun.net.www.protocol.https.HttpsURLConnectionImpl".replace('.', '/'));
-        }
-        
-        private final Class<?> klazz = HashMap.class;
-        
-        @Override
-        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
-                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
-            try {
-                if ( CLASSES_TO_TRANSFORM.contains(className)) {
-                    System.out.println("Asked to transform " + className);
-                    CtClass cc = ClassPool.getDefault().get(klazz.getName());
-                    CtMethod connectMethod = cc.getDeclaredMethod("connect");
-                    connectMethod.insertBefore("if ( getConnectTimeout() == 0 ) { setConnectTimeout(60); }");
-                    connectMethod.insertBefore("if ( getReadTimeout() == 0 ) { setReadTimeout(60); }");
-                    classfileBuffer = cc.toBytecode();
-                    cc.detach();
-                    System.err.println("Transformation complete!");
-                }
-                return classfileBuffer;
-            } catch (NotFoundException | CannotCompileException | IOException e) {
-                throw new RuntimeException("Transformation failed", e);
-            }
-        }
-    }
-}
     
+}
diff --git a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Main.java b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Main.java
index 21bcbb7..883aa01 100644
--- a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Main.java
+++ b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Main.java
@@ -1,14 +1,31 @@
 package org.apache.sling.uca.impl;
 
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.URLConnection;
 
 public class Main {
-    
+
     public static void main(String[] args) throws MalformedURLException, IOException {
         
-        new URL("http://sling.apache.org").openConnection();
+        if ( args.length != 1 )
+            throw new IllegalArgumentException("Usage: java -jar ... <URL>");
+
+        URLConnection con = new URL(args[0]).openConnection();
+        System.out.println("Connection type is " + con);
+        
+        try (InputStream in = con.getInputStream();
+                InputStreamReader isr = new InputStreamReader(in);
+                BufferedReader br = new BufferedReader(isr)) {
+            String line;
+            while ( (line = br.readLine()) != null )
+                System.out.println("[WEB] " + line);
+        }
+
     }
 
 }
diff --git a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/URLTimeoutTransformer.java b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/URLTimeoutTransformer.java
new file mode 100644
index 0000000..a5b720d
--- /dev/null
+++ b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/URLTimeoutTransformer.java
@@ -0,0 +1,74 @@
+package org.apache.sling.uca.impl;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.net.URLConnection;
+import java.security.ProtectionDomain;
+import java.util.HashSet;
+import java.util.Set;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.Descriptor;
+
+/**
+ * Transforms well-known HTTP URL connection classes
+ * 
+ * <p>This implementation adds connect and read timeouts to those connections
+ * if none are defined.</p>
+ * 
+ * @see URLConnection#getConnectTimeout()
+ * @see URLConnection#getReadTimeout()
+ *
+ */
+class URLTimeoutTransformer implements ClassFileTransformer {
+
+    private static final Set<String> CLASSES_TO_TRANSFORM = new HashSet<>();
+
+    static {
+        CLASSES_TO_TRANSFORM.add(Descriptor.toJvmName("sun.net.www.protocol.http.HttpURLConnection"));
+        CLASSES_TO_TRANSFORM.add(Descriptor.toJvmName("sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection"));
+    }
+
+    private final long readTimeoutMillis;
+    private final long connectTimeoutMillis;
+
+    public URLTimeoutTransformer(long connectTimeout, long readTimeout) {
+        this.readTimeoutMillis = connectTimeout;
+        this.connectTimeoutMillis = readTimeout;
+    }
+
+    @Override
+    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
+            ProtectionDomain protectionDomain, byte[] classfileBuffer) {
+        try {
+            if (CLASSES_TO_TRANSFORM.contains(className)) {
+                System.out.println("[AGENT] Asked to transform " + className);
+                CtMethod connectMethod = findConnectMethod(className);
+                connectMethod.insertBefore("if ( getConnectTimeout() == 0 ) { setConnectTimeout(" + connectTimeoutMillis + "); }");
+                connectMethod.insertBefore("if ( getReadTimeout() == 0 ) { setReadTimeout(" + readTimeoutMillis + "); }");
+                classfileBuffer = connectMethod.getDeclaringClass().toBytecode();
+                connectMethod.getDeclaringClass().detach();
+                System.out.println("[AGENT] Transformation complete!");
+            }
+            return classfileBuffer;
+        } catch (Exception e) {
+            e.printStackTrace(); // ensure _something_ is printed
+            throw new RuntimeException("[AGENT] Transformation failed", e);
+        }
+    }
+    
+    CtMethod findConnectMethod(String className) throws NotFoundException {
+        
+        ClassPool defaultPool = ClassPool.getDefault();
+        CtClass cc = defaultPool.get(Descriptor.toJavaName(className));
+        if (cc == null) {
+            System.out.println("[AGENT] no class found with name " + className);
+            return null;
+        }
+        return cc.getDeclaredMethod("connect");
+
+    }
+
+}
\ No newline at end of file
diff --git a/url-connection-agent/src/test/java/org/apache/sling/uca/impl/UrlTimeoutTransformerTest.java b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/UrlTimeoutTransformerTest.java
new file mode 100644
index 0000000..84da14c
--- /dev/null
+++ b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/UrlTimeoutTransformerTest.java
@@ -0,0 +1,31 @@
+package org.apache.sling.uca.impl;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import javassist.NotFoundException;
+
+public class UrlTimeoutTransformerTest {
+    
+    private URLTimeoutTransformer transformer;
+
+    @Before
+    public void initFields() {
+        transformer = new URLTimeoutTransformer(1, 1);
+    }
+
+    @Test
+    public void findDeclaredConnectMethod() throws NotFoundException {
+        assertNotNull(transformer.findConnectMethod("sun/net/www/protocol/http/HttpURLConnection"));
+    }
+
+    @Test(expected = NotFoundException.class)
+    public void findInheritedConnectMethod() throws NotFoundException {
+        // do NOT look for inherited methods, as we can only rewrite the precise classes the
+        // retransform was triggered for
+        transformer.findConnectMethod("sun/net/www/protocol/https/DelegateHttpsURLConnection");
+    }
+    
+}