Skip to main content

Appium Selenium Proxy

Connect Selenium / Appium java tests to the remote grid through a corporate proxy.

Java Client

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:

warning

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);
}

Python client

The code was tested with Appium-Python-Client versions 2.8.1 and 1.3.0

Appium-Python-Client 1.x should be used to run tests on Appium grid and Appium server 1.x

Appium-Python-Client 2.x should be used to run tests on  Appium server 1.x ans 2.x

Proxy URL is provided via environment variable.

import os
import unittest
from appium import webdriver

class AppiumTest(unittest.TestCase):
proxyUrl = 'http://10.100.102.6:9090'
cloudUrl = 'https://mycloud.experitest.com/wd/hub'
accessKey = "<access_key>"
dc = {}
driver = None

def setUp(self):
self.dc['accessKey'] = self.accessKey
self.dc['testName'] = 'Appium Python Proxy test'
self.dc['app'] = 'cloud:com.experitest.ExperiBank/.LoginActivity'
self.dc['appPackage'] = 'com.experitest.ExperiBank'
self.dc['appActivity'] = '.LoginActivity'
self.dc['platformName'] = 'android'
self.dc['appiumVersion'] = '1.22.3'
self.dc['deviceQuery'] = "@os='android'"

os.environ["HTTPS_PROXY"] = self.proxyUrl
self.driver = webdriver.Remote(self.cloudUrl, self.dc)

def testEribank(self):
self.driver.find_element("xpath", "//*[@text='Username']").send_keys('company')

def tearDown(self):
self.driver.quit()

if __name__ == '__main__':
unittest.main()