Skip to content

Commit bbc1b3d

Browse files
authored
Add ProxyConfiguration support for UrlConnectionHttpClient (#3112)
1 parent ec8c581 commit bbc1b3d

File tree

8 files changed

+715
-22
lines changed

8 files changed

+715
-22
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "URLConnection HTTP Client",
3+
"contributor": "",
4+
"type": "feature",
5+
"description": "ProxyConfiguration support for UrlConnectionHttpClient."
6+
}

http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/ProxyConfiguration.java

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,14 @@
1616
package software.amazon.awssdk.http.apache;
1717

1818
import static software.amazon.awssdk.utils.StringUtils.isEmpty;
19+
import static software.amazon.awssdk.utils.http.SdkHttpUtils.parseNonProxyHostsProperty;
1920

2021
import java.net.URI;
21-
import java.util.Arrays;
2222
import java.util.Collections;
2323
import java.util.HashSet;
2424
import java.util.Set;
25-
import java.util.stream.Collectors;
2625
import software.amazon.awssdk.annotations.SdkPublicApi;
2726
import software.amazon.awssdk.utils.ProxySystemSetting;
28-
import software.amazon.awssdk.utils.StringUtils;
2927
import software.amazon.awssdk.utils.ToString;
3028
import software.amazon.awssdk.utils.Validate;
3129
import software.amazon.awssdk.utils.builder.CopyableBuilder;
@@ -215,22 +213,6 @@ private String resolveValue(String value, ProxySystemSetting systemSetting) {
215213
: value;
216214
}
217215

