Skip to content

Commit ac1d5a2

Browse files
[PECO-1542] Add a way to provide proxy details to SDK (#271)
## Changes <!-- Summary of your changes that are easy to understand --> Duplicate of #242. Re-created to sign commits. (This PR adds a way to provide proxy configs in the SDK directly, ability to pick up the system properties and authentication for proxy via basic or negotiate-kerberos schemes) ## Tests <!-- How is this tested? --> Documented in #242 (These changes were tested in an environment consisting of a proxy server with kerberos authentication hosted on Ubuntu and Windows VMs. The detailed testing details are documented here: https://docs.google.com/document/d/1Zxlfx-R_JytFqMZfQMBfSQ8mj4W-aHa60vw4KdjUkEc/edit)
1 parent f66b5eb commit ac1d5a2

File tree

5 files changed

+327
-6
lines changed

5 files changed

+327
-6
lines changed

databricks-sdk-java/src/main/java/com/databricks/sdk/core/ConfigAttributeAccessor.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ public void setValueOnConfig(DatabricksConfig cfg, String value) throws IllegalA
4747
field.set(cfg, Integer.parseInt(value));
4848
} else if (field.getType() == boolean.class) {
4949
field.set(cfg, Boolean.parseBoolean(value));
50+
} else if (field.getType() == ProxyConfig.ProxyAuthType.class) {
51+
if (value != null) {
52+
field.set(cfg, ProxyConfig.ProxyAuthType.valueOf(value));
53+
}
5054
}
5155
field.setAccessible(false);
5256
}

