All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hubspot.chrome.devtools.client.ChromeDevToolsClient Maven / Gradle / Ivy

package com.hubspot.chrome.devtools.client;

import java.io.Closeable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.rholder.retry.RetryException;
import com.github.rholder.retry.Retryer;
import com.github.rholder.retry.RetryerBuilder;
import com.github.rholder.retry.StopStrategies;
import com.github.rholder.retry.WaitStrategies;
import com.hubspot.chrome.devtools.base.ChromeSessionInfo;
import com.hubspot.chrome.devtools.client.core.target.TargetID;
import com.hubspot.chrome.devtools.client.exceptions.ChromeDevToolsException;
import com.hubspot.horizon.HttpClient;
import com.hubspot.horizon.HttpRequest;
import com.hubspot.horizon.HttpRequest.Method;
import com.hubspot.horizon.HttpResponse;
import com.hubspot.horizon.HttpRuntimeException;

public class ChromeDevToolsClient implements Closeable {
  private static final Logger LOG = LoggerFactory.getLogger(ChromeDevToolsClient.class);

  private static final String WEBSOCKET_URL_TEMPLATE = "ws://%s:%s/devtools/page/%s";

  private final Retryer httpRetryer;

  private final ExecutorService executorService;
  private final ObjectMapper objectMapper;
  private final HttpClient httpClient;
  private final long actionTimeoutMillis;

  private ChromeDevToolsClient(ObjectMapper objectMapper, ExecutorService executorService, HttpClient httpClient, long actionTimeoutMillis, long sessionConnectTimeoutMillis) {
    this.executorService = executorService;
    this.objectMapper = objectMapper;
    this.httpClient = httpClient;
    this.actionTimeoutMillis = actionTimeoutMillis;
    this.httpRetryer = RetryerBuilder.newBuilder()
        .retryIfExceptionOfType(ChromeDevToolsException.class)
        .retryIfExceptionOfType(HttpRuntimeException.class)
        .withStopStrategy(StopStrategies.stopAfterDelay(sessionConnectTimeoutMillis))
        .withWaitStrategy(WaitStrategies.exponentialWait(100, TimeUnit.MILLISECONDS))
        .build();
  }

  public static ChromeDevToolsClient defaultClient() {
    return new Builder().build();
  }

  public ChromeDevToolsSession connect(String host, int port) throws URISyntaxException {
    TargetID targetId;
    try {
      targetId = httpRetryer.call(() -> getFirstAvailableTargetId(host, port));
    } catch (ExecutionException | RetryException e) {
      throw new ChromeDevToolsException(e);
    }
    String uri = String.format(WEBSOCKET_URL_TEMPLATE, host, port, targetId);
    return new ChromeDevToolsSession(new URI(uri), objectMapper, executorService, actionTimeoutMillis);
  }

  @Override
  public void close() {
    try {
      executorService.shutdown();
      httpClient.close();
    } catch (Throwable t) {
      LOG.error("Could not properly close chrome client", t);
    }
  }

  private TargetID getFirstAvailableTargetId(String host, int port) {
    String url = String.format("http://%s:%d/json/list", host, port);
    HttpRequest httpRequest = HttpRequest.newBuilder()
        .setUrl(url)
        .setMethod(Method.GET)
        .build();

    HttpResponse response = httpClient.execute(httpRequest);

    if (response.isError()) {
      throw new ChromeDevToolsException("Unable to find available chrome session info.");
    }

    List sessions = response.getAs(new TypeReference>() {});
    if (sessions.size() == 0) {
      throw new ChromeDevToolsException("No chrome sessions available.");
    }
    return new TargetID(sessions.get(0).getId());
  }

  public static class Builder {
    private ExecutorService executorService;
    private ObjectMapper objectMapper;
    private HttpClient httpClient;
    private long actionTimeoutMillis;
    private long sessionConnectTimeoutMillis;

    public Builder() {
      this.executorService = ChromeDevToolsClientDefaults.DEFAULT_EXECUTOR_SERVICE;
      this.objectMapper = ChromeDevToolsClientDefaults.DEFAULT_OBJECT_MAPPER;
      this.httpClient = ChromeDevToolsClientDefaults.DEFAULT_HTTP_CLIENT;
      this.actionTimeoutMillis = ChromeDevToolsClientDefaults.DEFAULT_CHROME_ACTION_TIMEOUT_MILLIS;
      this.sessionConnectTimeoutMillis = ChromeDevToolsClientDefaults.DEFAULT_HTTP_CONNECTION_RETRY_TIMEOUT_MILLIS;
    }

    public ChromeDevToolsClient.Builder setExecutorService(ExecutorService executorService) {
      this.executorService = executorService;
      return this;
    }

    public ChromeDevToolsClient.Builder setObjectMapper(ObjectMapper objectMapper) {
      this.objectMapper = objectMapper;
      return this;
    }

    public ChromeDevToolsClient.Builder setHttpClient(HttpClient httpClient) {
      this.httpClient = httpClient;
      return this;
    }

    public ChromeDevToolsClient.Builder setActionTimeoutMillis(long actionTimeoutMillis) {
      this.actionTimeoutMillis = actionTimeoutMillis;
      return this;
    }

    public Builder setSessionConnectTimeoutMillis(long sessionConnectTimeoutMillis) {
      this.sessionConnectTimeoutMillis = sessionConnectTimeoutMillis;
      return this;
    }

    public ChromeDevToolsClient build() {
      return new ChromeDevToolsClient(objectMapper, executorService, httpClient, actionTimeoutMillis, sessionConnectTimeoutMillis);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy