package com.soasta.jenkins.httpclient;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.util.EntityUtils;
import com.soasta.jenkins.ProxyChecker;
import hudson.ProxyConfiguration;
import jenkins.model.Jenkins;
public class GenericSelfClosingHttpClient
{
private static Logger m_log = Logger.getLogger(GenericSelfClosingHttpClient.class.getName());
private CloseableHttpClient m_client;
private boolean m_closeAfterUse = true;
/**
* HttpClient designed to be used for only one HttpCall.
* @param settings
*/
public GenericSelfClosingHttpClient(HttpClientSettings settings)
{
buildClient(settings);
}
public GenericSelfClosingHttpClient(HttpClientSettings settings, boolean closeAfterUse)
{
buildClient(settings);
this.m_closeAfterUse = closeAfterUse;
}
public GenericSelfClosingHttpClient(CloseableHttpClient client)
{
m_client = client;
}
public String sendRequest(HttpUriRequest httpRequest) throws IOException
{
// Jira Bug JENKINS-21033: Changing the User-Agent from "Java/<Java version #>" to "Jenkins/<Jenkins version #>"
httpRequest.addHeader("User-Agent", "Jenkins/" + Jenkins.getVersion().toString());
HttpResponse httpResponse = m_client.execute(httpRequest);
try
{
try
{
int statusCode = httpResponse.getStatusLine().getStatusCode();
String responseBody = httpResponse.getEntity() == null ? null : EntityUtils.toString(httpResponse.getEntity(), getDefaultResponseCharacterSet());
return processResponse(httpResponse, statusCode, responseBody);
}
finally
{
// Make sure the connection can be returned to the pool.
EntityUtils.consume(httpResponse.getEntity());
}
}
finally
{
if (m_closeAfterUse)
{
m_client.close();
}
}
}
/**
* Returns the character set to use when reading responses, if the response does not specify a character set
* in the "Content-Type" header.
* @return the default character set, or {@code null} to use the HTTP client default (currently "ISO-8859-1").
*/
private String getDefaultResponseCharacterSet()
{
return null;
}
private String processResponse(HttpResponse httpResponse, int statusCode, String responseBody)
{
if (statusCode < 300)
{
return responseBody;
}
else
{
throw new HttpException(statusCode, responseBody);
}
}
public void close() throws IOException
{
m_client.close();
}
private void buildClient(HttpClientSettings settings)
{
HttpClientBuilder builder = HttpClientBuilder.create();
RegistryBuilder<ConnectionSocketFactory> schemeRegistryBuilder = RegistryBuilder.<ConnectionSocketFactory>create();
schemeRegistryBuilder.register("http", PlainConnectionSocketFactory.getSocketFactory());
KeyManager[] keyManagers = getKeyManagers(settings);
TrustManager[] trustManagers = null;
// trust all self signed certs
if (settings.trustSelfSigned())
{
trustManagers = getTrustAllSelfSigned();
}
try
{
SSLConnectionSocketFactory sslConnectionFactory = getSSLFactory(keyManagers, trustManagers);
builder.setSSLSocketFactory(sslConnectionFactory);
schemeRegistryBuilder.register("https", sslConnectionFactory);
BasicHttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(schemeRegistryBuilder.build());
builder.setConnectionManager(connectionManager);
}
catch (Exception e)
{
m_log.log(Level.SEVERE, "Error Creating HTTP Client", e);
}
Jenkins jenkins = Jenkins.getInstance();
ProxyConfiguration proxyInfo = jenkins != null ? jenkins.proxy : null;
if (proxyInfo != null)
{
HttpHost proxy = new HttpHost(proxyInfo.name, proxyInfo.port);
String host = null;
try
{
host = new URL(settings.getUrl()).getHost();
}
catch (MalformedURLException e)
{
m_log.log(Level.SEVERE, "Error Creating HTTP Client", e);
}
builder.setDefaultCredentialsProvider(getProxyCreds(proxyInfo, host));
builder.setProxy(proxy);
}
m_client = builder.build();
}
public static KeyManager[] getKeyManagers(HttpClientSettings settings)
{
if (settings.getKeyStore() == null)
{
return null;
}
try
{
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(settings.getKeyStore(), settings.getKeyStorePassword() == null || settings.getKeyStorePassword().isEmpty() ? null : settings.getKeyStorePassword().toCharArray());
return keyManagerFactory.getKeyManagers();
}
catch (Exception e)
{
m_log.log(Level.SEVERE, "Error Creating HTTP Client", e);
return null; // as if no keystore was going to be used.
}
}
public static TrustManager[] getTrustAllSelfSigned()
{
return new TrustManager[]
{
new X509TrustManager()
{
public java.security.cert.X509Certificate[] getAcceptedIssuers()
{
return new X509Certificate[] {};
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType)
{
}
}
};
}
public static SSLConnectionSocketFactory getSSLFactory(KeyManager[] keyManager, TrustManager[] trustManagers) throws Exception
{
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManager, trustManagers, new java.security.SecureRandom());
SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
return sslConnectionFactory;
}
private CredentialsProvider getProxyCreds(ProxyConfiguration proxyInfo, String host)
{
CredentialsProvider credentialsProvider = null;
// is the host on the no proxy list?
if (proxyInfo != null && proxyInfo.name != null && ProxyChecker.useProxy(host, proxyInfo))
{
credentialsProvider = new BasicCredentialsProvider();
if (proxyInfo.getUserName() != null)
{
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(proxyInfo.getUserName(),proxyInfo.getPassword()));
}
}
return credentialsProvider;
}
}