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

com.aliyun.httpcomponent.httpclient.ApacheAsyncHttpClient Maven / Gradle / Ivy

There is a newer version: 0.2.16-beta
Show newest version
package com.aliyun.httpcomponent.httpclient;

import com.aliyun.core.http.*;
import com.aliyun.core.logging.ClientLogger;
import com.aliyun.core.utils.BinaryUtils;
import com.aliyun.core.utils.Context;
import com.aliyun.core.utils.StringUtils;
import com.aliyun.httpcomponent.httpclient.implementation.ApacheAsyncHttpResponse;
import com.aliyun.httpcomponent.httpclient.implementation.StreamRequestProducer;
import com.aliyun.httpcomponent.httpclient.implementation.reactive.ReactiveApacheHttpResponse;
import com.aliyun.httpcomponent.httpclient.implementation.reactive.ReactiveHttpResponse;
import com.aliyun.httpcomponent.httpclient.implementation.reactive.ReactiveResponseConsumer;
import com.aliyun.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import com.aliyun.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import com.aliyun.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import com.aliyun.apache.hc.client5.http.async.methods.SimpleResponseConsumer;
import com.aliyun.apache.hc.client5.http.config.RequestConfig;
import com.aliyun.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import com.aliyun.apache.hc.core5.concurrent.FutureCallback;
import com.aliyun.apache.hc.core5.http.ContentType;
import com.aliyun.apache.hc.core5.http.HttpHost;
import com.aliyun.apache.hc.core5.http.nio.AsyncRequestProducer;
import com.aliyun.apache.hc.core5.io.CloseMode;
import com.aliyun.apache.hc.core5.util.TimeValue;
import com.aliyun.apache.hc.core5.util.Timeout;

import java.net.*;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import static com.aliyun.core.http.ProxyOptions.Type.HTTP;

class ApacheAsyncHttpClient implements HttpClient {
    private final String RESPONSE_HANDLER_KEY = "RESPONSE_HANDLER";
    private final ClientLogger logger = new ClientLogger(ApacheAsyncHttpClient.class);
    private final CloseableHttpAsyncClient apacheHttpAsyncClient;
    private final Timeout connectTimeout;
    private final long connectionKeepAlive;

    ApacheAsyncHttpClient(CloseableHttpAsyncClient apacheHttpAsyncClient, Timeout connectTimeout, long connectionKeepAlive) {
        this.apacheHttpAsyncClient = apacheHttpAsyncClient;
        this.connectTimeout = connectTimeout;
        this.connectionKeepAlive = connectionKeepAlive;
    }

    @Override
    public CompletableFuture send(HttpRequest request) {
        return send(request, Context.NONE);
    }

    @Override
    public CompletableFuture send(HttpRequest request, Context context) {
        apacheHttpAsyncClient.start();
        if (request.getStreamBody() != null) {
            if (context.getData(RESPONSE_HANDLER_KEY).isPresent()) {
                return sendV3(request, context);
            }
            return sendV2(request, context);
        } else {
            return sendV1(request, context);
        }
    }

    @Override
    public void close() {
        if (apacheHttpAsyncClient != null)
            apacheHttpAsyncClient.close(CloseMode.GRACEFUL);
    }

    private SimpleHttpRequest toApacheAsyncRequest(HttpRequest request) throws URISyntaxException, ExecutionException, InterruptedException {
        final SimpleRequestBuilder apacheRequestBuilder = SimpleRequestBuilder.create(request.getHttpMethod().toString())
                .setUri(request.getUrl().toURI());
        final HttpHeaders headers = request.getHeaders();
        for (HttpHeader httpHeader : headers) {
            apacheRequestBuilder.setHeader(httpHeader.getName(), httpHeader.getValue());
        }

        switch (request.getHttpMethod()) {
            case GET:
            case HEAD:
            case DELETE:
                return apacheRequestBuilder.build();
            default:
                ContentType type;
                if (StringUtils.isEmpty(headers.getValue("content-type"))) {
                    type = ContentType.APPLICATION_FORM_URLENCODED;
                } else {
                    String[] ct = headers.getValue("content-type").split(";");
                    if (ct.length > 1) {
                        type = ContentType.create(ct[0].trim(), ct[1].replace("charset=", "").trim());
                    } else {
                        type = ContentType.create(ct[0].trim());
                    }
                }
                if (request.getBody() != null) {
                    apacheRequestBuilder.setBody(BinaryUtils.copyAllBytesFrom(request.getBody()), type);
                }
                return apacheRequestBuilder.build();
        }
    }

