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.AsyncHttpClientConfig;
import org.asynchttpclient.DefaultAsyncHttpClient;
import org.asynchttpclient.DefaultAsyncHttpClientConfig;
import org.asynchttpclient.Request;
import org.asynchttpclient.filter.ThrottleRequestFilter;

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.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 io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.HashedWheelTimer;
import io.netty.util.concurrent.DefaultThreadFactory;

public class NingAsyncHttpClient implements AsyncHttpClient {
  private static final HashedWheelTimer TIMER = new HashedWheelTimer(newThreadFactory("NingAsyncHttpClient-Timer"));

  private final org.asynchttpclient.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();

    AsyncHttpClientConfig ningConfig = new DefaultAsyncHttpClientConfig.Builder()
            .addRequestFilter(new ThrottleRequestFilter(config.getMaxConnections()))
            .addRequestFilter(new AcceptEncodingRequestFilter())
            .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