com.aliyun.datahub.client.http.HttpClient Maven / Gradle / Ivy
The newest version!
package com.aliyun.datahub.client.http;
import com.aliyun.datahub.client.auth.Account;
import com.aliyun.datahub.client.http.interceptor.Http2RetryInterceptor;
import com.aliyun.datahub.client.http.interceptor.HttpAuthInterceptor;
import com.aliyun.datahub.client.http.interceptor.HttpCompressInterceptor;
import com.aliyun.datahub.client.http.interceptor.HttpNormalInterceptor;
import com.aliyun.datahub.client.http.listener.RequestTraceListener;
import com.aliyun.datahub.client.http.protocol.ProtocolConverterFactory;
import com.aliyun.datahub.client.model.CompressType;
import okhttp3.Authenticator;
import okhttp3.*;
import okhttp3.logging.HttpLoggingInterceptor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import retrofit2.Retrofit;
import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.*;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
public class HttpClient {
private final static Logger LOGGER = LoggerFactory.getLogger(HttpClient.class);
private final static long CLIENT_CACHE_TIMEOUT_MS = 60 * 1000;
private static ConnectionPool connectionPool;
private static int maxIdleConnections = Integer.MAX_VALUE;
private static boolean enableRetrofitCache = true;
private static final TimeoutCache clientMap = new TimeoutCache<>(CLIENT_CACHE_TIMEOUT_MS);
public static void close() {
if (connectionPool != null) {
connectionPool.evictAll();
}
}
public static Retrofit createClient(String endpoint, HttpConfig config, String userAgent, Account account) {
if (!endpoint.startsWith("http")) {
endpoint = "http://" + endpoint;
}
if (!endpoint.endsWith("/")) {
endpoint += "/";
}
ClientConfig clientConfig = new ClientConfig(endpoint, config, userAgent, account);
if (!enableRetrofitCache) {
return newClient(clientConfig);
} else {
Retrofit retrofit = clientMap.get(clientConfig);
if (retrofit == null) {
synchronized (HttpClient.class) {
retrofit = clientMap.get(clientConfig);
if (retrofit == null) {
retrofit = newClient(clientConfig);
clientMap.put(clientConfig, retrofit);
}
}
}
return retrofit;
}
}
private static Retrofit newClient(ClientConfig clientConfig) {
return new Retrofit.Builder()
.client(buildClient(clientConfig))
.addConverterFactory(ProtocolConverterFactory.create())
.baseUrl(clientConfig.endpoint)
.build();
}
private static OkHttpClient buildClient(ClientConfig clientConfig) {
if (connectionPool == null) {
connectionPool = new ConnectionPool(maxIdleConnections, 60, TimeUnit.SECONDS);
}
HttpConfig config = clientConfig.httpConfig;
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectionPool(connectionPool)
.followRedirects(config.isFollowRedirects())
.socketFactory(new NoDelaySocketFactory(config.getNetworkInterface()))
.readTimeout(config.getReadTimeout(), TimeUnit.MILLISECONDS)
.writeTimeout(config.getReadTimeout(), TimeUnit.MILLISECONDS)
.connectTimeout(config.getConnTimeout(), TimeUnit.MILLISECONDS)
.sslSocketFactory(sslSocketFactory(), x509TrustManager())
.hostnameVerifier((s, sslSession) -> true)
.retryOnConnectionFailure(true);
if (!StringUtils.isEmpty(config.getProxyUri())) {
String proxyUri = config.getProxyUri();
int index = proxyUri.lastIndexOf(":");
String proxyHost = proxyUri.substring(0, index);
String proxyPortStr = proxyUri.substring(index + 1);
int proxyPort = StringUtils.isEmpty(proxyPortStr) ? 80 : Integer.valueOf(proxyPortStr);
builder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort)))
.proxyAuthenticator(new Authenticator() {
@Override
public Request authenticate(Route route, Response response) throws IOException {
String credential = Credentials.basic(config.getProxyUsername(), proxyHost);
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
}
});
}
if (config.isEnableH2C()) {
builder.protocols(Collections.singletonList(Protocol.H2_PRIOR_KNOWLEDGE));
}
if (config.isTraceRequest()) {
builder.eventListener(new RequestTraceListener());
}
// HttpNormalInterceptor <--> HttpCompressInterceptor <--> HttpAuthInterceptor <--> Http2RetryInterceptor
builder.addInterceptor(new HttpNormalInterceptor(clientConfig.userAgent, clientConfig.httpConfig.getUserHeader()));
// add interceptors
if (config.getCompressType() != null && config.getCompressType() != CompressType.NONE) {
builder.addInterceptor(new HttpCompressInterceptor(config.getCompressType()));
}
builder.addInterceptor(new HttpAuthInterceptor(clientConfig.account));
if (config.isDebugRequest()) {
builder.addInterceptor(loggingInterceptor());
}
if (config.isEnableH2C()) { // add at last
builder.addInterceptor(new Http2RetryInterceptor());
}
return builder.build();
}
private static HttpLoggingInterceptor loggingInterceptor() {
HttpLoggingInterceptor.Logger customLogger = new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
LOGGER.info("DataHubClient: " + message);
}
};
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(customLogger);
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return loggingInterceptor;
}
public static X509TrustManager x509TrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
};
}
public static SSLSocketFactory sslSocketFactory() {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{x509TrustManager()}, new SecureRandom());
return sslContext.getSocketFactory();
} catch (GeneralSecurityException ex) {
LOGGER.warn(ex.toString());
throw new RuntimeException(ex.getMessage(), ex);
}
}
public static int getMaxIdleConnections() {
return maxIdleConnections;
}
public static void setMaxIdleConnections(int maxIdleConnections) {
HttpClient.maxIdleConnections = maxIdleConnections;
}
public static boolean isEnableRetrofitCache() {
return enableRetrofitCache;
}
public static void setEnableRetrofitCache(boolean enableRetrofitCache) {
HttpClient.enableRetrofitCache = enableRetrofitCache;
}
private static class ClientConfig {
private final String endpoint;
private final HttpConfig httpConfig;
private final String userAgent;
private final Account account;
public ClientConfig(String endpoint, HttpConfig httpConfig, String userAgent, Account account) {
this.endpoint = endpoint;
this.httpConfig = httpConfig;
this.userAgent = userAgent;
this.account = account;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ClientConfig that = (ClientConfig) o;
return Objects.equals(endpoint, that.endpoint) && Objects.equals(httpConfig, that.httpConfig) && Objects.equals(userAgent, that.userAgent) && Objects.equals(account, that.account);
}
@Override
public int hashCode() {
return Objects.hash(endpoint, httpConfig, userAgent, account);
}
}
public static class NoDelaySocketFactory extends SocketFactory {
private final String networkInterface;
public NoDelaySocketFactory(String networkInterface) {
this.networkInterface = networkInterface;
}
@Override
public Socket createSocket() throws SocketException {
Socket ret = new Socket();
ret.setTcpNoDelay(true);
if (!StringUtils.isEmpty(networkInterface)) {
try {
NetworkInterface ni = NetworkInterface.getByName(networkInterface);
if (ni == null) {
throw new SocketException("Not find interface " + networkInterface);
}
Enumeration enumeration = ni.getInetAddresses();
if (!enumeration.hasMoreElements()) {
throw new SocketException("Not find address for " + networkInterface);
}
ret.bind(new InetSocketAddress(enumeration.nextElement(), 0));
} catch (Exception e) {
throw new SocketException(e.getMessage());
}
}
return ret;
}
public Socket createSocket(String host, int port)
throws IOException {
Socket socket = createSocket();
try {
socket.connect(new InetSocketAddress(host, port));
} catch (IOException e) {
socket.close();
throw e;
}
return socket;
}
public Socket createSocket(InetAddress address, int port)
throws IOException {
Socket socket = createSocket();
try {
socket.connect(new InetSocketAddress(address, port));
} catch (IOException e) {
socket.close();
throw e;
}
return socket;
}
public Socket createSocket(String host, int port,
InetAddress clientAddress, int clientPort)
throws IOException {
Socket socket = createSocket();
try {
socket.bind(new InetSocketAddress(clientAddress, clientPort));
socket.connect(new InetSocketAddress(host, port));
} catch (IOException e) {
socket.close();
throw e;
}
return socket;
}
public Socket createSocket(InetAddress address, int port,
InetAddress clientAddress, int clientPort)
throws IOException {
Socket socket = createSocket();
try {
socket.bind(new InetSocketAddress(clientAddress, clientPort));
socket.connect(new InetSocketAddress(address, port));
} catch (IOException e) {
socket.close();
throw e;
}
return socket;
}
}
}