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.

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





  • No labels