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

com.hubspot.horizon.ning.NingAsyncHttpClient Maven / Gradle / Ivy

There is a newer version: 0.3.0
Show newest version
package com.hubspot.horizon.ning;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ListenableFuture;
import com.hubspot.horizon.AsyncHttpClient;
import com.hubspot.horizon.HttpConfig;
import com.hubspot.horizon.HttpRequest;
import com.hubspot.horizon.HttpRequest.Options;
import com.hubspot.horizon.HttpResponse;
import com.hubspot.horizon.ning.internal.AcceptEncodingRequestFilter;
import com.hubspot.horizon.ning.internal.DefaultHeadersRequestFilter;
import com.hubspot.horizon.ning.internal.EmptyCallback;
import com.hubspot.horizon.ning.internal.NingCompletionHandler;
import com.hubspot.horizon.ning.internal.NingFuture;
import com.hubspot.horizon.ning.internal.NingHttpRequestConverter;
import com.hubspot.horizon.ning.internal.NingRetryHandler;
import com.hubspot.horizon.ning.internal.NingSSLContext;
import java.io.IOException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import org.asynchttpclient.shaded.AsyncHttpClientConfig;
import org.asynchttpclient.shaded.DefaultAsyncHttpClient;
import org.asynchttpclient.shaded.DefaultAsyncHttpClientConfig;
import org.asynchttpclient.shaded.Request;
import org.asynchttpclient.shaded.filter.ThrottleRequestFilter;
import org.asynchttpclient.shaded.io.netty.channel.EventLoopGroup;
import org.asynchttpclient.shaded.io.netty.channel.nio.NioEventLoopGroup;
import org.asynchttpclient.shaded.io.netty.util.HashedWheelTimer;
import org.asynchttpclient.shaded.io.netty.util.concurrent.DefaultThreadFactory;
import org.asynchttpclient.shaded.proxy.ProxyServer;
import org.asynchttpclient.shaded.proxy.ProxyType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NingAsyncHttpClient implements AsyncHttpClient {

  private static final HashedWheelTimer TIMER = new HashedWheelTimer(
    newThreadFactory("NingAsyncHttpClient-Timer")
  );
  private static final Logger LOG = LoggerFactory.getLogger(NingAsyncHttpClient.class);

  private final org.asynchttpclient.shaded.AsyncHttpClient ningClient;
  private final NingHttpRequestConverter requestConverter;
  private final Options defaultOptions;
  private final ObjectMapper mapper;
  private final EventLoopGroup eventLoopGroup;

  public NingAsyncHttpClient() {
    this(HttpConfig.newBuilder().build());
  }

  public NingAsyncHttpClient(HttpConfig config) {
    Preconditions.checkNotNull(config);

    this.eventLoopGroup = newEventLoopGroup();

    DefaultAsyncHttpClientConfig.Builder builder =
      new DefaultAsyncHttpClientConfig.Builder();

    if (config.isSocksProxied()) {
      LOG.debug(
        "Client will be routed via SOCKS proxy {}:{}",
        config.getSocksProxyHost().get(),
        config.getSocksProxyPort()
      );
      ProxyServer proxyServer = new ProxyServer.Builder(
        config.getSocksProxyHost().get(),
        config.getSocksProxyPort()
      )
        .setProxyType(ProxyType.SOCKS_V5)
        .build();
      builder.setProxyServer(proxyServer);
    }

    AsyncHttpClientConfig ningConfig = builder
      .addRequestFilter(new ThrottleRequestFilter(config.getMaxConnections()))
      .addRequestFilter(new AcceptEncodingRequestFilter())
      .addRequestFilter(new DefaultHeadersRequestFilter(config))
      .setMaxConnectionsPerHost(config.getMaxConnectionsPerHost())
      .setConnectionTtl(config.getConnectionTtlMillis())
      .setConnectTimeout(config.getConnectTimeoutMillis())
      .setRequestTimeout(config.getRequestTimeoutMillis())
      .setReadTimeout(config.getRequestTimeoutMillis())
      .setMaxRedirects(config.getMaxRedirects())
      .setFollowRedirect(config.isFollowRedirects())
      .setSslContext(NingSSLContext.forConfig(config.getSSLConfig()))
      .setUserAgent(config.getUserAgent())
      .setEventLoopGroup(eventLoopGroup)
      .setNettyTimer(TIMER)
      .setMaxRequestRetry(0) // we handle retries ourselves
      .build();

    this.ningClient = new DefaultAsyncHttpClient(ningConfig);
    this.requestConverter = new NingHttpRequestConverter(config.getObjectMapper());
    this.defaultOptions = config.getOptions();
    this.mapper = config.getObjectMapper();
  }

  @Override
  public ListenableFuture execute(HttpRequest request) {
    return execute(Preconditions.checkNotNull(request), Options.DEFAULT);
  }

  @Override
  public ListenableFuture execute(HttpRequest request, Options options) {
    return internalExecute(request, options, EmptyCallback.INSTANCE);
  }

  @Override
  public void execute(HttpRequest request, Callback callback) {
    execute(Preconditions.checkNotNull(request), Options.DEFAULT, callback);
  }

  @Override
  public void execute(HttpRequest request, Options options, Callback callback) {
    internalExecute(request, options, callback);
  }

  private ListenableFuture internalExecute(
    HttpRequest request,
    Options options,
    Callback callback
  ) {
    Preconditions.checkNotNull(request);
    Preconditions.checkNotNull(options);
    Preconditions.checkNotNull(callback);

    NingRetryHandler retryHandler = new NingRetryHandler(
      defaultOptions.mergeFrom(options)
    );
    NingFuture future = new NingFuture(callback);

    final NingCompletionHandler completionHandler = new NingCompletionHandler(
      request,
      future,
      retryHandler,
      mapper
    );
    final Request ningRequest = requestConverter.convert(request);
    Runnable runnable = () -> {
      try {
        ningClient.executeRequest(ningRequest, completionHandler);
      } catch (RuntimeException e) {
        completionHandler.onThrowable(e);
      }
    };
    retryHandler.setRetryRunnable(runnable);

    runnable.run();
    return future;
  }

  private EventLoopGroup newEventLoopGroup() {
    ThreadFactory threadFactory = newThreadFactory("NingAsyncHttpClient");
    int workerThreads = Math.min(Runtime.getRuntime().availableProcessors(), 4);

    return new NioEventLoopGroup(workerThreads, threadFactory);
  }

  private static ThreadFactory newThreadFactory(String name) {
    return new DefaultThreadFactory(name, true);
  }

  @Override
  public void close() throws IOException {
    try {
      ningClient.close();
    } finally {
      eventLoopGroup.shutdownGracefully(0, 5, TimeUnit.SECONDS);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy