From 61b35a27bd953c3cdb3eadd6faf7280349b653e4 Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Sun, 4 Feb 2024 18:34:41 +0100 Subject: [PATCH 1/5] bump up selenium to 4.17.0 --- build.gradle | 13 +++++++++++-- .../core/webdriver/StartSessionInterceptor.java | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/build.gradle b/build.gradle index aeffacd..d72797f 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,15 @@ plugins { group = 'com.zebrunner' version = "${version != 'unspecified' ? version : '1.7.0'}" +sourceSets.all { + configurations.getByName(runtimeClasspathConfigurationName) { + attributes.attribute(Attribute.of("org.gradle.jvm.environment", String), "standard-jvm") + } + configurations.getByName(compileClasspathConfigurationName) { + attributes.attribute(Attribute.of("org.gradle.jvm.environment", String), "standard-jvm") + } +} + repositories { mavenCentral() } @@ -13,8 +22,8 @@ dependencies { compileOnly('com.konghq:unirest-java:3.13.10') implementation('org.yaml:snakeyaml:1.30') - implementation("net.bytebuddy:byte-buddy:1.12.18") - compileOnly('io.appium:java-client:8.3.0') + implementation("net.bytebuddy:byte-buddy:1.14.11") + compileOnly('org.seleniumhq.selenium:selenium-remote-driver:4.17.0') implementation('org.slf4j:slf4j-api:1.7.36') compileOnly("log4j:log4j:1.2.17") diff --git a/src/main/java/com/zebrunner/agent/core/webdriver/StartSessionInterceptor.java b/src/main/java/com/zebrunner/agent/core/webdriver/StartSessionInterceptor.java index 89c4a66..96f749f 100644 --- a/src/main/java/com/zebrunner/agent/core/webdriver/StartSessionInterceptor.java +++ b/src/main/java/com/zebrunner/agent/core/webdriver/StartSessionInterceptor.java @@ -13,7 +13,7 @@ import org.openqa.selenium.remote.HttpCommandExecutor; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.http.ClientConfig; -import org.openqa.selenium.remote.http.netty.NettyClient; +import org.openqa.selenium.remote.http.jdk.JdkHttpClient; import java.io.PrintWriter; import java.io.StringWriter; @@ -81,7 +81,7 @@ private static void substituteSeleniumHub(RemoteWebDriver driver) throws NoSuchF setFieldValue(commandExecutor, "remoteServer", seleniumHubUrl); Object clientObject = getFieldValue(commandExecutor, "client"); - if (clientObject instanceof NettyClient) { + if (clientObject instanceof JdkHttpClient) { String userInfo = seleniumHubUrl.getUserInfo(); if (userInfo != null && !userInfo.isEmpty()) { String[] credentials = userInfo.split(":", 2); From 427e6051eb1d9b3c9756f2cfc119bf7785bf0623 Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Sun, 4 Feb 2024 21:19:03 +0100 Subject: [PATCH 2/5] changes --- .../core/webdriver/DriverSessionsAgent.java | 12 ++++ .../HttpCommandExecutorInterceptor.java | 50 +++++++++++++++ .../webdriver/StartSessionInterceptor.java | 62 ------------------- 3 files changed, 62 insertions(+), 62 deletions(-) create mode 100644 src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java diff --git a/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java b/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java index c05cf24..453be1b 100644 --- a/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java +++ b/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java @@ -2,23 +2,30 @@ import lombok.extern.slf4j.Slf4j; import net.bytebuddy.agent.builder.AgentBuilder; +import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.NameMatcher; import net.bytebuddy.pool.TypePool; +import org.openqa.selenium.remote.CommandInfo; +import org.openqa.selenium.remote.http.ClientConfig; +import org.openqa.selenium.remote.http.HttpClient; import java.lang.instrument.Instrumentation; import java.util.Arrays; import java.util.HashSet; +import java.util.Map; import java.util.Set; import static net.bytebuddy.implementation.MethodDelegation.to; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isStatic; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.not; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; @Slf4j public class DriverSessionsAgent { @@ -54,6 +61,11 @@ public static void premain(String args, Instrumentation instrumentation) { .transform((builder, type, classloader, module, protectionDomain) -> builder.method(named(START_SESSION_METHOD_MAME)) .intercept(to(startSessionInterceptor()))) + .type(named("org.openqa.selenium.remote.HttpCommandExecutor")) + .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder + .visit(Advice + .to(HttpCommandExecutorInterceptor.class) + .on(isConstructor().and(takesArguments(Map.class, ClientConfig.class, HttpClient.Factory.class))))) .installOn(instrumentation); } catch (Exception e) { log.error("Could not add interceptors for RemoteWebDriver", e); diff --git a/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java b/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java new file mode 100644 index 0000000..aa37733 --- /dev/null +++ b/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java @@ -0,0 +1,50 @@ +package com.zebrunner.agent.core.webdriver; + +import com.zebrunner.agent.core.config.ConfigurationHolder; +import lombok.extern.slf4j.Slf4j; +import net.bytebuddy.implementation.bind.annotation.AllArguments; +import net.bytebuddy.implementation.bind.annotation.Morph; +import net.bytebuddy.implementation.bind.annotation.Origin; +import net.bytebuddy.implementation.bind.annotation.RuntimeType; +import net.bytebuddy.implementation.bind.annotation.This; +import org.openqa.selenium.UsernameAndPassword; +import org.openqa.selenium.remote.http.ClientConfig; + +import java.lang.reflect.Constructor; +import java.net.URL; +import java.util.concurrent.Callable; + +@Slf4j +public class HttpCommandExecutorInterceptor { + + @RuntimeType + public static Object constructor(@This Object inst, @Origin Constructor constructor, @Morph(defaultMethod = true) Callable callable, + @AllArguments Object[] allArguments) throws Exception { + if (ConfigurationHolder.shouldSubstituteRemoteWebDrivers()) { + log.debug("Selenium Hub URL will be substituted by the value provided from Zebrunner."); + int index = -1; + for (int i = 0; i < allArguments.length; i++) { + if (allArguments[i] instanceof ClientConfig) { + index = i; + break; + } + } + if (index >= 0) { + ClientConfig config = (ClientConfig) allArguments[index]; + URL seleniumHubUrl = RemoteWebDriverFactory.getSeleniumHubUrl(); + String userInfo = seleniumHubUrl.getUserInfo(); + if (userInfo != null && !userInfo.isEmpty()) { + String[] credentials = userInfo.split(":", 2); + String username = credentials[0]; + String password = credentials.length > 1 ? credentials[1] : ""; + config = config.authenticateAs(new UsernameAndPassword(username, password)); + } + config = config.baseUri(seleniumHubUrl.toURI()); + allArguments[index] = config; + } else { + log.warn("Could not find ClientConfig parameter in HttpCommandExecutor class."); + } + } + return callable.call(); + } +} diff --git a/src/main/java/com/zebrunner/agent/core/webdriver/StartSessionInterceptor.java b/src/main/java/com/zebrunner/agent/core/webdriver/StartSessionInterceptor.java index 96f749f..ec1e7d9 100644 --- a/src/main/java/com/zebrunner/agent/core/webdriver/StartSessionInterceptor.java +++ b/src/main/java/com/zebrunner/agent/core/webdriver/StartSessionInterceptor.java @@ -9,17 +9,11 @@ import net.bytebuddy.implementation.bind.annotation.SuperCall; import net.bytebuddy.implementation.bind.annotation.This; import org.openqa.selenium.Capabilities; -import org.openqa.selenium.UsernameAndPassword; -import org.openqa.selenium.remote.HttpCommandExecutor; import org.openqa.selenium.remote.RemoteWebDriver; -import org.openqa.selenium.remote.http.ClientConfig; -import org.openqa.selenium.remote.http.jdk.JdkHttpClient; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Field; -import java.net.URISyntaxException; -import java.net.URL; import java.util.Arrays; @Slf4j @@ -33,7 +27,6 @@ public static void onSessionStart(@This RemoteWebDriver driver, @SuperCall Runnable methodInvocationProxy, @Argument(0) Capabilities capabilities) throws Exception { if (ConfigurationHolder.shouldSubstituteRemoteWebDrivers()) { - substituteSeleniumHub(driver); capabilities = customizeCapabilities(methodInvocationProxy, capabilities); } @@ -72,60 +65,6 @@ public static void onSessionStart(@This RemoteWebDriver driver, } } - private static void substituteSeleniumHub(RemoteWebDriver driver) throws NoSuchFieldException, IllegalAccessException, URISyntaxException { - URL seleniumHubUrl = RemoteWebDriverFactory.getSeleniumHubUrl(); - if (driver.getCommandExecutor() instanceof HttpCommandExecutor && seleniumHubUrl != null) { - log.debug("Selenium Hub URL will be substituted by the value provided from Zebrunner."); - - HttpCommandExecutor commandExecutor = (HttpCommandExecutor) driver.getCommandExecutor(); - setFieldValue(commandExecutor, "remoteServer", seleniumHubUrl); - - Object clientObject = getFieldValue(commandExecutor, "client"); - if (clientObject instanceof JdkHttpClient) { - String userInfo = seleniumHubUrl.getUserInfo(); - if (userInfo != null && !userInfo.isEmpty()) { - String[] credentials = userInfo.split(":", 2); - String username = credentials[0]; - String password = credentials.length > 1 ? credentials[1] : ""; - - ClientConfig clientConfig = ((ClientConfig) getFieldValue(clientObject, "config")) - .baseUri(seleniumHubUrl.toURI()) - .authenticateAs(new UsernameAndPassword(username, password)); - setFieldValue(clientObject, "config", clientConfig); - } - } else { - log.debug("Could not substitute address of remote selenium hub because of unknown http client."); - } - } - } - - private static Object getFieldValue(Object targetObject, String fieldName) throws NoSuchFieldException, IllegalAccessException { - Field remoteServer = findField(targetObject.getClass(), fieldName); - remoteServer.setAccessible(true); - Object value = remoteServer.get(targetObject); - remoteServer.setAccessible(false); - return value; - } - - private static void setFieldValue(Object targetObject, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException { - Field remoteServer = findField(targetObject.getClass(), fieldName); - remoteServer.setAccessible(true); - remoteServer.set(targetObject, value); - remoteServer.setAccessible(false); - } - - private static Field findField(Class targetClass, String fieldName) throws NoSuchFieldException { - try { - return targetClass.getDeclaredField(fieldName); - } catch (NoSuchFieldException e) { - Class superclass = targetClass.getSuperclass(); - if (superclass != Object.class) { - return findField(superclass, fieldName); - } - throw e; - } - } - private static Capabilities customizeCapabilities(Runnable methodInvocationProxy, Capabilities capabilities) { Class methodInvocationProxyClass = methodInvocationProxy.getClass(); log.debug("Class of the #startSession() invocation proxy is {}", methodInvocationProxyClass.getName()); @@ -156,5 +95,4 @@ private static Capabilities customizeCapabilities(Runnable methodInvocationProxy return capabilities; } - } From 48c0c25a158d3d6f772acc8f8252f1d06c7cb4f9 Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Sun, 4 Feb 2024 21:27:26 +0100 Subject: [PATCH 3/5] changes --- .../agent/core/webdriver/DriverSessionsAgent.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java b/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java index 453be1b..a7ef9bd 100644 --- a/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java +++ b/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java @@ -20,6 +20,7 @@ import java.util.Set; import static net.bytebuddy.implementation.MethodDelegation.to; +import static net.bytebuddy.matcher.ElementMatchers.any; import static net.bytebuddy.matcher.ElementMatchers.isConstructor; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.isStatic; @@ -62,10 +63,9 @@ public static void premain(String args, Instrumentation instrumentation) { builder.method(named(START_SESSION_METHOD_MAME)) .intercept(to(startSessionInterceptor()))) .type(named("org.openqa.selenium.remote.HttpCommandExecutor")) - .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder - .visit(Advice - .to(HttpCommandExecutorInterceptor.class) - .on(isConstructor().and(takesArguments(Map.class, ClientConfig.class, HttpClient.Factory.class))))) + .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder. + constructor(takesArguments(Map.class, ClientConfig.class, HttpClient.Factory.class)) + .intercept(to(HttpCommandExecutorInterceptor.class))) .installOn(instrumentation); } catch (Exception e) { log.error("Could not add interceptors for RemoteWebDriver", e); From e5f1291ccae734815ed2120426a8b298982e2d6c Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Sun, 4 Feb 2024 22:17:33 +0100 Subject: [PATCH 4/5] changes --- .../agent/core/webdriver/DriverSessionsAgent.java | 8 +++++++- .../core/webdriver/HttpCommandExecutorInterceptor.java | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java b/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java index a7ef9bd..a8bf6cc 100644 --- a/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java +++ b/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java @@ -6,10 +6,14 @@ import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; +import net.bytebuddy.implementation.MethodDelegation; +import net.bytebuddy.implementation.bind.annotation.FieldProxy; +import net.bytebuddy.implementation.bind.annotation.Morph; import net.bytebuddy.matcher.ElementMatcher; import net.bytebuddy.matcher.NameMatcher; import net.bytebuddy.pool.TypePool; import org.openqa.selenium.remote.CommandInfo; +import org.openqa.selenium.remote.HttpCommandExecutor; import org.openqa.selenium.remote.http.ClientConfig; import org.openqa.selenium.remote.http.HttpClient; @@ -65,7 +69,9 @@ public static void premain(String args, Instrumentation instrumentation) { .type(named("org.openqa.selenium.remote.HttpCommandExecutor")) .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder. constructor(takesArguments(Map.class, ClientConfig.class, HttpClient.Factory.class)) - .intercept(to(HttpCommandExecutorInterceptor.class))) + .intercept(MethodDelegation.withDefaultConfiguration().withBinders( + Morph.Binder.install(HttpCommandExecutor.class) + ).to(HttpCommandExecutorInterceptor.class))) .installOn(instrumentation); } catch (Exception e) { log.error("Could not add interceptors for RemoteWebDriver", e); diff --git a/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java b/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java index aa37733..224e6b0 100644 --- a/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java +++ b/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java @@ -18,7 +18,7 @@ public class HttpCommandExecutorInterceptor { @RuntimeType - public static Object constructor(@This Object inst, @Origin Constructor constructor, @Morph(defaultMethod = true) Callable callable, + public static Object constructor(@Morph Callable callable, @AllArguments Object[] allArguments) throws Exception { if (ConfigurationHolder.shouldSubstituteRemoteWebDrivers()) { log.debug("Selenium Hub URL will be substituted by the value provided from Zebrunner."); From 0e790c0e1a247d7c88a2a17b4c27af1111a57c03 Mon Sep 17 00:00:00 2001 From: Andrei Kamarouski Date: Sun, 4 Feb 2024 22:21:20 +0100 Subject: [PATCH 5/5] changes --- .../core/webdriver/DriverSessionsAgent.java | 26 ++++++++++--------- .../HttpCommandExecutorInterceptor.java | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java b/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java index a8bf6cc..ae5a203 100644 --- a/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java +++ b/src/main/java/com/zebrunner/agent/core/webdriver/DriverSessionsAgent.java @@ -18,6 +18,7 @@ import org.openqa.selenium.remote.http.HttpClient; import java.lang.instrument.Instrumentation; +import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.HashSet; import java.util.Map; @@ -68,7 +69,8 @@ public static void premain(String args, Instrumentation instrumentation) { .intercept(to(startSessionInterceptor()))) .type(named("org.openqa.selenium.remote.HttpCommandExecutor")) .transform((builder, typeDescription, classLoader, module, protectionDomain) -> builder. - constructor(takesArguments(Map.class, ClientConfig.class, HttpClient.Factory.class)) + defineConstructor(Modifier.PUBLIC) + .withParameters(Map.class, ClientConfig.class, HttpClient.Factory.class) .intercept(MethodDelegation.withDefaultConfiguration().withBinders( Morph.Binder.install(HttpCommandExecutor.class) ).to(HttpCommandExecutorInterceptor.class))) @@ -86,29 +88,29 @@ public static ElementMatcher isPublicMethodToIntercep private static DynamicType.Builder addInterceptors(DynamicType.Builder builder) { return builder.method(isPublicMethodToIntercept()) - .intercept(to(publicMethodsInterceptor())) - .method(named(START_SESSION_METHOD_MAME)) - .intercept(to(startSessionInterceptor())) - .method(named(QUIT_METHOD_MAME)) - .intercept(to(quitSessionInterceptor())); + .intercept(to(publicMethodsInterceptor())) + .method(named(START_SESSION_METHOD_MAME)) + .intercept(to(startSessionInterceptor())) + .method(named(QUIT_METHOD_MAME)) + .intercept(to(quitSessionInterceptor())); } private static TypeDescription publicMethodsInterceptor() { return TypePool.Default.ofSystemLoader() - .describe(PublicMethodInvocationInterceptor.class.getName()) - .resolve(); + .describe(PublicMethodInvocationInterceptor.class.getName()) + .resolve(); } private static TypeDescription startSessionInterceptor() { return TypePool.Default.ofSystemLoader() - .describe(StartSessionInterceptor.class.getName()) - .resolve(); + .describe(StartSessionInterceptor.class.getName()) + .resolve(); } private static TypeDescription quitSessionInterceptor() { return TypePool.Default.ofSystemLoader() - .describe(QuitSessionInterceptor.class.getName()) - .resolve(); + .describe(QuitSessionInterceptor.class.getName()) + .resolve(); } } diff --git a/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java b/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java index 224e6b0..d49007c 100644 --- a/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java +++ b/src/main/java/com/zebrunner/agent/core/webdriver/HttpCommandExecutorInterceptor.java @@ -18,7 +18,7 @@ public class HttpCommandExecutorInterceptor { @RuntimeType - public static Object constructor(@Morph Callable callable, + public static Object constructor(@This Object inst, @Morph Callable callable, @AllArguments Object[] allArguments) throws Exception { if (ConfigurationHolder.shouldSubstituteRemoteWebDrivers()) { log.debug("Selenium Hub URL will be substituted by the value provided from Zebrunner.");