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-whiteboard.git
commit eba62a80ea20ff0a71e048b805364120aeb1e264 Author: Robert Munteanu <[email protected]> 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"); + } + +}