218-
/**
219-
* Returns the Java system property for nonProxyHosts as set of Strings.
220-
* See http://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html.
221-
*/
222-
private Set<String> parseNonProxyHostsProperty() {
223-
String nonProxyHosts = ProxySystemSetting.NON_PROXY_HOSTS.getStringValue().orElse(null);
224-
225-
if (!StringUtils.isEmpty(nonProxyHosts)) {
226-
return Arrays.stream(nonProxyHosts.split("\\|"))
227-
.map(String::toLowerCase)
228-
.map(s -> s.replace("*", ".*?"))
229-
.collect(Collectors.toSet());
230-
}
231-
return Collections.emptySet();
232-
}
233-
234216
/**
235217
* A builder for {@link ProxyConfiguration}.
236218
*

http-clients/url-connection-client/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@
121121
<artifactId>jetty-server</artifactId>
122122
<scope>test</scope>
123123
</dependency>
124+
<dependency>
125+
<groupId>com.github.tomakehurst</groupId>
126+
<artifactId>wiremock-jre8</artifactId>
127+
<scope>test</scope>
128+
</dependency>
124129
</dependencies>
125130

126131
<build>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.http.urlconnection;
17+
18+
import static software.amazon.awssdk.utils.StringUtils.isEmpty;
19+
import static software.amazon.awssdk.utils.http.SdkHttpUtils.parseNonProxyHostsProperty;
20+
21+
import java.net.URI;
22+
import java.util.Collections;
23+
import java.util.HashSet;
24+
import java.util.Set;
25+
import software.amazon.awssdk.annotations.SdkPublicApi;
26+
import software.amazon.awssdk.utils.ProxySystemSetting;
27+
import software.amazon.awssdk.utils.ToString;
28+
import software.amazon.awssdk.utils.Validate;
29+
import software.amazon.awssdk.utils.builder.CopyableBuilder;
30+
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
31+
32+
/**
33+
* Proxy configuration for {@link UrlConnectionHttpClient}. This class is used to configure an HTTP proxy to be used by
34+
* the {@link UrlConnectionHttpClient}.
35+
*
36+
* @see UrlConnectionHttpClient.Builder#proxyConfiguration(ProxyConfiguration)
37+
*/
38+
@SdkPublicApi
39+
public final class ProxyConfiguration implements ToCopyableBuilder<ProxyConfiguration.Builder, ProxyConfiguration> {
40+
41+
private final URI endpoint;
42+
private final String username;
43+
private final String password;
44+
private final Set<String> nonProxyHosts;
45+
private final String host;
46+
private final int port;
47+
private final String scheme;
48+
private final boolean useSystemPropertyValues;
49+
50+
/**
51+
* Initialize this configuration. Private to require use of {@link #builder()}.
52+
*/
53+
private ProxyConfiguration(DefaultClientProxyConfigurationBuilder builder) {
54+
this.endpoint = builder.endpoint;
55+
this.username = builder.username;
56+
this.password = builder.password;
57+
this.nonProxyHosts = builder.nonProxyHosts;
58+
this.useSystemPropertyValues = builder.useSystemPropertyValues;
59+
this.host = resolveHost();
60+
this.port = resolvePort();
61+
this.scheme = resolveScheme();
62+
}
63+
64+
/**
65+
* Returns the proxy host name either from the configured endpoint or
66+
* from the "http.proxyHost" system property if {@link Builder#useSystemPropertyValues(Boolean)} is set to true.
67+
*/
68+
public String host() {
69+
return host;
70+
}
71+
72+
/**
73+
* Returns the proxy port either from the configured endpoint or
74+
* from the "http.proxyPort" system property if {@link Builder#useSystemPropertyValues(Boolean)} is set to true.
75+
*
76+
* If no value is found in neither of the above options, the default value of 0 is returned.
77+
*/
78+
public int port() {
79+
return port;
80+
}
81+
82+
/**
83+
* Returns the {@link URI#scheme} from the configured endpoint. Otherwise return null.
84+
*/
85+
public String scheme() {
86+
return scheme;
87+
}
88+
89+
/**
90+
* The username to use when connecting through a proxy.
91+
*
92+
* @see Builder#password(String)
93+
*/
94+
public String username() {
95+
return resolveValue(username, ProxySystemSetting.PROXY_USERNAME);
96+
}
97+
98+
/**
99+
* The password to use when connecting through a proxy.
100+
*
101+
* @see Builder#password(String)
102+
*/
103+
public String password() {
104+
return resolveValue(password, ProxySystemSetting.PROXY_PASSWORD);
105+
}
106+
107+
/**
108+
* The hosts that the client is allowed to access without going through the proxy.
109+
*
110+
* If the value is not set on the object, the value represent by "http.nonProxyHosts" system property is returned.
111+
* If system property is also not set, an unmodifiable empty set is returned.
112+
*
113+
* @see Builder#nonProxyHosts(Set)
114+
*/
115+
public Set<String> nonProxyHosts() {
116+
Set<String> hosts = nonProxyHosts == null && useSystemPropertyValues ? parseNonProxyHostsProperty()
117+
: nonProxyHosts;
118+
119+
return Collections.unmodifiableSet(hosts != null ? hosts : Collections.emptySet());
120+
}
121+
122+
@Override
123+
public Builder toBuilder() {
124+
return builder()
125+
.endpoint(endpoint)
126+
.username(username)
127+
.password(password)
128+
.nonProxyHosts(nonProxyHosts);
129+
}
130+
131+
/**
132+
* Create a {@link Builder}, used to create a {@link ProxyConfiguration}.
133+
*/
134+
public static Builder builder() {
135+
return new DefaultClientProxyConfigurationBuilder();
136+
}
137+
138+
@Override
139+
public String toString() {
140+
return ToString.builder("ProxyConfiguration")
141+
.add("endpoint", endpoint)
142+
.add("username", username)
143+
.add("nonProxyHosts", nonProxyHosts)
144+
.build();
145+
}
146+
147+
148+
private String resolveHost() {
149+
return endpoint != null ? endpoint.getHost()
150+
: resolveValue(null, ProxySystemSetting.PROXY_HOST);
151+
}
152+
153+
private int resolvePort() {
154+
if (endpoint != null) {
155+
return endpoint.getPort();
156+
} else if (useSystemPropertyValues) {
157+
return ProxySystemSetting.PROXY_PORT.getStringValue().map(Integer::parseInt).orElse(0);
158+
}
159+
return 0;
160+
}
161+
162+
public String resolveScheme() {
163+
return endpoint != null ? endpoint.getScheme() : null;
164+
}
165+
166+
/**
167+
* Uses the configuration options, system setting property and returns the final value of the given member.
168+
*/
169+
private String resolveValue(String value, ProxySystemSetting systemSetting) {
170+
return value == null && useSystemPropertyValues ? systemSetting.getStringValue().orElse(null)
171+
: value;
172+
}
173+
174+
175+
/**
176+
* A builder for {@link ProxyConfiguration}.
177+
*
178+
* <p>All implementations of this interface are mutable and not thread safe.</p>
179+
*/
180+
public interface Builder extends CopyableBuilder<Builder, ProxyConfiguration> {
181+
182+
/**
183+
* Configure the endpoint of the proxy server that the SDK should connect through. Currently, the endpoint is limited to
184+
* a host and port. Any other URI components will result in an exception being raised.
185+
*/
186+
Builder endpoint(URI endpoint);
187+
188+
/**
189+
* Configure the username to use when connecting through a proxy.
190+
*/
191+
Builder username(String username);
192+
193+
/**
194+
* Configure the password to use when connecting through a proxy.
195+
*/
196+
Builder password(String password);
197+
198+
/**
199+
* Configure the hosts that the client is allowed to access without going through the proxy.
200+
*/
201+
Builder nonProxyHosts(Set<String> nonProxyHosts);
202+
203+
/**
204+
* Add a host that the client is allowed to access without going through the proxy.
205+
*
206+
* @see ProxyConfiguration#nonProxyHosts()
207+
*/
208+
Builder addNonProxyHost(String nonProxyHost);
209+
210+
/**
211+
* Option whether to use system property values from {@link ProxySystemSetting} if any of the config options are missing.
212+
*
213+
* This value is set to "true" by default which means SDK will automatically use system property values
214+
* for options that are not provided during building the {@link ProxyConfiguration} object. To disable this behavior,
215+
* set this value to "false".
216+
*/
217+
Builder useSystemPropertyValues(Boolean useSystemPropertyValues);
218+
219+
}
220+
221+
/**
222+
* An SDK-internal implementation of {@link Builder}.
223+
*/
224+
private static final class DefaultClientProxyConfigurationBuilder implements Builder {
225+
226+
private URI endpoint;
227+
private String username;
228+
private String password;
229+
private Set<String> nonProxyHosts;
230+
private Boolean useSystemPropertyValues = Boolean.TRUE;
231+
232+
@Override
233+
public Builder endpoint(URI endpoint) {
234+
if (endpoint != null) {
235+
Validate.isTrue(isEmpty(endpoint.getUserInfo()), "Proxy endpoint user info is not supported.");
236+
Validate.isTrue(isEmpty(endpoint.getPath()), "Proxy endpoint path is not supported.");
237+
Validate.isTrue(isEmpty(endpoint.getQuery()), "Proxy endpoint query is not supported.");
238+
Validate.isTrue(isEmpty(endpoint.getFragment()), "Proxy endpoint fragment is not supported.");
239+
}
240+
241+
this.endpoint = endpoint;
242+
return this;
243+
}
244+
245+
public void setEndpoint(URI endpoint) {
246+
endpoint(endpoint);
247+
}
248+
249+
@Override
250+
public Builder username(String username) {
251+
this.username = username;
252+
return this;
253+
}
254+
255+
public void setUsername(String username) {
256+
username(username);
257+
}
258+
259+
@Override
260+
public Builder password(String password) {
261+
this.password = password;
262+
return this;
263+
}
264+
265+
public void setPassword(String password) {
266+
password(password);
267+
}
268+
269+
@Override
270+
public Builder nonProxyHosts(Set<String> nonProxyHosts) {
271+
this.nonProxyHosts = nonProxyHosts != null ? new HashSet<>(nonProxyHosts) : null;
272+
return this;
273+
}
274+
275+
@Override
276+
public Builder addNonProxyHost(String nonProxyHost) {
277+
if (this.nonProxyHosts == null) {
278+
this.nonProxyHosts = new HashSet<>();
279+
}
280+
this.nonProxyHosts.add(nonProxyHost);
281+
return this;
282+
}
283+
284+
public void setNonProxyHosts(Set<String> nonProxyHosts) {
285+
nonProxyHosts(nonProxyHosts);
286+
}
287+
288+
@Override
289+
public Builder useSystemPropertyValues(Boolean useSystemPropertyValues) {
290+
this.useSystemPropertyValues = useSystemPropertyValues;
291+
return this;
292+
}
293+
294+
public void setUseSystemPropertyValues(Boolean useSystemPropertyValues) {
295+
useSystemPropertyValues(useSystemPropertyValues);
296+
}
297+
298+
@Override
299+
public ProxyConfiguration build() {
300+
return new ProxyConfiguration(this);
301+
}
302+
}
303+
}

0 commit comments

Comments
 (0)