package com.vaguehope.onosendai.util;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import javax.net.ssl.TrustManagerFactory;
import org.apache.http.client.HttpClient;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SocketFactory;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
/*
* https://hc.apache.org/httpcomponents-client-ga/tutorial/html/index.html
* https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/conn/tsccm/ThreadSafeClientConnManager.html
*/
public class HttpClientFactory {
private static final int CONNECTION_TIMEOUT = 20000;
private static final int SO_TIMEOUT = 30000;
private static final int SO_BUFFER_SIZE = 8192;
private final String tsPath;
private final char[] tsPassword;
private final Object[] clientLock = new Object[0];
private volatile HttpClient httpClient;
public HttpClientFactory () {
this(null, null);
}
public HttpClientFactory (final String tsPath, final String tsPassword) {
this.tsPath = tsPath;
this.tsPassword = tsPassword != null ? tsPassword.toCharArray() : null;
}
public HttpClient getHttpClient () throws IOException {
synchronized (this.clientLock) {
try {
if (this.httpClient == null) this.httpClient = makeHttpClient();
return this.httpClient;
}
catch (final GeneralSecurityException e) {
throw new IOException("Failed to create HTTP client. " + e.toString(), e);
}
}
}
public void shutdown () {
synchronized (this.clientLock) {
if (this.httpClient != null) this.httpClient.getConnectionManager().shutdown();
}
}
private HttpClient makeHttpClient () throws IOException, GeneralSecurityException {
final HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
HttpConnectionParams.setSoTimeout(params, SO_TIMEOUT);
HttpConnectionParams.setSocketBufferSize(params, SO_BUFFER_SIZE);
HttpClientParams.setRedirecting(params, false);
final ClientConnectionManager conman = new ThreadSafeClientConnManager(params, new SchemeRegistry());
if (this.tsPath != null) {
addHttpsSchemaForTrustStore(conman, this.tsPath, this.tsPassword);
}
else {
addHttpsSchema(conman);
}
return new DefaultHttpClient(conman, params);
}
private static void addHttpsSchemaForTrustStore (final ClientConnectionManager connMan, final String tsPath, final char[] password) throws IOException, GeneralSecurityException {
final KeyStore truststore = loadKeyStore(tsPath, password);
final TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init(truststore);
final SocketFactory sf = new TlsSniSocketFactory(tmfactory.getTrustManagers());
final Scheme scheme = new Scheme("https", sf, 443); // NOSONAR 443 is not a magic number. Its HTTPS specification.
connMan.getSchemeRegistry().register(scheme);
}
private static void addHttpsSchema (final ClientConnectionManager connMan) {
final SocketFactory sf = SSLSocketFactory.getSocketFactory();
final Scheme scheme = new Scheme("https", sf, 443); // NOSONAR 443 is not a magic number. Its HTTPS specification.
connMan.getSchemeRegistry().register(scheme);
}
private static KeyStore loadKeyStore (final String tsPath, final char[] password) throws IOException, GeneralSecurityException {
final KeyStore ks = KeyStore.getInstance("BKS");
final InputStream is = HttpClientFactory.class.getResourceAsStream(tsPath);
try {
ks.load(is, password);
return ks;
}
finally {
is.close();
}
}
}