Package com.facebook.presto.jdbc.internal.airlift.http.client.netty

Source Code of com.facebook.presto.jdbc.internal.airlift.http.client.netty.NettyAsyncHttpClient$HttpConnectionCallback

package com.facebook.presto.jdbc.internal.airlift.http.client.netty;

import com.facebook.presto.jdbc.internal.guava.annotations.Beta;
import com.facebook.presto.jdbc.internal.guava.annotations.VisibleForTesting;
import com.facebook.presto.jdbc.internal.guava.base.Preconditions;
import com.facebook.presto.jdbc.internal.guava.base.Throwables;
import com.facebook.presto.jdbc.internal.guava.collect.ImmutableList;
import com.facebook.presto.jdbc.internal.airlift.concurrent.ThreadPoolExecutorMBean;
import com.facebook.presto.jdbc.internal.airlift.http.client.AsyncHttpClient;
import com.facebook.presto.jdbc.internal.airlift.http.client.BodyGenerator;
import com.facebook.presto.jdbc.internal.airlift.http.client.HttpClientConfig;
import com.facebook.presto.jdbc.internal.airlift.http.client.HttpRequestFilter;
import com.facebook.presto.jdbc.internal.airlift.http.client.Request;
import com.facebook.presto.jdbc.internal.airlift.http.client.RequestStats;
import com.facebook.presto.jdbc.internal.airlift.http.client.ResponseHandler;
import com.facebook.presto.jdbc.internal.airlift.http.client.netty.NettyConnectionPool.ConnectionCallback;
import com.facebook.presto.jdbc.internal.airlift.http.client.netty.NettyResponseFuture.NettyAsyncHttpState;
import com.facebook.presto.jdbc.internal.airlift.http.client.netty.socks.Socks4ClientBootstrap;
import com.facebook.presto.jdbc.internal.netty.bootstrap.ClientBootstrap;
import com.facebook.presto.jdbc.internal.netty.buffer.ChannelBufferOutputStream;
import com.facebook.presto.jdbc.internal.netty.buffer.DynamicChannelBuffer;
import com.facebook.presto.jdbc.internal.netty.channel.Channel;
import com.facebook.presto.jdbc.internal.netty.channel.ChannelFactory;
import com.facebook.presto.jdbc.internal.netty.channel.ChannelFuture;
import com.facebook.presto.jdbc.internal.netty.channel.ChannelFutureListener;
import com.facebook.presto.jdbc.internal.netty.channel.socket.nio.NioClientSocketChannelFactory;
import com.facebook.presto.jdbc.internal.netty.handler.codec.http.DefaultHttpRequest;
import com.facebook.presto.jdbc.internal.netty.handler.codec.http.HttpHeaders.Names;
import com.facebook.presto.jdbc.internal.netty.handler.codec.http.HttpMethod;
import com.facebook.presto.jdbc.internal.netty.handler.codec.http.HttpRequest;
import com.facebook.presto.jdbc.internal.netty.handler.codec.http.HttpVersion;
import com.facebook.presto.jdbc.internal.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;
import com.facebook.presto.jdbc.internal.netty.util.HashedWheelTimer;
import org.weakref.jmx.Flatten;
import org.weakref.jmx.Managed;
import org.weakref.jmx.Nested;

import javax.annotation.PreDestroy;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import static com.facebook.presto.jdbc.internal.airlift.concurrent.Threads.daemonThreadsNamed;

