Skip to content

Commit 8075ddc

Browse files
committed
BI-192 - Refactoring the build-info structure
HAP-556 - SNI Support when using Artifactory behind HTTPS
1 parent 5dd7706 commit 8075ddc

File tree

89 files changed

+569
-398
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+569
-398
lines changed

build-info-client/src/main/java/org/jfrog/build/client/ArtifactoryHttpClient.java

+42-25
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import org.apache.http.StatusLine;
2525
import org.apache.http.client.methods.HttpGet;
2626
import org.apache.http.client.methods.HttpPut;
27+
import org.apache.http.client.utils.HttpClientUtils;
28+
import org.apache.http.impl.client.CloseableHttpClient;
29+
import org.apache.http.util.EntityUtils;
2730
import org.codehaus.jackson.JsonFactory;
2831
import org.codehaus.jackson.JsonNode;
2932
import org.codehaus.jackson.JsonParser;
@@ -35,7 +38,6 @@
3538

3639
import java.io.IOException;
3740
import java.io.InputStream;
38-
import java.util.Map;
3941

4042
/**
4143
* @author Noam Y. Tenne
@@ -49,20 +51,24 @@ public class ArtifactoryHttpClient {
4951
public static final ArtifactoryVersion MINIMAL_ARTIFACTORY_VERSION = new ArtifactoryVersion("2.2.3");
5052
public static final String VERSION_INFO_URL = "/api/system/version";
5153
private static final int DEFAULT_CONNECTION_TIMEOUT_SECS = 300; // 5 Minutes in seconds
52-
private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT_SECS;
5354
private final Log log;
5455
private final String artifactoryUrl;
55-
private final String username;
56-
private final String password;
57-
private ProxyConfiguration proxyConfiguration;
56+
private int MAX_CONNECTION_PER_ROUTE = 50; //Default
57+
private int MAX_TOTAL_CONNECTION = 50; //Default
58+
private HttpClientConfigurator clientConfigurator;
59+
private CloseableHttpClient client;
5860

59-
private PreemptiveHttpClient deployClient;
6061

6162
public ArtifactoryHttpClient(String artifactoryUrl, String username, String password, Log log) {
6263
this.artifactoryUrl = StringUtils.stripEnd(artifactoryUrl, "/");
63-
this.username = username;
64-
this.password = password;
6564
this.log = log;
65+
66+
clientConfigurator = new HttpClientConfigurator().
67+
hostFromUrl(this.artifactoryUrl).
68+
authentication(username, password).
69+
connectionTimeout(DEFAULT_CONNECTION_TIMEOUT_SECS).
70+
maxTotalConnections(MAX_TOTAL_CONNECTION).
71+
defaultMaxConnectionsPerRoute(MAX_CONNECTION_PER_ROUTE);
6672
}
6773

6874
public static String encodeUrl(String unescaped) {
@@ -90,11 +96,13 @@ public void setProxyConfiguration(String host, int port) {
9096
* @param password Password to authenticate with the proxy
9197
*/
9298
public void setProxyConfiguration(String host, int port, String username, String password) {
93-
proxyConfiguration = new ProxyConfiguration();
99+
ProxyConfiguration proxyConfiguration = new ProxyConfiguration();
94100
proxyConfiguration.host = host;
95101
proxyConfiguration.port = port;
96102
proxyConfiguration.username = username;
97103
proxyConfiguration.password = password;
104+
105+
clientConfigurator.proxy(proxyConfiguration);
98106
}
99107

100108
/**
@@ -103,47 +111,55 @@ public void setProxyConfiguration(String host, int port, String username, String
103111
* @param connectionTimeout Timeout in seconds.
104112
*/
105113
public void setConnectionTimeout(int connectionTimeout) {
106-
this.connectionTimeout = connectionTimeout;
114+
clientConfigurator.connectionTimeout(connectionTimeout);
115+
}
116+
117+
public void setDefaultMaxConnectionsPerRoute(int maxConnectionsPerHost) {
118+
clientConfigurator.defaultMaxConnectionsPerRoute(maxConnectionsPerHost);
119+
}
120+
121+
public void setMaxTotalConnections(int maxTotalConnections) {
122+
clientConfigurator.maxTotalConnections(maxTotalConnections);
123+
}
124+
125+
public void setSoTimeout(int soTimeout) {
126+
clientConfigurator.soTimeout(soTimeout);
107127
}
108128

109129
/**
110130
* Release all connection and cleanup resources.
111131
*/
112132
public void shutdown() {
113-
if (deployClient != null) {
114-
deployClient.shutdown();
133+
if (clientConfigurator != null) {
134+
HttpClientUtils.closeQuietly(client);
115135
}
116136
}
117137

118-
public PreemptiveHttpClient getHttpClient() {
119-
if (deployClient == null) {
120-
PreemptiveHttpClient client = new PreemptiveHttpClient(username, password, connectionTimeout);
121-
if (proxyConfiguration != null) {
122-
client.setProxyConfiguration(proxyConfiguration.host, proxyConfiguration.port,
123-
proxyConfiguration.username, proxyConfiguration.password);
124-
}
125-
deployClient = client;
138+
public CloseableHttpClient getHttpClient() {
139+
if(client == null){
140+
client = clientConfigurator.getClient();
141+
126142
}
127143

128-
return deployClient;
144+
return client;
129145
}
130146

131147
public ArtifactoryVersion getVersion() throws IOException {
132148
String versionUrl = artifactoryUrl + VERSION_INFO_URL;
133-
PreemptiveHttpClient client = getHttpClient();
149+
CloseableHttpClient client = getHttpClient();
134150
HttpGet httpGet = new HttpGet(versionUrl);
135151
HttpResponse response = client.execute(httpGet);
136152
int statusCode = response.getStatusLine().getStatusCode();
137153
if (statusCode == HttpStatus.SC_NOT_FOUND) {
138154
HttpEntity httpEntity = response.getEntity();
139155
if (httpEntity != null) {
140-
httpEntity.consumeContent();
156+
EntityUtils.consume(httpEntity);
141157
}
142158
return ArtifactoryVersion.NOT_FOUND;
143159
}
144160
if (statusCode != HttpStatus.SC_OK) {
145161
if (response.getEntity() != null) {
146-
response.getEntity().consumeContent();
162+
EntityUtils.consume(response.getEntity());
147163
}
148164
throw new IOException(response.getStatusLine().getReasonPhrase());
149165
}
@@ -153,7 +169,7 @@ public ArtifactoryVersion getVersion() throws IOException {
153169
JsonParser parser;
154170
try {
155171
parser = createJsonParser(content);
156-
httpEntity.consumeContent();
172+
EntityUtils.consume(httpEntity);
157173
JsonNode result = parser.readValueAsTree();
158174
log.debug("Version result: " + result);
159175
String version = result.get("version").getTextValue();
@@ -166,6 +182,7 @@ public ArtifactoryVersion getVersion() throws IOException {
166182
}
167183
}
168184
}
185+
169186
return ArtifactoryVersion.NOT_FOUND;
170187
}
171188

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
package org.jfrog.build.client;
2+
3+
import org.apache.commons.lang.StringUtils;
4+
import org.apache.http.HttpException;
5+
import org.apache.http.HttpHost;
6+
import org.apache.http.HttpRequest;
7+
import org.apache.http.HttpRequestInterceptor;
8+
import org.apache.http.auth.AuthScope;
9+
import org.apache.http.auth.AuthState;
10+
import org.apache.http.auth.Credentials;
11+
import org.apache.http.auth.UsernamePasswordCredentials;
12+
import org.apache.http.client.CredentialsProvider;
13+
import org.apache.http.client.config.RequestConfig;
14+
import org.apache.http.client.protocol.HttpClientContext;
15+
import org.apache.http.conn.routing.HttpRoute;
16+
import org.apache.http.impl.auth.BasicScheme;
17+
import org.apache.http.impl.client.BasicCredentialsProvider;
18+
import org.apache.http.impl.client.CloseableHttpClient;
19+
import org.apache.http.impl.client.HttpClientBuilder;
20+
import org.apache.http.impl.client.HttpClients;
21+
import org.apache.http.impl.conn.DefaultRoutePlanner;
22+
import org.apache.http.impl.conn.DefaultSchemePortResolver;
23+
import org.apache.http.protocol.HttpContext;
24+
25+
import javax.annotation.Nullable;
26+
import java.io.IOException;
27+
import java.io.InputStream;
28+
import java.net.MalformedURLException;
29+
import java.net.URL;
30+
import java.util.Properties;
31+
32+
/**
33+
* @author Lior Hasson
34+
*/
35+
public class HttpClientConfigurator {
36+
private final static String CLIENT_VERSION;
37+
private HttpClientBuilder builder = HttpClients.custom();
38+
private RequestConfig.Builder config = RequestConfig.custom();
39+
private String baseUrl;
40+
private BasicCredentialsProvider credsProvider;
41+
42+
static {
43+
// initialize client version
44+
Properties properties = new Properties();
45+
InputStream is = HttpClientConfigurator.class.getResourceAsStream("/bi.client.properties");
46+
if (is != null) {
47+
try {
48+
properties.load(is);
49+
is.close();
50+
} catch (IOException e) {
51+
// ignore, use the default value
52+
}
53+
}
54+
CLIENT_VERSION = properties.getProperty("client.version", "unknown");
55+
}
56+
57+
public HttpClientConfigurator() {
58+
String userAgent = "ArtifactoryBuildClient/" + CLIENT_VERSION;
59+
builder.setUserAgent(userAgent);
60+
61+
/**
62+
Support system properties setup
63+
*/
64+
builder.useSystemProperties();
65+
credsProvider = new BasicCredentialsProvider();
66+
}
67+
68+
public HttpClientConfigurator defaultMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
69+
builder.setMaxConnPerRoute(maxConnectionsPerRoute);
70+
return this;
71+
}
72+
73+
public HttpClientConfigurator maxTotalConnections(int maxTotalConnections) {
74+
builder.setMaxConnTotal(maxTotalConnections);
75+
return this;
76+
}
77+
78+
public HttpClientConfigurator connectionTimeout(int connectionTimeout) {
79+
config.setConnectTimeout(connectionTimeout);
80+
return this;
81+
}
82+
83+
public HttpClientConfigurator soTimeout(int soTimeout) {
84+
config.setSocketTimeout(soTimeout);
85+
return this;
86+
}
87+
88+
public CloseableHttpClient getClient() {
89+
if (hasCredentials()) {
90+
builder.setDefaultCredentialsProvider(credsProvider);
91+
}
92+
return builder.setDefaultRequestConfig(config.build()).build();
93+
}
94+
95+
/**
96+
* May throw a runtime exception when the given URL is invalid.
97+
*/
98+
public HttpClientConfigurator hostFromUrl(String urlStr) throws IllegalArgumentException {
99+
if (StringUtils.isNotBlank(urlStr)) {
100+
try {
101+
URL url = new URL(urlStr);
102+
host(url.getHost());
103+
} catch (MalformedURLException e) {
104+
throw new IllegalArgumentException("Cannot parse the url " + urlStr, e);
105+
}
106+
}
107+
return this;
108+
}
109+
110+
/**
111+
* Ignores blank getValues
112+
*/
113+
public HttpClientConfigurator host(String host) {
114+
if (StringUtils.isNotBlank(host)) {
115+
this.baseUrl = host;
116+
builder.setRoutePlanner(new DefaultHostRoutePlanner(host));
117+
}
118+
return this;
119+
}
120+
121+
private boolean hasCredentials() {
122+
return credsProvider.getCredentials(AuthScope.ANY) != null;
123+
}
124+
125+
/**
126+
* Ignores blank username input
127+
*/
128+
public HttpClientConfigurator authentication(String username, String password) {
129+
if (StringUtils.isNotBlank(username)) {
130+
if (StringUtils.isBlank(baseUrl)) {
131+
throw new IllegalStateException("Cannot configure authentication when host is not set.");
132+
}
133+
134+
credsProvider.setCredentials(
135+
new AuthScope(baseUrl, AuthScope.ANY_PORT, AuthScope.ANY_REALM),
136+
new UsernamePasswordCredentials(username, password));
137+
138+
builder.addInterceptorFirst(new PreemptiveAuthInterceptor());
139+
}
140+
141+
return this;
142+
}
143+
144+
public HttpClientConfigurator proxy(@Nullable ProxyConfiguration proxyConfiguration) {
145+
configureProxy(proxyConfiguration);
146+
return this;
147+
}
148+
149+
private void configureProxy(ProxyConfiguration proxy) {
150+
if (proxy != null) {
151+
config.setProxy(new HttpHost(proxy.host, proxy.port));
152+
if (proxy.username != null) {
153+
Credentials creds = new UsernamePasswordCredentials(proxy.username, proxy.password);
154+
//TODO NTLM?
155+
credsProvider.setCredentials(new AuthScope(proxy.host, proxy.port, AuthScope.ANY_REALM), creds);
156+
}
157+
}
158+
}
159+
160+
static class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
161+
@Override
162+
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
163+
HttpClientContext clientContext = HttpClientContext.adapt(context);
164+
AuthState authState = clientContext.getTargetAuthState();
165+
166+
// If there's no auth scheme available yet, try to initialize it preemptively
167+
if (authState.getAuthScheme() == null) {
168+
CredentialsProvider credsProvider = clientContext.getCredentialsProvider();
169+
HttpHost targetHost = clientContext.getTargetHost();
170+
Credentials creds = credsProvider.getCredentials(
171+
new AuthScope(targetHost.getHostName(), targetHost.getPort()));
172+
if (creds == null) {
173+
throw new HttpException("No credentials for preemptive authentication");
174+
}
175+
authState.update(new BasicScheme(), creds);
176+
}
177+
}
178+
}
179+
180+
static class DefaultHostRoutePlanner extends DefaultRoutePlanner {
181+
private final HttpHost defaultHost;
182+
183+
public DefaultHostRoutePlanner(String defaultHost) {
184+
super(DefaultSchemePortResolver.INSTANCE);
185+
this.defaultHost = new HttpHost(defaultHost);
186+
}
187+
188+
@Override
189+
public HttpRoute determineRoute(HttpHost host, HttpRequest request, HttpContext context) throws HttpException {
190+
if (host == null) {
191+
host = defaultHost;
192+
}
193+
194+
return super.determineRoute(host, request, context);
195+
}
196+
197+
public HttpHost getDefaultHost() {
198+
return defaultHost;
199+
}
200+
}
201+
}

0 commit comments

Comments
 (0)