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

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

The newest version!
package com.aliyun.httpcomponent.httpclient;

import com.aliyun.apache.hc.client5.http.async.methods.*;
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.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();
        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.");
        if (context.getData(RESPONSE_HANDLER_KEY).isPresent()
                || request.getStreamBody() != null) {
            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:
                if (request.getBody() != null) {
                    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());
                        }
                    }
                    apacheRequestBuilder.setBody(BinaryUtils.copyAllBytesFrom(request.getBody()), type);
                }
                return apacheRequestBuilder.build();
        }
    }

    private AsyncRequestProducer toApacheRequestProducer(HttpRequest request) throws URISyntaxException, ExecutionException, InterruptedException {
        SimpleHttpRequest simpleHttpRequest = toApacheAsyncRequest(request);
        simpleHttpRequest.setConfig(new ApacheIndividualRequestBuilder(request, connectTimeout, connectionKeepAlive).build());
        if (request.getStreamBody() != null) {
            return StreamRequestProducer.create(simpleHttpRequest, request.getStreamBody());
        } else {
            return SimpleRequestProducer.create(simpleHttpRequest);
        }
    }

    private CompletableFuture sendV1(HttpRequest request, Context context) {
        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));
        }

        apacheRequest.setConfig(new ApacheIndividualRequestBuilder(request, connectTimeout, connectionKeepAlive).build());

        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 CompletableFuture sendV2(HttpRequest request, Context context) {
        AsyncRequestProducer apacheRequestProducer;
        try {
            apacheRequestProducer = toApacheRequestProducer(request);
        } catch (URISyntaxException | ExecutionException | InterruptedException e) {
            throw logger.logExceptionAsWarning(new IllegalArgumentException("'url' must can convert to a valid URI", e));
        }

        if (context.getData(RESPONSE_HANDLER_KEY).isPresent()) {
            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));
        } else {
            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 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