@Beta
public class NettyAsyncHttpClient
        implements AsyncHttpClient
{
    private final RequestStats stats = new RequestStats();
    private final List<HttpRequestFilter> requestFilters;

    private final OrderedMemoryAwareThreadPoolExecutor executor;
    private final ThreadPoolExecutorMBean executorMBean;
    private final NettyConnectionPool nettyConnectionPool;
    private final HashedWheelTimer timer;

    public NettyAsyncHttpClient(String name, HttpClientConfig config, NettyIoPool ioPool)
    {
        this(name, ioPool, config, new NettyAsyncHttpClientConfig(), Collections.<HttpRequestFilter>emptySet());
    }

    public NettyAsyncHttpClient(String name,
            NettyIoPool ioPool,
            HttpClientConfig config,
            NettyAsyncHttpClientConfig asyncConfig,
            Set<? extends HttpRequestFilter> requestFilters)
    {
        Preconditions.checkNotNull(name, "name is null");
        Preconditions.checkNotNull(ioPool, "ioPool is null");
        Preconditions.checkNotNull(config, "config is null");
        Preconditions.checkNotNull(asyncConfig, "asyncConfig is null");
        Preconditions.checkNotNull(requestFilters, "requestFilters is null");

        this.requestFilters = ImmutableList.copyOf(requestFilters);

        String namePrefix = "http-client-" + name;

        // shared timer for channel factory and read timeout channel handler
        this.timer = new HashedWheelTimer(daemonThreadsNamed(namePrefix + "-timer-%s"));

        ChannelFactory channelFactory = new NioClientSocketChannelFactory(ioPool.getBossPool(), ioPool.getWorkerPool());

        ThreadFactory workerThreadFactory = daemonThreadsNamed(namePrefix + "-worker-%s");
        this.executor = new OrderedMemoryAwareThreadPoolExecutor(asyncConfig.getWorkerThreads(), 0, 0, 30, TimeUnit.SECONDS, workerThreadFactory);
        this.executorMBean = new ThreadPoolExecutorMBean(executor);

        ClientBootstrap bootstrap;
        if (config.getSocksProxy() == null) {
            bootstrap = new ClientBootstrap(channelFactory);
        }
        else {
            bootstrap = new Socks4ClientBootstrap(channelFactory, config.getSocksProxy());
        }
        bootstrap.setOption("connectTimeoutMillis", config.getConnectTimeout().toMillis());
        bootstrap.setOption("soLinger", 0);

        nettyConnectionPool = new NettyConnectionPool(bootstrap,
                config.getMaxConnections(),
                executor,
                asyncConfig.isEnableConnectionPooling());

        HttpClientPipelineFactory pipelineFactory = new HttpClientPipelineFactory(nettyConnectionPool, timer, executor, config.getReadTimeout(), asyncConfig.getMaxContentLength());
        bootstrap.setPipelineFactory(pipelineFactory);
    }

    public List<HttpRequestFilter> getRequestFilters()
    {
        return requestFilters;
    }

    @SuppressWarnings("deprecation")
    @PreDestroy
    @Override
    public void close()
    {
        try {
            executor.shutdownNow();
        }
        catch (Exception e) {
            // ignored
        }

        try {
            nettyConnectionPool.close();
        }
        catch (Exception e) {
            // ignored
        }

        try {
            timer.stop();
        }
        catch (Exception e) {
            // ignored
        }
    }

    @Override
    public <T, E extends Exception> T execute(Request request, ResponseHandler<T, E> responseHandler)
            throws E
    {
        try {
            return executeAsync(request, responseHandler).get();
        }
        catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw Throwables.propagate(e);
        }
        catch (ExecutionException e) {
            Throwables.propagateIfPossible(e.getCause());

            if (e.getCause() instanceof Exception) {
                // the HTTP client and ResponseHandler interface enforces this
                throw (E) e.getCause();
            }

            // e.getCause() is some direct subclass of throwable
            throw Throwables.propagate(e.getCause());
        }
    }

    @Managed
    @Flatten
    @Override
    public RequestStats getStats()
    {
        return stats;
    }

    @Managed
    @Nested
    public ThreadPoolExecutorMBean getExecutor()
    {
        return executorMBean;
    }

    @Override
    public <T, E extends Exception> AsyncHttpResponseFuture<T> executeAsync(Request request, ResponseHandler<T, E> responseHandler)
    {
        // process the request through the filters
        for (HttpRequestFilter requestFilter : requestFilters) {
            request = requestFilter.filterRequest(request);
        }

        Preconditions.checkArgument("http".equalsIgnoreCase(request.getUri().getScheme()) || "https".equalsIgnoreCase(request.getUri().getScheme()),
                "%s only supports http and https requests", getClass().getSimpleName());

        // create a future for the caller
        NettyResponseFuture<T, E> nettyResponseFuture = new NettyResponseFuture<>(request, responseHandler, stats);

        // schedule the request with a connection
        nettyConnectionPool.execute(request.getUri(), new HttpConnectionCallback<>(request, nettyResponseFuture));

        // return caller's future
        return nettyResponseFuture;
    }

    @VisibleForTesting
    public static HttpRequest buildNettyHttpRequest(Request request)
            throws Exception
    {
        //
        // http request path
        URI uri = request.getUri();
        StringBuilder pathBuilder = new StringBuilder(100);
        // path part
        if (uri.getRawPath() == null || uri.getRawPath().isEmpty()) {
            pathBuilder.append('/');
        }
        else {
            pathBuilder.append(uri.getRawPath());
        }
        // query
        if (uri.getRawQuery() != null) {
            pathBuilder.append('?').append(uri.getRawQuery());
        }
        // http clients should not send the #fragment

        //
        // set http request line
        HttpRequest nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_1, new HttpMethod(request.getMethod()), pathBuilder.toString());

        //
        // set host header
        if (uri.getPort() == -1) {
            nettyRequest.setHeader(Names.HOST, uri.getHost());
        }
        else {
            nettyRequest.setHeader(Names.HOST, uri.getHost() + ":" + uri.getPort());
        }

        //
        // set user defined headers
        for (Entry<String, Collection<String>> header : request.getHeaders().asMap().entrySet()) {
            nettyRequest.setHeader(header.getKey(), header.getValue());
        }

        //
        // set body
        BodyGenerator bodyGenerator = request.getBodyGenerator();
        if (bodyGenerator != null) {
            DynamicChannelBuffer content = new DynamicChannelBuffer(64 * 1024);
            ChannelBufferOutputStream out = new ChannelBufferOutputStream(content);
            bodyGenerator.write(out);

            nettyRequest.setHeader(Names.CONTENT_LENGTH, content.readableBytes());
            nettyRequest.setContent(content);
        }
        return nettyRequest;
    }

    private static class HttpConnectionCallback<T, E extends Exception>
            implements ConnectionCallback
    {
        private final Request request;
        private final NettyResponseFuture<T, E> nettyResponseFuture;

        public HttpConnectionCallback(Request request, NettyResponseFuture<T, E> nettyResponseFuture)
        {
            this.request = request;
            this.nettyResponseFuture = nettyResponseFuture;
        }

        @Override
        public void run(Channel channel)
                throws Exception
        {
            nettyResponseFuture.setState(NettyAsyncHttpState.SENDING_REQUEST);

            // add the response handler to the channel object, so we can notify caller when request is complete
            channel.getPipeline().getContext(NettyHttpResponseChannelHandler.class).setAttachment(nettyResponseFuture);

            HttpRequest nettyRequest = buildNettyHttpRequest(request);
            channel.write(nettyRequest).addListener(new ChannelFutureListener()
            {
                @Override
                public void operationComplete(ChannelFuture future)
                        throws Exception
                {
                    if (future.isSuccess()) {
                        nettyResponseFuture.setState(NettyAsyncHttpState.WAITING_FOR_RESPONSE);
                    }
                    else if (future.isCancelled()) {
                        nettyResponseFuture.failed(new CanceledRequestException());
                    }
                    else {
                        Throwable cause = future.getCause();
                        if (cause == null) {
                            cause = new UnknownRequestException();
                        }
                        nettyResponseFuture.failed(cause);
                    }
                }
            });
        }

        @Override
        public void onError(Throwable throwable)
        {
            nettyResponseFuture.failed(throwable);
        }
    }
}
TOP

Related Classes of com.facebook.presto.jdbc.internal.airlift.http.client.netty.NettyAsyncHttpClient$HttpConnectionCallback

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.