Page tree
Skip to end of metadata
Go to start of metadata

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:

It is important to keep the package name

Proxy Netty Client
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
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
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
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()




  • No labels