Skip to content

Commit a6c9092

Browse files
authored
Support Non proxy host settings in the ProxyConfiguration for Crt http client. (#4974)
* Support Non proxy host settings in the ProxyConfiguration for Crt http client. * Handled comments
1 parent 51c1657 commit a6c9092

File tree

8 files changed

+234
-11
lines changed

8 files changed

+234
-11
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: 56 additions & 1 deletion
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) {
@@ -162,7 +184,10 @@ public boolean equals(Object o) {
162184
if (!Objects.equals(useSystemPropertyValues, that.useSystemPropertyValues)) {
163185
return false;
164186
}
165-
return Objects.equals(useEnvironmentVariableValues, that.useEnvironmentVariableValues);
187+
if (!Objects.equals(useEnvironmentVariableValues, that.useEnvironmentVariableValues)) {
188+
return false;
189+
}
190+
return Objects.equals(nonProxyHosts, that.nonProxyHosts);
166191
}
167192

168193
@Override
@@ -175,6 +200,7 @@ public int hashCode() {
175200
result = 31 * result + (useSystemPropertyValues != null ? useSystemPropertyValues.hashCode() : 0);
176201
result = 31 * result + (useEnvironmentVariableValues != null ? useEnvironmentVariableValues.hashCode() : 0);
177202
result = 31 * result + (scheme != null ? scheme.hashCode() : 0);
203+
result = 31 * result + (nonProxyHosts != null ? nonProxyHosts.hashCode() : 0);
178204
return result;
179205
}
180206

@@ -253,6 +279,17 @@ public interface Builder {
253279
*/
254280
Builder useEnvironmentVariableValues(Boolean useEnvironmentVariableValues);
255281

282+
/**
283+
* Configure the hosts that the client is allowed to access without going through the proxy.
284+
*/
285+
Builder nonProxyHosts(Set<String> nonProxyHosts);
286+
287+
288+
/**
289+
* Add a host that the client is allowed to access without going through the proxy.
290+
*/
291+
Builder addNonProxyHost(String nonProxyHost);
292+
256293

257294
CrtProxyConfiguration build();
258295
}
@@ -266,6 +303,8 @@ protected abstract static class DefaultBuilder<B extends Builder> implements Bui
266303
private String password;
267304
private Boolean useSystemPropertyValues = Boolean.TRUE;
268305
private Boolean useEnvironmentVariableValues = Boolean.TRUE;
306+
private Set<String> nonProxyHosts;
307+
269308

270309
protected DefaultBuilder() {
271310
}
@@ -278,6 +317,7 @@ protected DefaultBuilder(CrtProxyConfiguration proxyConfiguration) {
278317
this.port = proxyConfiguration.port;
279318
this.username = proxyConfiguration.username;
280319
this.password = proxyConfiguration.password;
320+
this.nonProxyHosts = proxyConfiguration.nonProxyHosts;
281321
}
282322

283323
@Override
@@ -322,6 +362,21 @@ public B useEnvironmentVariableValues(Boolean useEnvironmentVariableValues) {
322362
return (B) this;
323363
}
324364

365+
@Override
366+
public B nonProxyHosts(Set<String> nonProxyHosts) {
367+
this.nonProxyHosts = nonProxyHosts != null ? new HashSet<>(nonProxyHosts) : null;
368+
return (B) this;
369+
}
370+
371+
@Override
372+
public B addNonProxyHost(String nonProxyHost) {
373+
if (this.nonProxyHosts == null) {
374+
this.nonProxyHosts = new HashSet<>();
375+
}
376+
this.nonProxyHosts.add(nonProxyHost);
377+
return (B) this;
378+
}
379+
325380
public B setuseEnvironmentVariableValues(Boolean useEnvironmentVariableValues) {
326381
return useEnvironmentVariableValues(useEnvironmentVariableValues);
327382
}

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

Lines changed: 58 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,60 @@ 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+
.addNonProxyHost("someRandom")
99+
.addNonProxyHost(null)
100+
.username("foo")
101+
.build();
102+
103+
TlsContext tlsContext = Mockito.mock(TlsContext.class);
104+
105+
Optional<HttpProxyOptions> httpProxyOptions = CrtConfigurationUtils.resolveProxy(configuration, tlsContext);
106+
assertThat(httpProxyOptions).hasValueSatisfying(proxy -> {
107+
assertThat(proxy.getTlsContext()).isEqualTo(tlsContext);
108+
assertThat(proxy.getAuthorizationPassword()).isEqualTo("bar");
109+
assertThat(proxy.getAuthorizationUsername()).isEqualTo("foo");
110+
assertThat(proxy.getAuthorizationType()).isEqualTo(HttpProxyOptions.HttpProxyAuthorizationType.Basic);
111+
});
112+
}
113+
114+
57115
@Test
58116
void resolveProxy_noneAuthorization() {
59117
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: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515

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

18+
import java.util.Set;
1819
import software.amazon.awssdk.annotations.SdkPublicApi;
1920
import software.amazon.awssdk.crtcore.CrtProxyConfiguration;
21+
import software.amazon.awssdk.utils.ProxyEnvironmentSetting;
2022
import software.amazon.awssdk.utils.ProxySystemSetting;
2123
import software.amazon.awssdk.utils.builder.CopyableBuilder;
2224
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
@@ -116,9 +118,35 @@ public interface Builder extends CrtProxyConfiguration.Builder, CopyableBuilder<
116118
Builder useSystemPropertyValues(Boolean useSystemPropertyValues);
117119

118120

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

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

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
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919

2020
import java.lang.reflect.InvocationTargetException;
2121
import java.lang.reflect.Method;
22+
import java.util.HashSet;
2223
import java.util.Random;
24+
import java.util.Set;
25+
import java.util.stream.Collectors;
26+
import java.util.stream.IntStream;
2327
import java.util.stream.Stream;
2428
import org.junit.jupiter.api.AfterAll;
2529
import org.junit.jupiter.api.BeforeEach;
@@ -181,6 +185,8 @@ private void setRandomValue(Object o, Method setter) throws InvocationTargetExce
181185
setter.invoke(o, RNG.nextInt());
182186
} else if (Boolean.class.equals(paramClass)) {
183187
setter.invoke(o, RNG.nextBoolean());
188+
} else if (Set.class.equals(paramClass)) {
189+
setter.invoke(o, IntStream.range(0, 5).mapToObj(i -> randomString()).collect(Collectors.toSet()));
184190
} else {
185191
throw new RuntimeException("Don't know how create random value for type " + paramClass);
186192
}

0 commit comments

Comments
 (0)