    private CompletableFuture sendV1(HttpRequest request, Context context) {
        Objects.requireNonNull(request.getHttpMethod(), "'request.getHttpMethod()' cannot be null.");
        Objects.requireNonNull(request.getUrl(), "'request.getUrl()' cannot be null.");
        Objects.requireNonNull(request.getUrl().getProtocol(), "'request.getUrl().getProtocol()' cannot be null.");

        SimpleHttpRequest apacheRequest;
        try {
            apacheRequest = toApacheAsyncRequest(request);
        } catch (URISyntaxException | ExecutionException | InterruptedException e) {
            throw logger.logExceptionAsWarning(new IllegalArgumentException("'url' must can convert to a valid URI", e));
        }

        // set individual request config
        apacheRequest.setConfig(new ApacheIndividualRequestBuilder(request, connectTimeout, connectionKeepAlive).build());

//        ProxyOptions proxyOptions = request.getProxyOptions();
//        if (proxyOptions != null && proxyOptions.getUsername() != null) {
//            String userInfo = proxyOptions.getUsername() + ":" + proxyOptions.getPassword();
//            apacheRequest.setAuthority(new URIAuthority(
//                    userInfo,
//                    proxyOptions.getAddress().getHostString(),
//                    proxyOptions.getAddress().getPort()));
//        }
        CompletableFuture cf = new CompletableFuture<>();
        final Future future = apacheHttpAsyncClient.execute(
                apacheRequest,
                new FutureCallback() {
                    @Override
                    public void completed(SimpleHttpResponse response) {
                        cf.complete(response);
                    }

                    @Override
                    public void failed(final Exception ex) {
                        cf.completeExceptionally(ex);
                    }

                    @Override
                    public void cancelled() {
                        cf.cancel(true);
                    }
                });
        return cf.thenApply(simpleHttpResponse ->
                new ApacheAsyncHttpResponse(request, simpleHttpResponse));
    }

    private AsyncRequestProducer toApacheRequestProducer(HttpRequest request) throws URISyntaxException {
        final SimpleRequestBuilder apacheRequestBuilder = SimpleRequestBuilder.create(request.getHttpMethod().toString())
                .setUri(request.getUrl().toURI());
        final HttpHeaders headers = request.getHeaders();
        for (HttpHeader httpHeader : headers) {
            apacheRequestBuilder.setHeader(httpHeader.getName(), httpHeader.getValue());
        }
        SimpleHttpRequest simpleHttpRequest = apacheRequestBuilder.build();
        return StreamRequestProducer.create(simpleHttpRequest, request.getStreamBody());
    }

    private CompletableFuture sendV2(HttpRequest request, Context context) {
        Objects.requireNonNull(request.getHttpMethod(), "'request.getHttpMethod()' cannot be null.");
        Objects.requireNonNull(request.getUrl(), "'request.getUrl()' cannot be null.");
        Objects.requireNonNull(request.getUrl().getProtocol(), "'request.getUrl().getProtocol()' cannot be null.");

        AsyncRequestProducer apacheRequestProducer;
        try {
            apacheRequestProducer = toApacheRequestProducer(request);
        } catch (URISyntaxException e) {
            throw logger.logExceptionAsWarning(new IllegalArgumentException("'url' must can convert to a valid URI", e));
        }

        CompletableFuture cf = new CompletableFuture<>();
        final Future future = apacheHttpAsyncClient.execute(
                apacheRequestProducer,
                SimpleResponseConsumer.create(),
                new FutureCallback() {
                    @Override
                    public void completed(SimpleHttpResponse response) {
                        cf.complete(response);
                    }

                    @Override
                    public void failed(final Exception ex) {
                        cf.completeExceptionally(ex);
                    }

                    @Override
                    public void cancelled() {
                        cf.cancel(true);
                    }
                });
        return cf.thenApply(simpleHttpResponse ->
                new ApacheAsyncHttpResponse(request, simpleHttpResponse));
    }

