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

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

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

import com.aliyun.core.http.HttpClient;
import com.aliyun.core.http.ProxyOptions;
import com.aliyun.core.logging.ClientLogger;
import com.aliyun.core.utils.Configuration;
import com.aliyun.httpcomponent.httpclient.implementation.CompositeX509TrustManager;
import com.aliyun.httpcomponent.httpclient.implementation.SdkConnectionKeepAliveStrategy;
import com.aliyun.apache.hc.client5.http.ConnectionKeepAliveStrategy;
import com.aliyun.apache.hc.client5.http.auth.AuthScope;
import com.aliyun.apache.hc.client5.http.auth.CredentialsProvider;
import com.aliyun.apache.hc.client5.http.config.RequestConfig;
import com.aliyun.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
import com.aliyun.apache.hc.client5.http.impl.async.HttpAsyncClients;
import com.aliyun.apache.hc.client5.http.impl.auth.CredentialsProviderBuilder;
import com.aliyun.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import com.aliyun.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import com.aliyun.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
import com.aliyun.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
import com.aliyun.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import com.aliyun.apache.hc.core5.http.HttpHost;
import com.aliyun.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
import com.aliyun.apache.hc.core5.http.nio.ssl.TlsStrategy;
import com.aliyun.apache.hc.core5.reactor.IOReactorConfig;
import com.aliyun.apache.hc.core5.util.Timeout;

import javax.net.ssl.*;
import java.net.*;
import java.security.KeyStore;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class ApacheAsyncHttpClientBuilder {
    private final ClientLogger logger = new ClientLogger(ApacheAsyncHttpClientBuilder.class);
    private HttpAsyncClientBuilder httpAsyncClientBuilder;
    private Duration connectionTimeout;
    private Duration responseTimeout;
    private Duration maxIdleTimeOut;
    private Duration keepAlive;
    private int maxConnections = 128;
    private int maxConnectionsPerRoute = 128;
    private Duration connectRequestTimeout;
    private ProxyOptions proxyOptions = null;
    private Configuration configuration;
    private static final long DEFAULT_TIMEOUT = TimeUnit.SECONDS.toMillis(20);
    private static final long DEFAULT_KEEP_ALIVE = TimeUnit.SECONDS.toMillis(20);
    private static final long DEFAULT_MAX_CONN_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
    private static final long DEFAULT_MAX_RESPONSE_TIMEOUT = TimeUnit.SECONDS.toMillis(20);
    private static final long DEFAULT_MINIMUM_TIMEOUT = TimeUnit.MILLISECONDS.toMillis(1);
    private static final long DEFAULT_CONNECT_REQUEST_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
    private static final long DEFAULT_MAX_IDLE_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
    private X509TrustManager[] x509TrustManagers = null;
    private KeyManager[] keyManagers = null;
    private HostnameVerifier hostnameVerifier = null;
    private boolean ignoreSSL = false;

    public ApacheAsyncHttpClientBuilder() {
        this.httpAsyncClientBuilder = HttpAsyncClients.custom();
    }

    public ApacheAsyncHttpClientBuilder(HttpAsyncClientBuilder httpAsyncClientBuilder) {
        this.httpAsyncClientBuilder = Objects.requireNonNull(httpAsyncClientBuilder, "'httpAsyncClientBuilder' cannot be null.");
    }

    public ApacheAsyncHttpClientBuilder connectionTimeout(Duration connectionTimeout) {
        // setConnectionTimeout can be null
        this.connectionTimeout = connectionTimeout;
        return this;
    }

    public ApacheAsyncHttpClientBuilder responseTimeout(Duration responseTimeout) {
        this.responseTimeout = responseTimeout;
        return this;
    }

    public ApacheAsyncHttpClientBuilder maxIdleTimeOut(Duration maxIdleTimeOut) {
        this.maxIdleTimeOut = maxIdleTimeOut;
        return this;
    }

    public ApacheAsyncHttpClientBuilder keepAlive(Duration keepAlive) {
        this.keepAlive = keepAlive;
        return this;
    }

    public ApacheAsyncHttpClientBuilder maxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
        return this;
    }

    public ApacheAsyncHttpClientBuilder maxConnectionsPerRoute(int maxConnectionsPerRoute) {
        this.maxConnectionsPerRoute = maxConnectionsPerRoute;
        return this;
    }

    public ApacheAsyncHttpClientBuilder connectRequestTimeout(Duration connectRequestTimeout) {
        this.connectRequestTimeout = connectRequestTimeout;
        return this;
    }

    public ApacheAsyncHttpClientBuilder proxy(ProxyOptions proxyOptions) {
        this.proxyOptions = proxyOptions;
        return this;
    }

    public ApacheAsyncHttpClientBuilder configuration(Configuration configuration) {
        this.configuration = configuration;
        return this;
    }

    public ApacheAsyncHttpClientBuilder x509TrustManagers(X509TrustManager[] x509TrustManagers) {
        this.x509TrustManagers = x509TrustManagers;
        return this;
    }

    public ApacheAsyncHttpClientBuilder keyManagers(KeyManager[] keyManagers) {
        this.keyManagers = keyManagers;
        return this;
    }

    public ApacheAsyncHttpClientBuilder hostnameVerifier(HostnameVerifier hostnameVerifier) {
        this.hostnameVerifier = hostnameVerifier;
        return this;
    }

    public ApacheAsyncHttpClientBuilder ignoreSSL(boolean ignoreSSL) {
        this.ignoreSSL = ignoreSSL;
        return this;
    }

    public HttpClient build() {
        // requestConfig
        httpAsyncClientBuilder.setDefaultRequestConfig(defaultRequestConfig());

        // connectionManager config
        PoolingAsyncClientConnectionManager cm = connectionPoolConfig();
        httpAsyncClientBuilder.setConnectionManager(cm);

        // proxy
        Configuration buildConfiguration = (configuration == null)
                ? Configuration.getGlobalConfiguration()
                : configuration;
        if (proxyOptions == null && buildConfiguration != Configuration.NONE)
            proxyOptions = ProxyOptions.fromConfiguration(buildConfiguration);
        if (proxyOptions != null) {
            InetSocketAddress inetSocketAddress = proxyOptions.getAddress();
            ProxyOptions.Type type = proxyOptions.getType();
            switch (type) {
                case SOCKS4:
                case SOCKS5:
                    IOReactorConfig.Builder config = IOReactorConfig.custom()
                            .setSocksProxyAddress(inetSocketAddress);
                    if (proxyOptions.getUsername() != null) {
                        config.setSocksProxyUsername(proxyOptions.getUsername())
                                .setSocksProxyPassword(proxyOptions.getPassword());
                    }
                    httpAsyncClientBuilder.setIOReactorConfig(config.build());
                    break;
                default:
                    httpAsyncClientBuilder.setProxy(new HttpHost(
                            proxyOptions.getScheme(),
                            inetSocketAddress.getAddress(),
                            inetSocketAddress.getHostString(),
                            inetSocketAddress.getPort()
                    ));
            }
            if (proxyOptions.getUsername() != null) {
                CredentialsProvider credentialsProvider = CredentialsProviderBuilder.create()
                        .add(new AuthScope(proxyOptions.getAddress().getHostString(),
                                        proxyOptions.getAddress().getPort()),
                                proxyOptions.getUsername(),
                                proxyOptions.getPassword().toCharArray())
                        .build();
                httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            }

//            httpAsyncClientBuilder.setRoutePlanner(new SdkProxyRoutePlanner(
//                    proxyOptions.getScheme(),
//                    proxyOptions.getAddress(),
//                    proxyOptions.getNonProxyHosts()));

        }

        // httpAsyncClientBuilder
        httpAsyncClientBuilder
                // Connection reuse policy, i.e. whether it can keepAlive
                .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
                // Set how long a connection can remain idle before it is reused, maximum idle time
                .setKeepAliveStrategy(buildKeepAliveStrategy())
                // Set the connections in the connection pool to clear the maximum idle time using a background thread
                .evictIdleConnections(getTimeoutMillis(this.maxIdleTimeOut, "idle"))
                // Set up to use background threads to clear expired connections
                .evictExpiredConnections()
                // Disable redirects
                .disableRedirectHandling()
                // Disable reconnect policy
                .disableAutomaticRetries()
                // SDK will set the user agent header in the pipeline. Don't let Apache waste time
                .setUserAgent("");
        return new ApacheAsyncHttpClient(httpAsyncClientBuilder.build(),
                getTimeoutMillis(this.connectionTimeout, "connect"),
                this.keepAlive == null ? DEFAULT_KEEP_ALIVE : this.keepAlive.toMillis());
    }

    private PoolingAsyncClientConnectionManager connectionPoolConfig() {
        // set TrustManager
        PoolingAsyncClientConnectionManagerBuilder cmb = PoolingAsyncClientConnectionManagerBuilder.create();
        List trustManagerList = new ArrayList();
        X509TrustManager[] trustManagers = this.x509TrustManagers;
        if (null != trustManagers) {
            trustManagerList.addAll(Arrays.asList(trustManagers));
        }
        TrustManagerFactory tmf = null;
        try {
            // get trustManager using default certification from jdk
            tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init((KeyStore) null);
            trustManagerList.addAll(Arrays.asList(tmf.getTrustManagers()));
        } catch (Exception e) {
            throw logger.logExceptionAsError(new RuntimeException(e));
        }
        final List finalTrustManagerList = new ArrayList();
        for (TrustManager tm : trustManagerList) {
            if (tm instanceof X509TrustManager) {
                finalTrustManagerList.add((X509TrustManager) tm);
            }
        }
        CompositeX509TrustManager compositeX509TrustManager = new CompositeX509TrustManager(finalTrustManagerList);
        compositeX509TrustManager.setIgnoreSSLCert(this.ignoreSSL);

        // set KeyManager
        KeyManager[] keyManagers = null;
        if (this.keyManagers != null) {
            keyManagers = this.keyManagers;
        }

        // Generate SSLContext by TrustManager and KeyManager
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(keyManagers, new TrustManager[]{compositeX509TrustManager}, null);
        } catch (Exception e) {
            throw logger.logExceptionAsError(new RuntimeException(e));
        }

        // set HostnameVerifier
        HostnameVerifier hostnameVerifier = null;
        if (this.ignoreSSL) {
            hostnameVerifier = new NoopHostnameVerifier();
        } else if (this.hostnameVerifier != null) {
            hostnameVerifier = this.hostnameVerifier;
        } else {
            hostnameVerifier = new DefaultHostnameVerifier();
        }

        // set connection pool TlsStrategy
        TlsStrategy tlsStrategy = ClientTlsStrategyBuilder
                .create()
                .setSslContext(sslContext)
                .setHostnameVerifier(hostnameVerifier)
                .build();
        cmb.setTlsStrategy(tlsStrategy);
        // Set the number of connection pool connections and generate a connection manager
        PoolingAsyncClientConnectionManager cm = cmb
                // Specify the maximum total connection value
                .setMaxConnTotal(getMaxConnTotal(this.maxConnections))
                // Specify the maximum connection value for each route
                .setMaxConnPerRoute(getMaxConnTotal(this.maxConnectionsPerRoute))
                .build();
        return cm;
    }

    private RequestConfig defaultRequestConfig() {
        RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
                .setConnectionRequestTimeout(getTimeoutMillis(this.connectRequestTimeout, "connectionRequest"))
                .setConnectTimeout(getTimeoutMillis(this.connectionTimeout, "connect"))
                .setResponseTimeout(getTimeoutMillis(this.responseTimeout, "response"))
                .setDefaultKeepAlive(this.keepAlive == null ? DEFAULT_KEEP_ALIVE : this.keepAlive.toMillis(), TimeUnit.MILLISECONDS);
        return requestConfigBuilder.setRedirectsEnabled(false).build();
    }

    private static Timeout getTimeoutMillis(Duration timeout, String name) {
        if (timeout == null) {
            switch (name) {
                case "idle":
                    return Timeout.ofMilliseconds(DEFAULT_MAX_IDLE_TIMEOUT);
                case "connect":
                    return Timeout.ofMilliseconds(DEFAULT_MAX_CONN_TIMEOUT);
                case "response":
                    return Timeout.ofMilliseconds(DEFAULT_MAX_RESPONSE_TIMEOUT);
                case "connectionRequest":
                    return Timeout.ofMilliseconds(DEFAULT_CONNECT_REQUEST_TIMEOUT);
                default:
                    return Timeout.ofMilliseconds(DEFAULT_TIMEOUT);
            }
        }

        if (timeout.isZero() || timeout.isNegative()) {
            return Timeout.ofMilliseconds(0);
        }

        return Timeout.ofMilliseconds(Math.max(timeout.toMillis(), DEFAULT_MINIMUM_TIMEOUT));
    }

    private static int getMaxConnTotal(int maxConnTotal) {
        if (maxConnTotal <= 0) {
            return Runtime.getRuntime().availableProcessors() * 10;
        }
        return maxConnTotal;
    }

    private ConnectionKeepAliveStrategy buildKeepAliveStrategy() {
        long maxIdle = getTimeoutMillis(this.maxIdleTimeOut, "idle").toMilliseconds();
        return maxIdle > 0 ? new SdkConnectionKeepAliveStrategy(maxIdle) : null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy