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

com.aliyuncs.v5.http.clients.ApacheHttpClient Maven / Gradle / Ivy

package com.aliyuncs.v5.http.clients;

import com.aliyuncs.v5.exceptions.ClientException;
import com.aliyuncs.v5.http.*;
import com.aliyuncs.v5.utils.EnvironmentUtils;
import com.aliyuncs.v5.utils.IOUtils;
import com.aliyuncs.v5.utils.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.*;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class ApacheHttpClient extends IHttpClient {

    protected static final String CONTENT_TYPE = "Content-Type";
    protected static final String ACCEPT_ENCODING = "Accept-Encoding";

    private static final String EXT_PARAM_KEY_BUILDER = "apache.httpclient.builder";
    private static final int DEFAULT_THREAD_KEEP_ALIVE_TIME = 60;

    private ExecutorService executorService;
    private CloseableHttpClient httpClient;
    private PoolingHttpClientConnectionManager connectionManager;
    private AtomicBoolean initialized = new AtomicBoolean(false);
    private CountDownLatch latch = new CountDownLatch(1);

    private static volatile ApacheHttpClient client;

    /**
     * use ApacheHttpClient.getInstance() instead
     */
    @Deprecated
    public static ApacheHttpClient getInstance(HttpClientConfig config) throws ClientException {
        throw new IllegalStateException("use ApacheHttpClient.getInstance() instead");
    }

    public static ApacheHttpClient getInstance() {
        if (client == null) {
            synchronized (ApacheHttpClient.class) {
                if (client == null) {
                    client = new ApacheHttpClient();
                }
            }
        }
        return client;
    }

    private ApacheHttpClient() {
        super();
    }

    private SSLConnectionSocketFactory createSSLConnectionSocketFactory() throws ClientException {
        try {
            List trustManagerList = new ArrayList();
            X509TrustManager[] trustManagers = clientConfig.getX509TrustManagers();

            if (null != trustManagers) {
                trustManagerList.addAll(Arrays.asList(trustManagers));
            }

            // get trustManager using default certification from jdk
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init((KeyStore) null);
            trustManagerList.addAll(Arrays.asList(tmf.getTrustManagers()));

            final List finalTrustManagerList = new ArrayList();
            for (TrustManager tm : trustManagerList) {
                if (tm instanceof X509TrustManager) {
                    finalTrustManagerList.add((X509TrustManager) tm);
                }
            }
            CompositeX509TrustManager compositeX509TrustManager = new CompositeX509TrustManager(finalTrustManagerList);
            compositeX509TrustManager.setIgnoreSSLCert(clientConfig.isIgnoreSSLCerts());
            KeyManager[] keyManagers = null;
            if (clientConfig.getKeyManagers() != null) {
                keyManagers = clientConfig.getKeyManagers();
            }

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(keyManagers, new TrustManager[]{compositeX509TrustManager}, clientConfig.getSecureRandom());

            HostnameVerifier hostnameVerifier = null;
            if (clientConfig.isIgnoreSSLCerts()) {
                hostnameVerifier = new NoopHostnameVerifier();
            } else if (clientConfig.getHostnameVerifier() != null) {
                hostnameVerifier = clientConfig.getHostnameVerifier();
            } else {
                hostnameVerifier = new DefaultHostnameVerifier();
            }
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
            return sslConnectionSocketFactory;
        } catch (Exception e) {
            throw new ClientException("SDK.InitFailed", "Init https with SSL socket failed", e);
        }

    }

    private void initConnectionManager() throws ClientException {
        // http
        RegistryBuilder socketFactoryRegistryBuilder = RegistryBuilder.create();
        socketFactoryRegistryBuilder.register("http", new PlainConnectionSocketFactory());

        // https
        SSLConnectionSocketFactory sslConnectionSocketFactory = createSSLConnectionSocketFactory();
        socketFactoryRegistryBuilder.register("https", sslConnectionSocketFactory);

        // connPool
        connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistryBuilder.build());
        connectionManager.setMaxTotal(clientConfig.getMaxRequests());
        connectionManager.setDefaultMaxPerRoute(clientConfig.getMaxRequestsPerHost());
    }

    private HttpClientBuilder initHttpClientBuilder() {
        HttpClientBuilder builder;
        if (clientConfig.containsExtParam(EXT_PARAM_KEY_BUILDER)) {
            builder = (HttpClientBuilder) clientConfig.getExtParam(EXT_PARAM_KEY_BUILDER);
        } else {
            builder = HttpClientBuilder.create();
        }
        return builder;
    }

    private void initExecutor() {
        // async
        if (clientConfig.getExecutorService() == null) {
            executorService = new ThreadPoolExecutor(0, clientConfig.getMaxRequests(), DEFAULT_THREAD_KEEP_ALIVE_TIME,
                    TimeUnit.SECONDS, new SynchronousQueue(), new DefaultAsyncThreadFactory());
        } else {
            executorService = clientConfig.getExecutorService();
        }
    }

    @Override
    protected void init(final HttpClientConfig config0) throws ClientException {
        if (!initialized.compareAndSet(false, true)) {
            try {
                latch.await();
            } catch (InterruptedException e) {
                throw new ClientException("SDK.InitFailed", "Init apacheHttpClient failed", e);
            }
            return;
        }
        final HttpClientConfig config = (config0 != null ? config0 : HttpClientConfig.getDefault());
        this.clientConfig = config;

        HttpClientBuilder builder = initHttpClientBuilder();

        // default request config
        RequestConfig defaultConfig = RequestConfig.custom().setConnectTimeout((int) config
                .getConnectionTimeoutMillis()).setSocketTimeout((int) config.getReadTimeoutMillis())
                .setConnectionRequestTimeout((int) config.getWriteTimeoutMillis()).build();
        builder.setDefaultRequestConfig(defaultConfig);

        initConnectionManager();
        builder.setConnectionManager(connectionManager);
        ApacheIdleConnectionCleaner.registerConnectionManager(connectionManager, config.getMaxIdleTimeMillis());

        initExecutor();
        builder.setRetryHandler(new DefaultHttpRequestRetryHandler(0, false));
        // keepAlive
        if (config.getKeepAliveDurationMillis() > 0) {
            builder.setKeepAliveStrategy(new ConnectionKeepAliveStrategy() {
                @Override
                public long getKeepAliveDuration(org.apache.http.HttpResponse response, HttpContext context) {
                    long duration = DefaultConnectionKeepAliveStrategy.INSTANCE.getKeepAliveDuration(response, context);

                    if (duration > 0 && duration < config.getKeepAliveDurationMillis()) {
                        return duration;
                    } else {
                        return config.getKeepAliveDurationMillis();
                    }
                }
            });
        }

        httpClient = builder.build();
        latch.countDown();
    }

    private HttpUriRequest parseToHttpRequest(HttpRequest apiReq) throws IOException, ClientException {
        RequestBuilder builder = RequestBuilder.create(apiReq.getSysMethod().name());

        builder.setUri(apiReq.getSysUrl());

        if (apiReq.getSysMethod().hasContent() && apiReq.getHttpContent() != null && apiReq.getHttpContent().length > 0) {
            EntityBuilder bodyBuilder = EntityBuilder.create();

            String contentType = apiReq.getHeaderValue(CONTENT_TYPE);
            if (StringUtils.isEmpty(contentType)) {
                contentType = apiReq.getContentTypeValue(apiReq.getHttpContentType(), apiReq.getSysEncoding());
            }
            bodyBuilder.setContentType(ContentType.parse(contentType));
            bodyBuilder.setBinary(apiReq.getHttpContent());
            builder.setEntity(bodyBuilder.build());
        }

        builder.addHeader(ACCEPT_ENCODING, "identity");

        // calcProxy will modify the "Proxy-Authorization" header of the request
        HttpHost proxy = calcProxy(apiReq);

        for (Map.Entry entry : apiReq.getSysHeaders().entrySet()) {
            if ("Content-Length".equalsIgnoreCase(entry.getKey())) {
                continue;
            }
            builder.addHeader(entry.getKey(), entry.getValue());
        }
        int connectTimeout;
        int readTimeout;
        if (null != apiReq.getSysConnectTimeout()) {
            connectTimeout = apiReq.getSysConnectTimeout();
        } else {
            connectTimeout = (int) clientConfig.getConnectionTimeoutMillis();
        }
        if (null != apiReq.getSysReadTimeout()) {
            readTimeout = apiReq.getSysReadTimeout();
        } else {
            readTimeout = (int) clientConfig.getReadTimeoutMillis();
        }
        RequestConfig requestConfig = RequestConfig.custom().setProxy(proxy).setConnectTimeout(connectTimeout).setSocketTimeout(
                readTimeout).setConnectionRequestTimeout((int) clientConfig.getWriteTimeoutMillis()).build();
        builder.setConfig(requestConfig);
        return builder.build();
    }

    private HttpHost calcProxy(HttpRequest apiReq) throws MalformedURLException, ClientException {
        boolean needProxy = HttpUtil.needProxy(new URL(apiReq.getSysUrl()).getHost(), clientConfig.getNoProxy(), EnvironmentUtils.getNoProxy());
        if (!needProxy) {
            return null;
        }
        URL url = new URL(apiReq.getSysUrl());
        HttpHost proxy = null;
        if ("https".equalsIgnoreCase(url.getProtocol())) {
            proxy = HttpUtil.getApacheProxy(clientConfig.getHttpsProxy(), EnvironmentUtils.getHttpsProxy(), apiReq);
        } else {
            proxy = HttpUtil.getApacheProxy(clientConfig.getHttpProxy(), EnvironmentUtils.getHttpProxy(), apiReq);
        }
        return proxy;
    }

    private HttpResponse parseToHttpResponse(org.apache.http.HttpResponse httpResponse) throws IOException {
        HttpResponse result = new HttpResponse();
        // status code
        result.setStatus(httpResponse.getStatusLine().getStatusCode());
        result.setReasonPhrase(httpResponse.getStatusLine().getReasonPhrase());
        boolean existed = ((httpResponse.getEntity() != null && (httpResponse.getEntity().getContentLength() > 0 || httpResponse
                .getEntity().isChunked())));
        if (existed) {
            // content type
            Header contentTypeHeader = httpResponse.getEntity().getContentType();
            ContentType contentType = ContentType.parse(contentTypeHeader.getValue());
            FormatType formatType = FormatType.mapAcceptToFormat(contentType.getMimeType());
            result.setHttpContentType(formatType);

            String charset = "utf-8";
            if (contentType.getCharset() != null) {
                charset = contentType.getCharset().toString();
            }

            // body
            result.setHttpContent(EntityUtils.toByteArray(httpResponse.getEntity()), charset, formatType);
        }

        // headers
        for (Header header : httpResponse.getAllHeaders()) {
            result.putHeaderParameter(header.getName(), header.getValue());
        }

        return result;
    }

    @Override
    public final HttpResponse syncInvoke(HttpRequest apiRequest) throws IOException, ClientException {
        HttpUriRequest httpRequest = parseToHttpRequest(apiRequest);
        CloseableHttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpRequest);
            return parseToHttpResponse(httpResponse);
        } finally {
            IOUtils.closeQuietly(httpResponse);
        }
    }

    @Override
    public final Future asyncInvoke(final HttpRequest apiRequest,
                                                  final CallBack callback) {
        return executorService.submit(new Callable() {
            @Override
            public HttpResponse call() throws Exception {
                HttpResponse result;
                try {
                    result = syncInvoke(apiRequest);
                } catch (Exception e) {
                    if (callback != null) {
                        callback.onFailure(apiRequest, e);
                    }
                    throw e;
                }

                if (callback != null) {
                    callback.onResponse(apiRequest, result);
                }
                return result;
            }
        });
    }

    /**
     * use HttpClientConfig.setIgnoreSSLCerts(true) instead
     */
    @Override
    public void ignoreSSLCertificate() {
        throw new IllegalStateException("Apache httpclient does not support modify sslFactory after inited, "
                + "use HttpClientConfig.setIgnoreSSLCerts(true) while building client");
    }

    /**
     * use HttpClientConfig.setIgnoreSSLCerts(false) instead
     */
    @Override
    public void restoreSSLCertificate() {
        throw new IllegalStateException("Apache httpclient does not support modify sslFactory after inited, "
                + "use HttpClientConfig.setIgnoreSSLCerts(true) while building client");
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void close() throws IOException {
        client = null;
        if (initialized.compareAndSet(true, false)) {
            executorService.shutdown();
            ApacheIdleConnectionCleaner.removeConnectionManager(connectionManager);
            connectionManager.shutdown();
            IOUtils.closeQuietly(httpClient);
        }
    }

    private class DefaultAsyncThreadFactory implements ThreadFactory {

        private AtomicInteger counter = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable runnable) {
            return new Thread(runnable, "Aliyun_SDK_Async_ThreadPool_" + counter.incrementAndGet());
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy