- Created by Ola Hamud on Jun 30, 2022
Connect Selenium / Appium java tests to the remote grid through a corporate proxy.
Appium Client 8 / Selenium 4
The code was tested with Appium java client version 8.0.0
Appium client 8.x should be used to run tests on Appium server 2.x
Copy the following code to your client-side:
It is important to keep the package name
Proxy Netty Client
Expand source
package org.openqa.selenium.remote.http.netty; import io.netty.util.HashedWheelTimer; import io.netty.util.Timer; import io.netty.util.concurrent.DefaultThreadFactory; import org.asynchttpclient.AsyncHttpClient; import org.asynchttpclient.DefaultAsyncHttpClientConfig; import org.asynchttpclient.Dsl; import org.asynchttpclient.config.AsyncHttpClientConfigDefaults; import org.openqa.selenium.internal.Require; import org.openqa.selenium.remote.http.*; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; /** * Based on this code with disabled proxySelector: .setUseProxySelector(false) * https://github.com/SeleniumHQ/selenium/blob/selenium-4.3.0/java/src/org/openqa/selenium/remote/http/netty/NettyClient.java */ public class ProxyNettyClient implements HttpClient { private static final Timer TIMER; private static final AsyncHttpClient client = createHttpClient(ClientConfig.defaultConfig()); static { ThreadFactory threadFactory = new DefaultThreadFactory("netty-client-timer", true); TIMER = new HashedWheelTimer( threadFactory, AsyncHttpClientConfigDefaults.defaultHashedWheelTimerTickDuration(), TimeUnit.MILLISECONDS, AsyncHttpClientConfigDefaults.defaultHashedWheelTimerSize()); } private final ClientConfig config; private final HttpHandler handler; private final BiFunction<HttpRequest, WebSocket.Listener, WebSocket> toWebSocket; private ProxyNettyClient(ClientConfig config) { this.config = Require.nonNull("HTTP client config", config); this.handler = new NettyHttpHandler(config, client).with(config.filter()); this.toWebSocket = NettyWebSocket.create(config, client); } /** * Converts a long to an int, clamping the maximum and minimum values to * fit in an integer without overflowing. */ static int toClampedInt(long value) { return (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, value)); } private static AsyncHttpClient createHttpClient(ClientConfig config) { DefaultAsyncHttpClientConfig.Builder builder = new DefaultAsyncHttpClientConfig.Builder() .setThreadFactory(new DefaultThreadFactory("AsyncHttpClient", true)) .setUseInsecureTrustManager(true) .setAggregateWebSocketFrameFragments(true) .setWebSocketMaxBufferSize(Integer.MAX_VALUE) .setWebSocketMaxFrameSize(Integer.MAX_VALUE) .setNettyTimer(TIMER) .setRequestTimeout(toClampedInt(config.readTimeout().toMillis())) .setConnectTimeout(toClampedInt(config.connectionTimeout().toMillis())) .setReadTimeout(toClampedInt(config.readTimeout().toMillis())) .setFollowRedirect(true) .setUseProxyProperties(true) .setUseProxySelector(false) .setMaxRequestRetry(0); return Dsl.asyncHttpClient(builder); } @Override public HttpResponse execute(HttpRequest request) { return handler.execute(request); } @Override public WebSocket openSocket(HttpRequest request, WebSocket.Listener listener) { Require.nonNull("Request to send", request); Require.nonNull("WebSocket listener", listener); return toWebSocket.apply(request, listener); } @Override public HttpClient with(Filter filter) { Require.nonNull("Filter", filter); return new ProxyNettyClient(config.withFilter(filter)); } @Override public void close() { // no-op -- closing the client when the JVM shuts down } @HttpClientName("netty") public static class Factory implements HttpClient.Factory { @Override public HttpClient createClient(ClientConfig config) { Require.nonNull("Client config", config); if (config.baseUri() != null && "unix".equals(config.baseUri().getScheme())) { return new NettyDomainSocketClient(config); } return new ProxyNettyClient(config); } } }
Running Appium test using proxy:
Example
Expand source
package example; import io.appium.java_client.MobileCommand; import io.appium.java_client.ios.IOSDriver; import io.appium.java_client.remote.AppiumCommandExecutor; import io.appium.java_client.remote.MobileCapabilityType; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.HttpCommandExecutor; import org.openqa.selenium.remote.http.netty.ProxyNettyClient; import java.net.URL; public class AppiumViaProxy { static String cloudUrl = "https://myorg.experitest.com/wd/hub"; static String accessKey = "<access_key>"; IOSDriver driver = null; protected DesiredCapabilities dc = new DesiredCapabilities(); static { System.setProperty("http.proxyHost", "localhost"); System.setProperty("http.proxyPort", "3128"); System.setProperty("org.asynchttpclient.AsyncHttpClientConfig.proxy.user", "username"); System.setProperty("org.asynchttpclient.AsyncHttpClientConfig.proxy.password", "password"); } public void start() throws Exception { dc.setCapability(MobileCapabilityType.PLATFORM_NAME, "ios"); dc.setCapability(MobileCapabilityType.AUTOMATION_NAME, "XCUITest"); dc.setCapability("deviceQuery", "@os='ios'"); dc.setCapability("accessKey", accessKey); dc.setCapability("appiumVersion", "2.0.0.beta.23"); HttpCommandExecutor commandExecutor = new AppiumCommandExecutor( MobileCommand.commandRepository, new URL(cloudUrl), new ProxyNettyClient.Factory()); driver = new IOSDriver(commandExecutor, dc); System.out.println("Page source len: " + driver.getPageSource()); driver.quit(); } public static void main(String[] args) throws Exception { new AppiumViaProxy().start(); } }
Appium Client 7 or below
The provided code was tested with Appium java client version 7.1.0.
Appium client 7 or below should be used to run tests on Appium server 1.x
Copy the following code to your client-side :
Web-Driver Proxy Client Factory
Expand source
import io.appium.java_client.MobileCommand; import io.appium.java_client.remote.AppiumCommandExecutor; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.URL; import java.util.concurrent.TimeUnit; import okhttp3.*; import org.openqa.selenium.remote.http.HttpClient; public class WebDriverProxyClientFactory implements HttpClient.Factory { public static AppiumCommandExecutor createAppiumExecutor(URL url, String proxyHost, int proxyPort, String proxyUsername, String proxyPassword) { return new AppiumCommandExecutor(MobileCommand.commandRepository, url, new WebDriverProxyClientFactory(proxyHost, proxyPort, proxyUsername, proxyPassword)); } private String proxyHost; private int proxyPort; private String proxyUsername; private String proxyPassword; public WebDriverProxyClientFactory(String proxyHost, int proxyPort, String proxyUsername, String proxyPassword) { this.proxyHost = proxyHost; this.proxyPort = proxyPort; this.proxyUsername = proxyUsername; this.proxyPassword = proxyPassword; } private final ConnectionPool pool = new ConnectionPool(); @Override public HttpClient.Builder builder() { return new org.openqa.selenium.remote.http.HttpClient.Builder() { public HttpClient createClient(URL url) { // configure the proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)); // configure the credentials and register in the builder Authenticator authenticator = null; if (proxyUsername != null && proxyPassword != null) { String credential = Credentials.basic(proxyUsername, proxyPassword); authenticator = new Authenticator() { public Request authenticate(Route route, Response response) throws IOException { return response.request().newBuilder() .header("Proxy-Authorization", credential) .build(); } }; } OkHttpClient.Builder client = (new OkHttpClient.Builder()) .connectionPool(WebDriverProxyClientFactory.this.pool) .followRedirects(true) .followSslRedirects(true) .proxy(this.proxy) .readTimeout(this.readTimeout.toMillis(), TimeUnit.MILLISECONDS) .connectTimeout(this.connectionTimeout.toMillis(), TimeUnit.MILLISECONDS) .proxyAuthenticator(authenticator); return new org.openqa.selenium.remote.internal.OkHttpClient(client.build(), url); } }; } @Override public void cleanupIdleClients() { this.pool.evictAll(); } }
Running Appium test using proxy:
Example
Expand source
static String host = "<env>.experitest.com"; static String accessKey = "<access key>"; static String proxyHost = "<proxy host>"; static int proxyPort = 8080; static String proxyUser = "user"; static String proxyPassword = "password"; IOSDriver driver = null; protected DesiredCapabilities dc = new DesiredCapabilities(); @Before public void setUp() throws Exception{ dc.setCapability("deviceQuery", "@os='ios'"); dc.setCapability("accessKey", accessKey); dc.setCapability("platformName", "ios"); URL url = new URL("https://" + host + "/wd/hub"); driver = new IOSDriver<IOSElement>( WebDriverProxyClientFactory.createAppiumExecutor(url, proxyHost, proxyPort , proxyUser, proxyPassword), dc); }
- No labels