Skip to content

Commit d66beb0

Browse files
committed
Support Non proxy host settings in the ProxyConfiguration for Crt http client.
1 parent 4de1e05 commit d66beb0

File tree

7 files changed

+223
-10
lines changed

7 files changed

+223
-10
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS CRT HTTP Client",
4+
"contributor": "",
5+
"description": "Support Non proxy host settings in the ProxyConfiguration for Crt http client."
6+
}

core/crt-core/src/main/java/software/amazon/awssdk/crtcore/CrtConfigurationUtils.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515

1616
package software.amazon.awssdk.crtcore;
1717

18+
import static software.amazon.awssdk.utils.StringUtils.lowerCase;
19+
20+
import java.util.Objects;
1821
import java.util.Optional;
22+
import java.util.Set;
1923
import software.amazon.awssdk.annotations.SdkProtectedApi;
2024
import software.amazon.awssdk.crt.http.HttpMonitoringOptions;
2125
import software.amazon.awssdk.crt.http.HttpProxyOptions;
@@ -33,7 +37,9 @@ public static Optional<HttpProxyOptions> resolveProxy(CrtProxyConfiguration prox
3337
if (proxyConfiguration == null) {
3438
return Optional.empty();
3539
}
36-
40+
if(doesTargetMatchNonProxyHosts(proxyConfiguration.host(), proxyConfiguration.nonProxyHosts())){
41+
return Optional.empty();
42+
}
3743
HttpProxyOptions clientProxyOptions = new HttpProxyOptions();
3844

3945
clientProxyOptions.setHost(proxyConfiguration.host());
@@ -54,11 +60,19 @@ public static Optional<HttpProxyOptions> resolveProxy(CrtProxyConfiguration prox
5460
return Optional.of(clientProxyOptions);
5561
}
5662

63+
private static boolean doesTargetMatchNonProxyHosts(String target, Set<String> hostPatterns) {
64+
return Optional.ofNullable(hostPatterns)
65+
.map(patterns ->
66+
patterns.stream()
67+
.filter(Objects::nonNull)
68+
.anyMatch(pattern -> target != null && lowerCase(target).matches(pattern)))
69+
.orElse(false);
70+
}
71+
5772
public static Optional<HttpMonitoringOptions> resolveHttpMonitoringOptions(CrtConnectionHealthConfiguration config) {
5873
if (config == null) {
5974
return Optional.empty();
6075
}
61-
6276
HttpMonitoringOptions httpMonitoringOptions = new HttpMonitoringOptions();
6377
httpMonitoringOptions.setMinThroughputBytesPerSecond(config.minimumThroughputInBps());
6478
int seconds = NumericUtils.saturatedCast(config.minimumThroughputTimeout().getSeconds());

core/crt-core/src/main/java/software/amazon/awssdk/crtcore/CrtProxyConfiguration.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717

1818
import static software.amazon.awssdk.utils.ProxyConfigProvider.fromSystemEnvironmentSettings;
1919

20+
import java.util.Collections;
21+
import java.util.HashSet;
2022
import java.util.Objects;
23+
import java.util.Set;
2124
import software.amazon.awssdk.annotations.SdkPublicApi;
2225
import software.amazon.awssdk.utils.ProxyConfigProvider;
2326
import software.amazon.awssdk.utils.ProxySystemSetting;
@@ -36,6 +39,7 @@ public abstract class CrtProxyConfiguration {
3639
private final String password;
3740
private final Boolean useSystemPropertyValues;
3841
private final Boolean useEnvironmentVariableValues;
42+
private final Set<String> nonProxyHosts;
3943

4044
protected CrtProxyConfiguration(DefaultBuilder<?> builder) {
4145
this.useSystemPropertyValues = builder.useSystemPropertyValues;
@@ -49,6 +53,7 @@ protected CrtProxyConfiguration(DefaultBuilder<?> builder) {
4953
this.port = resolvePort(builder, proxyConfigProvider);
5054
this.username = resolveUsername(builder, proxyConfigProvider);
5155
this.password = resolvePassword(builder, proxyConfigProvider);
56+
this.nonProxyHosts = resolveNonProxyHosts(builder, proxyConfigProvider);
5257
}
5358

5459
private static String resolvePassword(DefaultBuilder<?> builder, ProxyConfigProvider proxyConfigProvider) {
@@ -83,6 +88,13 @@ private static String resolveHost(DefaultBuilder<?> builder, ProxyConfigProvider
8388
}
8489
}
8590

91+
private Set<String> resolveNonProxyHosts(DefaultBuilder<?> builder, ProxyConfigProvider proxyConfigProvider) {
92+
if (builder.nonProxyHosts != null || proxyConfigProvider == null) {
93+
return builder.nonProxyHosts;
94+
}
95+
return proxyConfigProvider.nonProxyHosts();
96+
}
97+
8698
/**
8799
* @return The proxy scheme.
88100
*/
@@ -132,6 +144,16 @@ public final Boolean isUseEnvironmentVariableValues() {
132144
return useEnvironmentVariableValues;
133145
}
134146

147+
/**
148+
* Retrieves the hosts that the client is allowed to access without going through the proxy.
149+
* If the value is not set on the object, the value represented by the environment variable or system property is returned.
150+
*
151+
* @see Builder#nonProxyHosts(Set)
152+
*/
153+
public Set<String> nonProxyHosts() {
154+
return Collections.unmodifiableSet(nonProxyHosts != null ? nonProxyHosts : Collections.emptySet());
155+
}
156+
135157
@Override
136158
public boolean equals(Object o) {
137159
if (this == o) {
@@ -253,6 +275,17 @@ public interface Builder {
253275
*/
254276
Builder useEnvironmentVariableValues(Boolean useEnvironmentVariableValues);
255277

278+
/**
279+
* Configure the hosts that the client is allowed to access without going through the proxy.
280+
*/
281+
Builder nonProxyHosts(Set<String> nonProxyHosts);
282+
283+
284+
/**
285+
* Add a host that the client is allowed to access without going through the proxy.
286+
*/
287+
Builder addNonProxyHost(String nonProxyHost);
288+
256289

257290
CrtProxyConfiguration build();
258291
}
@@ -266,6 +299,8 @@ protected abstract static class DefaultBuilder<B extends Builder> implements Bui
266299
private String password;
267300
private Boolean useSystemPropertyValues = Boolean.TRUE;
268301
private Boolean useEnvironmentVariableValues = Boolean.TRUE;
302+
private Set<String> nonProxyHosts;
303+
269304

270305
protected DefaultBuilder() {
271306
}
@@ -278,6 +313,7 @@ protected DefaultBuilder(CrtProxyConfiguration proxyConfiguration) {
278313
this.port = proxyConfiguration.port;
279314
this.username = proxyConfiguration.username;
280315
this.password = proxyConfiguration.password;
316+
this.nonProxyHosts = proxyConfiguration.nonProxyHosts;
281317
}
282318

283319
@Override
@@ -322,6 +358,21 @@ public B useEnvironmentVariableValues(Boolean useEnvironmentVariableValues) {
322358
return (B) this;
323359
}
324360

361+
@Override
362+
public B nonProxyHosts(Set<String> nonProxyHosts) {
363+
this.nonProxyHosts = nonProxyHosts != null ? new HashSet<>(nonProxyHosts) : null;
364+
return (B) this;
365+
}
366+
367+
@Override
368+
public B addNonProxyHost(String nonProxyHost) {
369+
if (this.nonProxyHosts == null) {
370+
this.nonProxyHosts = new HashSet<>();
371+
}
372+
this.nonProxyHosts.add(nonProxyHost);
373+
return (B) this;
374+
}
375+
325376
public B setuseEnvironmentVariableValues(Boolean useEnvironmentVariableValues) {
326377
return useEnvironmentVariableValues(useEnvironmentVariableValues);
327378
}

core/crt-core/src/test/java/software/amazon/awssdk/crtcore/CrtConnectionUtilsTest.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020

2121
import java.time.Duration;
2222
import java.util.Optional;
23+
import java.util.stream.Collectors;
24+
import java.util.stream.Stream;
2325
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.params.ParameterizedTest;
27+
import org.junit.jupiter.params.provider.ValueSource;
2428
import org.mockito.Mockito;
2529
import software.amazon.awssdk.crt.http.HttpMonitoringOptions;
2630
import software.amazon.awssdk.crt.http.HttpProxyOptions;
@@ -54,6 +58,59 @@ void resolveProxy_emptyProxy_shouldReturnEmpty() {
5458
assertThat(CrtConfigurationUtils.resolveProxy(null, tlsContext)).isEmpty();
5559
}
5660

61+
@ParameterizedTest
62+
@ValueSource(strings = {".*?.2.3.4", "1.*?.3.4", ".*?"})
63+
void resolveProxy_withSingleNonProxyHostsWidCards_shouldReturnEmpty(String nonProxyHost) {
64+
TlsContext tlsContext = Mockito.mock(TlsContext.class);
65+
CrtProxyConfiguration configuration = new TestProxy.Builder().host("1.2.3.4")
66+
.port(123)
67+
.scheme("https")
68+
.password("bar")
69+
.username("foo")
70+
.nonProxyHosts(Stream.of(nonProxyHost,"someRandom")
71+
.collect(Collectors.toSet()))
72+
.build();
73+
assertThat(CrtConfigurationUtils.resolveProxy(configuration, tlsContext)).isEmpty();
74+
}
75+
76+
77+
78+
@Test
79+
void resolveProxy_withNullHostAndNonPorxy_shouldNotReturnEmpty( ) {
80+
TlsContext tlsContext = Mockito.mock(TlsContext.class);
81+
CrtProxyConfiguration configuration = new TestProxy.Builder().host(null)
82+
.port(123)
83+
.scheme("https")
84+
.password("bar")
85+
.username("foo")
86+
.nonProxyHosts(Stream.of("someRandom", "null")
87+
.collect(Collectors.toSet()))
88+
.build();
89+
assertThat(CrtConfigurationUtils.resolveProxy(configuration, tlsContext)).isNotEmpty();
90+
}
91+
92+
@Test
93+
void resolveProxy_basicAuthorization_WithNonMatchingNoProxy() {
94+
CrtProxyConfiguration configuration = new TestProxy.Builder().host("1.2.3.4")
95+
.port(123)
96+
.scheme("https")
97+
.password("bar")
98+
.nonProxyHosts(Stream.of("someRandom", null, "").collect(Collectors.toSet()))
99+
.username("foo")
100+
.build();
101+
102+
TlsContext tlsContext = Mockito.mock(TlsContext.class);
103+
104+
Optional<HttpProxyOptions> httpProxyOptions = CrtConfigurationUtils.resolveProxy(configuration, tlsContext);
105+
assertThat(httpProxyOptions).hasValueSatisfying(proxy -> {
106+
assertThat(proxy.getTlsContext()).isEqualTo(tlsContext);
107+
assertThat(proxy.getAuthorizationPassword()).isEqualTo("bar");
108+
assertThat(proxy.getAuthorizationUsername()).isEqualTo("foo");
109+
assertThat(proxy.getAuthorizationType()).isEqualTo(HttpProxyOptions.HttpProxyAuthorizationType.Basic);
110+
});
111+
}
112+
113+
57114
@Test
58115
void resolveProxy_noneAuthorization() {
59116
CrtProxyConfiguration configuration = new TestProxy.Builder().host("1.2.3.4")

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515

1616
package software.amazon.awssdk.http.crt;
1717

18+
import java.util.Set;
19+
import java.util.function.Consumer;
1820
import software.amazon.awssdk.annotations.SdkPublicApi;
1921
import software.amazon.awssdk.crtcore.CrtProxyConfiguration;
22+
import software.amazon.awssdk.utils.ProxyEnvironmentSetting;
2023
import software.amazon.awssdk.utils.ProxySystemSetting;
2124
import software.amazon.awssdk.utils.builder.CopyableBuilder;
2225
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
@@ -116,9 +119,35 @@ public interface Builder extends CrtProxyConfiguration.Builder, CopyableBuilder<
116119
Builder useSystemPropertyValues(Boolean useSystemPropertyValues);
117120

118121

122+
/**
123+
* Set the option whether to use environment variable values for {@link ProxyEnvironmentSetting} if any of the config
124+
* options are missing. The value is set to "true" by default, enabling the SDK to automatically use environment variable
125+
* values for proxy configuration options that are not provided during building the {@link ProxyConfiguration} object. To
126+
* disable this behavior, set this value to "false".It is important to note that when this property is set to "true," all
127+
* proxy settings will exclusively originate from Environment Variable Values, and no partial settings will be obtained
128+
* from System Property Values.
129+
* <p>Comma-separated host names in the NO_PROXY environment variable indicate multiple hosts to exclude from
130+
* proxy settings.
131+
*
132+
* @param useEnvironmentVariableValues The option whether to use environment variable values
133+
* @return This object for method chaining.
134+
*/
119135
@Override
120136
Builder useEnvironmentVariableValues(Boolean useEnvironmentVariableValues);
121137

138+
/**
139+
* Configure the hosts that the client is allowed to access without going through the proxy.
140+
*/
141+
@Override
142+
Builder nonProxyHosts(Set<String> nonProxyHosts);
143+
144+
145+
/**
146+
* Add a host that the client is allowed to access without going through the proxy.
147+
*/
148+
@Override
149+
Builder addNonProxyHost(String nonProxyHost);
150+
122151
@Override
123152
ProxyConfiguration build();
124153
}

http-clients/aws-crt-client/src/test/java/software/amazon/awssdk/http/crt/CrtHttpProxyTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.assertj.core.api.Assertions.assertThat;
1919

2020
import java.net.URISyntaxException;
21+
import java.util.Set;
2122
import software.amazon.awssdk.http.HttpProxyTestSuite;
2223
import software.amazon.awssdk.http.proxy.TestProxySetting;
2324

@@ -35,6 +36,7 @@ protected void assertProxyConfiguration(TestProxySetting userSetProxySettings,
3536
Integer portNumber = userSetProxySettings.getPort();
3637
String userName = userSetProxySettings.getUserName();
3738
String password = userSetProxySettings.getPassword();
39+
Set<String> nonProxyHosts = userSetProxySettings.getNonProxyHosts();
3840

3941
if (hostName != null) {
4042
proxyBuilder.host(hostName);
@@ -48,6 +50,9 @@ protected void assertProxyConfiguration(TestProxySetting userSetProxySettings,
4850
if (password != null) {
4951
proxyBuilder.password(password);
5052
}
53+
if (nonProxyHosts != null && !nonProxyHosts.isEmpty()) {
54+
proxyBuilder.nonProxyHosts(nonProxyHosts);
55+
}
5156
}
5257

5358
if (!"http".equals(protocol)) {
@@ -64,6 +69,7 @@ protected void assertProxyConfiguration(TestProxySetting userSetProxySettings,
6469
assertThat(proxyConfiguration.port()).isEqualTo(expectedProxySettings.getPort());
6570
assertThat(proxyConfiguration.username()).isEqualTo(expectedProxySettings.getUserName());
6671
assertThat(proxyConfiguration.password()).isEqualTo(expectedProxySettings.getPassword());
72+
assertThat(proxyConfiguration.nonProxyHosts()).isEqualTo(expectedProxySettings.getNonProxyHosts());
6773
}
6874

6975
}

0 commit comments

Comments
 (0)