databricks-sdk-java/src/main/java/com/databricks/sdk/core/DatabricksConfig.java

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,24 @@ public class DatabricksConfig {
114114
@ConfigAttribute(env = "DATABRICKS_RATE_LIMIT")
115115
private Integer rateLimit;
116116

117+
@ConfigAttribute(env = "PROXY_HOST")
118+
private String proxyHost;
119+
120+
@ConfigAttribute(env = "PROXY_PORT")
121+
private Integer proxyPort;
122+
123+
@ConfigAttribute(env = "PROXY_USERNAME")
124+
private String proxyUsername;
125+
126+
@ConfigAttribute(env = "PROXY_PASSWORD")
127+
private String proxyPassword;
128+
129+
@ConfigAttribute(env = "PROXY_AUTH_TYPE")
130+
private ProxyConfig.ProxyAuthType proxyAuthType;
131+
132+
@ConfigAttribute(env = "USE_SYSTEM_PROPERTIES_HTTP")
133+
private Boolean useSystemPropertiesHttp;
134+
117135
private volatile boolean resolved;
118136
private HeaderFactory headerFactory;
119137

@@ -156,12 +174,8 @@ private void initHttp() {
156174
if (httpClient != null) {
157175
return;
158176
}
159-
int timeout = 300;
160-
if (httpTimeoutSeconds != null) {
161-
timeout = httpTimeoutSeconds;
162-
}
163177
// eventually it'll get decoupled from config.
164-
httpClient = new CommonsHttpClient(timeout);
178+
httpClient = new CommonsHttpClient(this);
165179
}
166180

167181
public synchronized Map<String, String> authenticate() throws DatabricksException {
@@ -462,6 +476,60 @@ public DatabricksConfig setHttpClient(HttpClient httpClient) {
462476
return this;
463477
}
464478

479+
public String getProxyHost() {
480+
return proxyHost;
481+
}
482+
483+
public DatabricksConfig setProxyHost(String proxyHost) {
484+
this.proxyHost = proxyHost;
485+
return this;
486+
}
487+
488+
public Integer getProxyPort() {
489+
return proxyPort;
490+
}
491+
492+
public DatabricksConfig setProxyPort(Integer proxyPort) {
493+
this.proxyPort = proxyPort;
494+
return this;
495+
}
496+
497+
public String getProxyUsername() {
498+
return proxyUsername;
499+
}
500+
501+
public DatabricksConfig setProxyUsername(String proxyUsername) {
502+
this.proxyUsername = proxyUsername;
503+
return this;
504+
}
505+
506+
public String getProxyPassword() {
507+
return proxyPassword;
508+
}
509+
510+
public DatabricksConfig setProxyPassword(String proxyPassword) {
511+
this.proxyPassword = proxyPassword;
512+
return this;
513+
}
514+
515+
public ProxyConfig.ProxyAuthType getProxyAuthType() {
516+
return proxyAuthType;
517+
}
518+
519+
public DatabricksConfig setProxyAuthType(ProxyConfig.ProxyAuthType proxyAuthType) {
520+
this.proxyAuthType = proxyAuthType;
521+
return this;
522+
}
523+
524+
public Boolean getUseSystemPropertiesHttp() {
525+
return useSystemPropertiesHttp;
526+
}
527+
528+
public DatabricksConfig setUseSystemPropertiesHttp(Boolean useSystemPropertiesHttp) {
529+
this.useSystemPropertiesHttp = useSystemPropertiesHttp;
530+
return this;
531+
}
532+
465533
public boolean isAzure() {
466534
return this.getDatabricksEnvironment().getCloud() == Cloud.AZURE;
467535
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.databricks.sdk.core;
2+
3+
public class ProxyConfig {
4+
private String host;
5+
private Integer port;
6+
private String username;
7+
private String password;
8+
private ProxyAuthType proxyAuthType;
9+
private Boolean useSystemProperties;
10+
11+
public enum ProxyAuthType {
12+
// Currently we only support BASIC and SPNEGO
13+
NONE,
14+
BASIC,
15+
// We only support kerberos for negotiate
16+
SPNEGO
17+
}
18+
19+
public ProxyConfig(DatabricksConfig config) {
20+
this.host = config.getProxyHost();
21+
this.port = config.getProxyPort();
22+
this.username = config.getProxyUsername();
23+
this.password = config.getProxyPassword();
24+
this.proxyAuthType = config.getProxyAuthType();
25+
this.useSystemProperties = config.getUseSystemPropertiesHttp();
26+
}
27+
28+
public String getHost() {
29+
return host;
30+
}
31+
32+
public ProxyConfig setHost(String host) {
33+
this.host = host;
34+
return this;
35+
}
36+
37+
public Integer getPort() {
38+
return port;
39+
}
40+
41+
public ProxyConfig setPort(Integer port) {
42+
this.port = port;
43+
return this;
44+
}
45+
46+
public String getUsername() {
47+
return username;
48+
}
49+
50+
public ProxyConfig setUsername(String username) {
51+
this.username = username;
52+
return this;
53+
}
54+
55+
public String getPassword() {
56+
return password;
57+
}
58+
59+
public ProxyConfig setPassword(String password) {
60+
this.password = password;
61+
return this;
62+
}
63+
64+
public ProxyAuthType getProxyAuthType() {
65+
return proxyAuthType;
66+
}
67+
68+
public ProxyConfig setProxyAuthType(ProxyAuthType proxyAuthType) {
69+
this.proxyAuthType = proxyAuthType;
70+
return this;
71+
}
72+
73+
public Boolean getUseSystemProperties() {
74+
return useSystemProperties;
75+
}
76+
77+
public ProxyConfig setUseSystemProperties(Boolean useSystemProperties) {
78+
this.useSystemProperties = useSystemProperties;
79+
return this;
80+
}
81+
}

databricks-sdk-java/src/main/java/com/databricks/sdk/core/commons/CommonsHttpClient.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
44

5+
import com.databricks.sdk.core.DatabricksConfig;
56
import com.databricks.sdk.core.DatabricksException;
7+
import com.databricks.sdk.core.ProxyConfig;
68
import com.databricks.sdk.core.http.HttpClient;
79
import com.databricks.sdk.core.http.Request;
810
import com.databricks.sdk.core.http.Response;
911
import com.databricks.sdk.core.utils.CustomCloseInputStream;
12+
import com.databricks.sdk.core.utils.ProxyUtils;
1013
import java.io.IOException;
1114
import java.io.InputStream;
1215
import java.nio.charset.StandardCharsets;
@@ -41,6 +44,20 @@ public CommonsHttpClient(int timeoutSeconds) {
4144
hc = makeClosableHttpClient();
4245
}
4346

47+
public CommonsHttpClient(DatabricksConfig databricksConfig) {
48+
this(
49+
databricksConfig.getHttpTimeoutSeconds() == null
50+
? 300
51+
: databricksConfig.getHttpTimeoutSeconds(),
52+
new ProxyConfig(databricksConfig));
53+
}
54+
55+
public CommonsHttpClient(int timeoutSeconds, ProxyConfig proxyConfig) {
56+
timeout = timeoutSeconds * 1000;
57+
connectionManager.setMaxTotal(100);
58+
hc = makeClosableHttpClient(proxyConfig);
59+
}
60+
4461
private RequestConfig makeRequestConfig() {
4562
return RequestConfig.custom()
4663
.setConnectionRequestTimeout(timeout)
@@ -53,10 +70,18 @@ private CloseableHttpClient makeClosableHttpClient() {
5370
return HttpClientBuilder.create()
5471
.setConnectionManager(connectionManager)
5572
.setDefaultRequestConfig(makeRequestConfig())
56-
.useSystemProperties()
5773
.build();
5874
}
5975

76+
private CloseableHttpClient makeClosableHttpClient(ProxyConfig proxyConfig) {
77+
HttpClientBuilder builder =
78+
HttpClientBuilder.create()
79+
.setConnectionManager(connectionManager)
80+
.setDefaultRequestConfig(makeRequestConfig());
81+
ProxyUtils.setupProxy(proxyConfig, builder);
82+
return builder.build();
83+
}
84+
6085
@Override
6186
public Response execute(Request in) throws IOException {
6287
HttpUriRequest request = transformRequest(in);
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package com.databricks.sdk.core.utils;
2+
3+
import com.databricks.sdk.core.DatabricksException;
4+
import com.databricks.sdk.core.ProxyConfig;
5+
import java.security.Principal;
6+
import org.apache.http.HttpHost;
7+
import org.apache.http.auth.AuthSchemeProvider;
8+
import org.apache.http.auth.AuthScope;
9+
import org.apache.http.auth.Credentials;
10+
import org.apache.http.auth.UsernamePasswordCredentials;
11+
import org.apache.http.client.CredentialsProvider;
12+
import org.apache.http.client.config.AuthSchemes;
13+
import org.apache.http.config.RegistryBuilder;
14+
import org.apache.http.impl.auth.SPNegoSchemeFactory;
15+
import org.apache.http.impl.client.BasicCredentialsProvider;
16+
import org.apache.http.impl.client.HttpClientBuilder;
17+
import org.apache.http.impl.client.ProxyAuthenticationStrategy;
18+
19+
/**
20+
* This class is used to setup the proxy configs for the http client. This includes setting up the
21+
* proxy host, port, and authentication.
22+
*/
23+
public class ProxyUtils {
24+
25+
/**
26+
* Setup the proxy configuration in the http client builder.
27+
*
28+
* @param config the proxy configuration
29+
* @param builder the http client builder
30+
*/
31+
public static void setupProxy(ProxyConfig config, HttpClientBuilder builder) {
32+
String proxyHost = null;
33+
Integer proxyPort = null;
34+
String proxyUser = null;
35+
String proxyPassword = null;
36+
if (config.getUseSystemProperties() != null && config.getUseSystemProperties()) {
37+
builder.useSystemProperties();
38+
String protocol = System.getProperty("https.proxyHost") != null ? "https" : "http";
39+
proxyHost = System.getProperty(protocol + ".proxyHost");
40+
proxyPort = Integer.parseInt(System.getProperty(protocol + ".proxyPort"));
41+
proxyUser = System.getProperty(protocol + ".proxyUser");
42+
proxyPassword = System.getProperty(protocol + ".proxyPassword");
43+
}
44+
// Override system properties if proxy configuration is explicitly set
45+
if (config.getHost() != null) {
46+
proxyHost = config.getHost();
47+
proxyPort = config.getPort();
48+
proxyUser = config.getUsername();
49+
proxyPassword = config.getPassword();
50+
builder.setProxy(new HttpHost(proxyHost, proxyPort));
51+
}
52+
setupProxyAuth(
53+
proxyHost, proxyPort, config.getProxyAuthType(), proxyUser, proxyPassword, builder);
54+
}
55+
56+
/**
57+
* This method sets up the proxy authentication in the http client builder.
58+
*
59+
* @param proxyHost the proxy host
60+
* @param proxyPort the proxy port
61+
* @param proxyAuthType the proxy authentication type
62+
* @param proxyUser the proxy user
63+
* @param proxyPassword the proxy password
64+
* @param builder the http client builder
65+
*/
66+
public static void setupProxyAuth(
67+
String proxyHost,
68+
Integer proxyPort,
69+
ProxyConfig.ProxyAuthType proxyAuthType,
70+
String proxyUser,
71+
String proxyPassword,
72+
HttpClientBuilder builder) {
73+
if (proxyAuthType == null) {
74+
return;
75+
}
76+
AuthScope authScope = new AuthScope(proxyHost, proxyPort);
77+
switch (proxyAuthType) {
78+
case NONE:
79+
break;
80+
case BASIC:
81+
setupBasicProxyAuth(builder, authScope, proxyUser, proxyPassword);
82+
break;
83+
case SPNEGO:
84+
setupNegotiateProxyAuth(builder, authScope);
85+
break;
86+
default:
87+
throw new DatabricksException("Unknown proxy auth type: " + proxyAuthType);
88+
}
89+
}
90+
91+
/**
92+
* This method sets up the proxy authentication using the negotiate mechanism in the http client
93+
* builder.
94+
*
95+
* @param builder the http client builder
96+
* @param authScope the authentication scope
97+
*/
98+
public static void setupNegotiateProxyAuth(HttpClientBuilder builder, AuthScope authScope) {
99+
// We only support kerberos for negotiate as of now
100+
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
101+
// "java.security.krb5.conf" system property needs to be set if krb5.conf is not in the default
102+
// location
103+
// Use "sun.security.krb5.debug" and "sun.security.jgss.debug" system properties for debugging
104+
Credentials useJaasCreds =
105+
new Credentials() {
106+
public String getPassword() {
107+
return null;
108+
}
109+
110+
public Principal getUserPrincipal() {
111+
return null;
112+
}
113+
};
114+
115+
CredentialsProvider credsProvider = new BasicCredentialsProvider();
116+
credsProvider.setCredentials(authScope, useJaasCreds);
117+
builder
118+
.setDefaultCredentialsProvider(credsProvider)
119+
.setDefaultAuthSchemeRegistry(
120+
RegistryBuilder.<AuthSchemeProvider>create()
121+
.register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true))
122+
.build());
123+
}
124+
125+
/**
126+
* This method sets up the proxy authentication using the basic mechanism credentials provided
127+
* into the http client builder.
128+
*
129+
* @param builder the http client builder
130+
* @param authScope the authentication scope
131+
* @param proxyUser the proxy user
132+
* @param proxyPassword the proxy password
133+
*/
134+
public static void setupBasicProxyAuth(
135+
HttpClientBuilder builder, AuthScope authScope, String proxyUser, String proxyPassword) {
136+
CredentialsProvider credsProvider = new BasicCredentialsProvider();
137+
credsProvider.setCredentials(
138+
authScope, new UsernamePasswordCredentials(proxyUser, proxyPassword));
139+
builder
140+
.setDefaultCredentialsProvider(credsProvider)
141+
.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
142+
}
143+
}

0 commit comments

Comments
 (0)