com.facebook.airlift.http.client.testing.TestingHttpClient Maven / Gradle / Ivy
The newest version!
package com.facebook.airlift.http.client.testing;
import com.facebook.airlift.http.client.HttpClient;
import com.facebook.airlift.http.client.HttpClientConfig;
import com.facebook.airlift.http.client.Request;
import com.facebook.airlift.http.client.RequestStats;
import com.facebook.airlift.http.client.Response;
import com.facebook.airlift.http.client.ResponseHandler;
import com.google.common.util.concurrent.ForwardingListenableFuture;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import io.airlift.units.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.util.concurrent.MoreExecutors.listeningDecorator;
import static com.google.common.util.concurrent.MoreExecutors.newDirectExecutorService;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
public class TestingHttpClient
implements HttpClient
{
private final Processor processor;
private final ListeningExecutorService executor;
private final RequestStats stats = new RequestStats();
private final AtomicBoolean closed = new AtomicBoolean();
public TestingHttpClient(Processor processor)
{
this(processor, newDirectExecutorService());
}
public TestingHttpClient(Processor processor, ExecutorService executor)
{
this.processor = requireNonNull(processor, "processor is null");
this.executor = listeningDecorator(requireNonNull(executor, "executor is null"));
}
@Override
public HttpResponseFuture executeAsync(Request request, ResponseHandler responseHandler)
{
requireNonNull(request, "request is null");
requireNonNull(responseHandler, "responseHandler is null");
checkState(!closed.get(), "client is closed");
AtomicReference state = new AtomicReference<>("SENDING_REQUEST");
ListenableFuture future = executor.submit(() -> execute(request, responseHandler, state));
return new TestingHttpResponseFuture<>(future, state);
}
@Override
public T execute(Request request, ResponseHandler responseHandler)
throws E
{
requireNonNull(request, "request is null");
requireNonNull(responseHandler, "responseHandler is null");
checkState(!closed.get(), "client is closed");
return execute(request, responseHandler, new AtomicReference<>("SENDING_REQUEST"));
}
private T execute(Request request, ResponseHandler responseHandler, AtomicReference state)
throws E
{
state.set("PROCESSING_REQUEST");
Response response;
long requestStart = System.nanoTime();
try {
response = processor.handle(request);
}
catch (Exception | Error e) {
state.set("FAILED");
long responseStart = System.nanoTime();
Duration requestProcessingTime = new Duration(responseStart - requestStart, NANOSECONDS);
if (e instanceof Exception) {
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
try {
return responseHandler.handleException(request, (Exception) e);
}
finally {
stats.recordResponseReceived(request.getMethod(),
0,
0,
0,
requestProcessingTime,
Duration.nanosSince(responseStart));
}
}
stats.recordResponseReceived(request.getMethod(),
0,
0,
0,
requestProcessingTime,
new Duration(0, NANOSECONDS));
throw (Error) e;
}
checkState(response != null, "response is null");
// notify handler
state.set("PROCESSING_RESPONSE");
long responseStart = System.nanoTime();
Duration requestProcessingTime = new Duration(responseStart - requestStart, NANOSECONDS);
try {
return responseHandler.handle(request, response);
}
finally {
state.set("DONE");
stats.recordResponseReceived(request.getMethod(),
response.getStatusCode(),
response.getBytesRead(),
response.getBytesRead(),
requestProcessingTime,
Duration.nanosSince(responseStart));
}
}
@Override
public RequestStats getStats()
{
return stats;
}
@Override
public long getMaxContentLength()
{
return new HttpClientConfig().getMaxContentLength().toBytes();
}
@Override
public void close()
{
closed.set(true);
}
@Override
public boolean isClosed()
{
return closed.get();
}
public interface Processor
{
Response handle(Request request)
throws Exception;
}
private class TestingHttpResponseFuture
extends ForwardingListenableFuture
implements HttpResponseFuture
{
private final AtomicReference state;
private final ListenableFuture future;
private TestingHttpResponseFuture(ListenableFuture future, AtomicReference state)
{
this.future = future;
this.state = state;
}
@Override
protected ListenableFuture delegate()
{
return future;
}
@Override
public String getState()
{
return state.get();
}
}
}