    private CompletableFuture sendV3(HttpRequest request, Context context) {
        Objects.requireNonNull(request.getHttpMethod(), "'request.getHttpMethod()' cannot be null.");
        Objects.requireNonNull(request.getUrl(), "'request.getUrl()' cannot be null.");
        Objects.requireNonNull(request.getUrl().getProtocol(), "'request.getUrl().getProtocol()' cannot be null.");

        AsyncRequestProducer apacheRequestProducer;
        try {
            apacheRequestProducer = toApacheRequestProducer(request);
        } catch (URISyntaxException e) {
            throw logger.logExceptionAsWarning(new IllegalArgumentException("'url' must can convert to a valid URI", e));
        }

        CompletableFuture cf = new CompletableFuture<>();
        final Future future = apacheHttpAsyncClient.execute(
                apacheRequestProducer,
                new ReactiveResponseConsumer((HttpResponseHandler) context.getData(RESPONSE_HANDLER_KEY).get()),
                new FutureCallback() {
                    @Override
                    public void completed(ReactiveApacheHttpResponse response) {
                        cf.complete(response);
                    }

                    @Override
                    public void failed(final Exception ex) {
                        cf.completeExceptionally(ex);
                    }

                    @Override
                    public void cancelled() {
                        cf.cancel(true);
                    }
                });
        return cf.thenApply(reactiveHttpResponse ->
                new ReactiveHttpResponse(request, reactiveHttpResponse));
    }

    private static final class ApacheIndividualRequestBuilder {
        private final ClientLogger logger = new ClientLogger(ApacheIndividualRequestBuilder.class);
        private final HttpRequest request;
        private final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
        private final Timeout connectTimeout;
        private final long connectionKeepAlive;

        public ApacheIndividualRequestBuilder(HttpRequest request, Timeout connectTimeout, long connectionKeepAlive) {
            this.request = request;
            this.connectTimeout = connectTimeout;
            this.connectionKeepAlive = connectionKeepAlive;
        }

        ApacheIndividualRequestBuilder setConnectTimeout() {
            if (request.getConnectTimeout() != null)
                requestConfigBuilder.setConnectTimeout(duration2Timeout(request.getConnectTimeout()));
            else {
                requestConfigBuilder.setConnectTimeout(connectTimeout);
            }
            return this;
        }

        ApacheIndividualRequestBuilder setResponseTimeout() {
            if (request.getResponseTimeout() != null)
                requestConfigBuilder.setResponseTimeout(duration2Timeout(request.getResponseTimeout()));
            return this;
        }

        // if not set, based on setRoutePlanner config
        ApacheIndividualRequestBuilder setProxy() {
            ProxyOptions proxyOptions = request.getProxyOptions();
            if (proxyOptions != null && proxyOptions.getType() == HTTP) {
                if (proxyOptions.getNonProxyHosts() == null || !proxyOptions.getNonProxyHosts().contains(request.getUrl().getHost())) {
                    InetSocketAddress inetSocketAddress = proxyOptions.getAddress();
                    requestConfigBuilder.setProxy(new HttpHost(
                            proxyOptions.getScheme(),
                            inetSocketAddress.getAddress(),
                            inetSocketAddress.getHostString(),
                            inetSocketAddress.getPort()
                    ));
                } else {
                    requestConfigBuilder.setProxy(null);
                }
            }
            return this;
        }

        RequestConfig build() {
            this.setConnectTimeout().setResponseTimeout().setProxy();
            return requestConfigBuilder.setConnectionKeepAlive(TimeValue.of(connectionKeepAlive, TimeUnit.MILLISECONDS)).build();
        }

        private Timeout duration2Timeout(Duration duration) {
            return Timeout.ofMilliseconds(duration.toMillis());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy