Skip to content

Commit

Permalink
Support providing SSLContext in the client builder (#303)
Browse files Browse the repository at this point in the history
  • Loading branch information
yahavi authored Jan 20, 2021
1 parent 3c02cbd commit de63056
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
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.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
Expand Down Expand Up @@ -43,16 +42,19 @@
*
* @author Yossi Shaul
*/
public abstract class HttpBuilderBase<T extends HttpBuilderBase> {
@SuppressWarnings("UnusedReturnValue")
public abstract class HttpBuilderBase<T extends HttpBuilderBase<?>> {

private final BasicCredentialsProvider credsProvider;
// Signifies what auth scheme will be used by the client
private final JFrogAuthScheme chosenAuthScheme = JFrogAuthScheme.BASIC;
private final RequestConfig.Builder config = RequestConfig.custom();

protected HttpClientBuilder builder = HttpClients.custom();
private RequestConfig.Builder config = RequestConfig.custom();
private HttpHost defaultHost;
private BasicCredentialsProvider credsProvider;
private JFrogAuthScheme chosenAuthScheme = JFrogAuthScheme.BASIC; //Signifies what auth scheme will be used by the client
private boolean cookieSupportEnabled = false;
private boolean trustSelfSignCert = false;
private SSLContextBuilder sslContextBuilder;
private SSLContext sslContext;
private int maxConnectionsTotal = DEFAULT_MAX_CONNECTIONS;
private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS;
private int connectionPoolTimeToLive = CONNECTION_POOL_TIME_TO_LIVE;
Expand All @@ -67,9 +69,8 @@ public CloseableHttpClient build() {
builder.setRoutePlanner(new DefaultHostRoutePlanner(defaultHost));
}
PoolingHttpClientConnectionManager connectionMgr = configConnectionManager();
CloseableHttpClientDecorator client = new CloseableHttpClientDecorator(builder.build(), connectionMgr,
return new CloseableHttpClientDecorator(builder.build(), connectionMgr,
chosenAuthScheme == JFrogAuthScheme.SPNEGO);
return client;
}

private T self() {
Expand Down Expand Up @@ -157,6 +158,15 @@ public T sslContextBuilder(SSLContextBuilder sslContextBuilder) {
return self();
}

/**
* @param sslContext SSLContext
* @return {@link T}
*/
public T sslContext(SSLContext sslContext) {
this.sslContext = sslContext;
return self();
}

/**
* Configures preemptive authentication on this client. Ignores blank username input.
*/
Expand All @@ -180,8 +190,8 @@ public T authentication(String username, String password, boolean allowAnyHost)
return self();
}

public HttpBuilderBase.ProxyConfigBuilder proxy(String host, int port) {
return new HttpBuilderBase.ProxyConfigBuilder(host, port);
public HttpBuilderBase<?>.ProxyConfigBuilder proxy(String host, int port) {
return new HttpBuilderBase<?>.ProxyConfigBuilder(host, port);
}

public class ProxyConfigBuilder {
Expand All @@ -195,7 +205,7 @@ public ProxyConfigBuilder(String host, int port) {
config.setProxy(new HttpHost(host, port));
}

public HttpBuilderBase.ProxyConfigBuilder authentication(String username, String password) {
public HttpBuilderBase<?>.ProxyConfigBuilder authentication(String username, String password) {
creds = new UsernamePasswordCredentials(username, password);
//This will demote the NTLM authentication scheme so that the proxy won't barf
//when we try to give it traditional credentials. If the proxy doesn't do NTLM
Expand All @@ -218,7 +228,7 @@ private void setProxyCreds(String host, int port) {
}

public boolean isCookieSupportEnabled() {
return cookieSupportEnabled;
return false;
}

/**
Expand All @@ -227,41 +237,35 @@ public boolean isCookieSupportEnabled() {
* @return keep-alive strategy to be used for connection pool
*/
public static ConnectionKeepAliveStrategy createConnectionKeepAliveStrategy() {
return new ConnectionKeepAliveStrategy() {
@Override
public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch (NumberFormatException ignore) {
}
return (response, context) -> {
// Honor 'keep-alive' header
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
try {
return Long.parseLong(value) * 1000;
} catch (NumberFormatException ignore) {
}
}
return 30 * 1000;
}
return 30 * 1000;
};
}

protected PoolingHttpClientConnectionManager configConnectionManager() {
if (!isCookieSupportEnabled()) {
builder.disableCookieManagement();
}
builder.disableCookieManagement();
if (hasCredentials()) {
builder.setDefaultCredentialsProvider(credsProvider);
}
RequestConfig defaultRequestConfig = config.build();
builder.setDefaultRequestConfig(defaultRequestConfig);

/**
* Connection management
*/

// Connection management
builder.setKeepAliveStrategy(createConnectionKeepAliveStrategy());

builder.setMaxConnTotal(maxConnectionsTotal);
Expand All @@ -280,17 +284,11 @@ protected PoolingHttpClientConnectionManager configConnectionManager() {
private PoolingHttpClientConnectionManager createConnectionMgr() {
PoolingHttpClientConnectionManager connectionMgr;

// prepare SSLContext
SSLContext sslContext = buildSslContext();
// Prepare SSLContext
SSLContext sslContext = this.sslContext != null ? this.sslContext : buildSslContext();
LayeredConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new DefaultHostnameVerifier());

ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
// we allow to disable host name verification against CA certificate,
// notice: in general this is insecure and should be avoided in production,
// (this type of configuration is useful for development purposes)
boolean noHostVerification = false;
LayeredConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext,
noHostVerification ? NoopHostnameVerifier.INSTANCE : new DefaultHostnameVerifier()
);
Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", plainsf)
.register("https", sslsf)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.jfrog.artifactory.client.impl.ArtifactoryImpl;
import org.jfrog.artifactory.client.impl.util.ArtifactoryHttpClient;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
Expand All @@ -23,8 +24,11 @@
* @author Lior Hasson
* @author Alexei Vainshtein
*/
@SuppressWarnings("UnusedReturnValue")
public class ArtifactoryClientBuilder {

private final List<HttpRequestInterceptor> requestInterceptorList = new ArrayList<>();

private String url;
private String username;
private String password;
Expand All @@ -33,9 +37,9 @@ public class ArtifactoryClientBuilder {
private ProxyConfig proxy;
private String userAgent;
private boolean ignoreSSLIssues;
private SSLContext sslContext;
private SSLContextBuilder sslContextBuilder;
private String accessToken;
private List<HttpRequestInterceptor> requestInterceptorList = new ArrayList<>();

protected ArtifactoryClientBuilder() {
super();
Expand Down Expand Up @@ -93,6 +97,11 @@ public ArtifactoryClientBuilder setSslContextBuilder(SSLContextBuilder sslContex
return this;
}

public ArtifactoryClientBuilder setSslContext(SSLContext sslContext) {
this.sslContext = sslContext;
return this;
}

public ArtifactoryClientBuilder setAccessToken(String accessToken) {
this.accessToken = accessToken;
return this;
Expand All @@ -103,6 +112,7 @@ public ArtifactoryClientBuilder setAccessToken(String accessToken) {
* <br>
* For further details see
* {@link org.apache.http.impl.client.HttpClientBuilder#addInterceptorLast(org.apache.http.HttpRequestInterceptor)}
*
* @param httpRequestInterceptor request interceptor that allows manipulating and examining of outgoing requests
* @return ArtifactoryClientBuilder
*/
Expand Down Expand Up @@ -136,10 +146,11 @@ private CloseableHttpClient createClientBuilder(URI uri) {
artifactoryHttpClient.socketTimeout(socketTimeout);
}

if (sslContextBuilder != null) {
if (sslContext != null) {
artifactoryHttpClient.sslContext(sslContext);
} else if (sslContextBuilder != null) {
artifactoryHttpClient.sslContextBuilder(sslContextBuilder);
}
else {
} else {
artifactoryHttpClient.trustSelfSignCert(!ignoreSSLIssues);
}
for (HttpRequestInterceptor httpRequestInterceptor : requestInterceptorList) {
Expand All @@ -155,6 +166,9 @@ public Artifactory build() {
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid Artifactory URL: " + url + ".", e);
}
if (this.sslContext != null && ignoreSSLIssues) {
throw new IllegalStateException("SslContext can't be set with ignoreSSLIssues=true.");
}

if (StringUtils.isBlank(userAgent)) {
try {
Expand Down

0 comments on commit de63056

Please sign in to comment.