package org.to2mbn.jmccc.mcdownloader.download.io.async; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.SocketAddress; import java.util.Arrays; import java.util.concurrent.ExecutorService; import org.apache.http.HttpHost; import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; import org.apache.http.impl.nio.reactor.IOReactorConfig; import org.apache.http.message.BasicHeader; import org.to2mbn.jmccc.mcdownloader.download.Downloader; import org.to2mbn.jmccc.mcdownloader.download.io.AbstractDownloaderBuilder; import org.to2mbn.jmccc.mcdownloader.util.ThreadPoolUtils; import org.to2mbn.jmccc.util.Builder; public class HttpAsyncDownloaderBuilder extends AbstractDownloaderBuilder { public static boolean isAvailable() { try { Class.forName("org.apache.http.impl.nio.client.HttpAsyncClientBuilder"); } catch (ClassNotFoundException e) { return false; } return true; } private static class HttpAsyncClientBuilderAdapter implements Builder<CloseableHttpAsyncClient> { private HttpAsyncClientBuilder adapted; public HttpAsyncClientBuilderAdapter(HttpAsyncClientBuilder adapted) { this.adapted = adapted; } @Override public CloseableHttpAsyncClient build() { return adapted.build(); } } public static HttpAsyncDownloaderBuilder create() { return new HttpAsyncDownloaderBuilder(); } public static Downloader buildDefault() { return create().build(); } protected Builder<CloseableHttpAsyncClient> httpClient; protected int bootstrapPoolSize = Runtime.getRuntime().availableProcessors(); public HttpAsyncDownloaderBuilder httpClient(Builder<CloseableHttpAsyncClient> httpClient) { this.httpClient = httpClient; return this; } public HttpAsyncDownloaderBuilder httpClient(HttpAsyncClientBuilder httpClient) { this.httpClient = httpClient == null ? null : new HttpAsyncClientBuilderAdapter(httpClient); return this; } public HttpAsyncDownloaderBuilder bootstrapPoolSize(int bootstrapPoolSize) { this.bootstrapPoolSize = bootstrapPoolSize; return this; } @Override public Downloader build() { CloseableHttpAsyncClient client = null; ExecutorService pool = null; try { if (httpClient == null) { client = buildDefaultHttpAsyncClient(); } else { client = httpClient.build(); } pool = ThreadPoolUtils.createPool(bootstrapPoolSize, downloadPoolKeepAliveTime, downloadPoolKeepAliveTimeUnit, "asyncDownloader.bootstrap"); return new HttpAsyncDownloader(client, pool); } catch (Throwable e) { if (client != null) { try { client.close(); } catch (Throwable e1) { e.addSuppressed(e1); } } if (pool != null) { try { pool.shutdownNow(); } catch (Throwable e1) { e.addSuppressed(e1); } } throw e; } } protected CloseableHttpAsyncClient buildDefaultHttpAsyncClient() { HttpHost httpProxy = resolveProxy(proxy); return HttpAsyncClientBuilder.create() .setMaxConnTotal(maxConnections) .setMaxConnPerRoute(maxConnections) .setProxy(httpProxy) .setDefaultIOReactorConfig(IOReactorConfig.custom() .setConnectTimeout(connectTimeout) .setSoTimeout(readTimeout) .build()) .setDefaultRequestConfig(RequestConfig.custom() .setConnectTimeout(connectTimeout) .setSocketTimeout(readTimeout) .setProxy(httpProxy) .build()) .setDefaultHeaders(Arrays.asList(new BasicHeader("Accept-Encoding", "gzip"))) .build(); } private static HttpHost resolveProxy(Proxy proxy) { if (proxy.type() == Proxy.Type.DIRECT) { return null; } if (proxy.type() == Proxy.Type.HTTP) { SocketAddress socketAddress = proxy.address(); if (socketAddress instanceof InetSocketAddress) { InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; return new HttpHost(inetSocketAddress.getAddress(), inetSocketAddress.getPort()); } } throw new IllegalArgumentException("Proxy '" + proxy + "' is not supported"); } }