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

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

package com.hubspot.horizon.ning;

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 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 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 - 2025 Weber Informatics LLC | Privacy Policy