diff --git a/.changes/next-release/bugfix-AWSSDKforJavav2-134c9c4.json b/.changes/next-release/bugfix-AWSSDKforJavav2-134c9c4.json new file mode 100644 index 000000000000..066f0e7a5587 --- /dev/null +++ b/.changes/next-release/bugfix-AWSSDKforJavav2-134c9c4.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "AWS SDK for Java v2", + "contributor": "afarber", + "description": "Support leading-dot domain suffixes in the NO_PROXY environment variable." +} diff --git a/core/crt-core/src/main/java/software/amazon/awssdk/crtcore/CrtProxyConfiguration.java b/core/crt-core/src/main/java/software/amazon/awssdk/crtcore/CrtProxyConfiguration.java index 014cbd6a044c..bb57a838c4a4 100644 --- a/core/crt-core/src/main/java/software/amazon/awssdk/crtcore/CrtProxyConfiguration.java +++ b/core/crt-core/src/main/java/software/amazon/awssdk/crtcore/CrtProxyConfiguration.java @@ -276,7 +276,8 @@ public interface Builder { * originate from environment variableValues, and no partial settings will be obtained from SystemPropertyValues. *

Comma-separated host names in the NO_PROXY environment variable indicate multiple hosts to exclude from * proxy settings. Surrounding empty spaces around each host name are trimmed, so both {@code "a.com,b.com"} and - * {@code "a.com, b.com"} are accepted. + * {@code "a.com, b.com"} are accepted. A leading-dot suffix such as {@code .example.com} is equivalent to + * {@code *.example.com}. * * @param useEnvironmentVariableValues The option whether to use environment variable values * @return This object for method chaining. @@ -410,4 +411,4 @@ public void setUseSystemPropertyValues(Boolean useSystemPropertyValues) { } } -} \ No newline at end of file +} diff --git a/http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/ProxyConfiguration.java b/http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/ProxyConfiguration.java index ab83d254d3fe..0eb5aed2e9ab 100644 --- a/http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/ProxyConfiguration.java +++ b/http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/ProxyConfiguration.java @@ -313,6 +313,7 @@ public interface Builder extends CopyableBuilder { * proxy settings. Surrounding empty spaces around each host name are trimmed, so both {@code "a.com,b.com"} and * {@code "a.com, b.com"} are accepted. Each entry may be an exact host name (e.g. {@code example.com}), a * leading-wildcard suffix (e.g. {@code *.example.com}, which matches {@code example.com} and its subdomains), + * or a leading-dot suffix (e.g. {@code .example.com}, equivalent to {@code *.example.com}), * a single {@code *} (which matches all hosts), or a CIDR range for IP addresses (e.g. {@code 10.0.0.0/8}). * * @param useEnvironmentVariableValues The option whether to use environment variable values. diff --git a/http-clients/apache-client/src/test/java/software/amazon/awssdk/http/apache/internal/SdkProxyRoutePlannerTest.java b/http-clients/apache-client/src/test/java/software/amazon/awssdk/http/apache/internal/SdkProxyRoutePlannerTest.java index 3685df2b3bbd..0afad8eb0caa 100644 --- a/http-clients/apache-client/src/test/java/software/amazon/awssdk/http/apache/internal/SdkProxyRoutePlannerTest.java +++ b/http-clients/apache-client/src/test/java/software/amazon/awssdk/http/apache/internal/SdkProxyRoutePlannerTest.java @@ -16,6 +16,7 @@ package software.amazon.awssdk.http.apache.internal; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import java.util.Collections; import org.apache.http.HttpException; @@ -29,6 +30,7 @@ */ public class SdkProxyRoutePlannerTest { private static final HttpHost S3_HOST = new HttpHost("s3.us-west-2.amazonaws.com", 443, "https"); + private static final HttpHost INTERNAL_HOST = new HttpHost("s3.storage.company.internal", 443, "https"); private static final HttpGet S3_REQUEST = new HttpGet("/my-bucket/my-object"); private static final HttpClientContext CONTEXT = new HttpClientContext(); @@ -49,4 +51,12 @@ public void testSetsCorrectSchemeBasedOnProcotol_HTTP() throws HttpException { assertEquals("localhost", proxyHost.getHostName()); assertEquals("http", proxyHost.getSchemeName()); } + + @Test + public void leadingDotSuffixPatternBypassesProxy() throws HttpException { + SdkProxyRoutePlanner planner = new SdkProxyRoutePlanner("localhost", 1234, "https", + Collections.singleton(".*?.company.internal")); + + assertNull(planner.determineRoute(INTERNAL_HOST, S3_REQUEST, CONTEXT).getProxyHost()); + } } diff --git a/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/ProxyConfiguration.java b/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/ProxyConfiguration.java index 61ad3805468d..df6310f3feaf 100644 --- a/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/ProxyConfiguration.java +++ b/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/ProxyConfiguration.java @@ -308,6 +308,7 @@ public interface Builder extends CopyableBuilder { * proxy settings. Surrounding empty spaces around each host name are trimmed, so both {@code "a.com,b.com"} and * {@code "a.com, b.com"} are accepted. Each entry may be an exact host name (e.g. {@code example.com}), a * leading-wildcard suffix (e.g. {@code *.example.com}, which matches {@code example.com} and its subdomains), + * or a leading-dot suffix (e.g. {@code .example.com}, equivalent to {@code *.example.com}), * a single {@code *} (which matches all hosts), or a CIDR range for IP addresses (e.g. {@code 10.0.0.0/8}). * * @param useEnvironmentVariableValues The option whether to use environment variable values. diff --git a/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/ProxyConfiguration.java b/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/ProxyConfiguration.java index da0f58d7d849..f7853e8f2ad8 100644 --- a/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/ProxyConfiguration.java +++ b/http-clients/aws-crt-client/src/main/java/software/amazon/awssdk/http/crt/ProxyConfiguration.java @@ -126,7 +126,7 @@ public interface Builder extends CrtProxyConfiguration.Builder, CopyableBuilder< * proxy settings will exclusively originate from Environment Variable Values, and no partial settings will be obtained * from System Property Values. *

Comma-separated host names in the NO_PROXY environment variable indicate multiple hosts to exclude from - * proxy settings. + * proxy settings. A leading-dot suffix such as {@code .example.com} is equivalent to {@code *.example.com}. * * @param useEnvironmentVariableValues The option whether to use environment variable values * @return This object for method chaining. @@ -177,4 +177,4 @@ public ProxyConfiguration build() { return new ProxyConfiguration(this); } } -} \ No newline at end of file +} diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/ProxyConfiguration.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/ProxyConfiguration.java index 5c0b6c54b8a6..5411283b21df 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/ProxyConfiguration.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/ProxyConfiguration.java @@ -353,6 +353,7 @@ public interface Builder extends CopyableBuilder { * proxy settings. Surrounding empty spaces around each host name are trimmed, so both {@code "a.com,b.com"} and * {@code "a.com, b.com"} are accepted. Each entry may be an exact host name (e.g. {@code example.com}), a * leading-wildcard suffix (e.g. {@code *.example.com}, which matches {@code example.com} and its subdomains), + * or a leading-dot suffix (e.g. {@code .example.com}, equivalent to {@code *.example.com}), * a single {@code *} (which matches all hosts), or a CIDR range for IP addresses (e.g. {@code 10.0.0.0/8}). * * @param useEnvironmentVariablesValues The option whether to use environment variable values @@ -460,4 +461,4 @@ public ProxyConfiguration build() { return new ProxyConfiguration(this); } } -} \ No newline at end of file +} diff --git a/http-clients/url-connection-client/src/main/java/software/amazon/awssdk/http/urlconnection/ProxyConfiguration.java b/http-clients/url-connection-client/src/main/java/software/amazon/awssdk/http/urlconnection/ProxyConfiguration.java index db1b91ffdcab..e5116f4a8a36 100644 --- a/http-clients/url-connection-client/src/main/java/software/amazon/awssdk/http/urlconnection/ProxyConfiguration.java +++ b/http-clients/url-connection-client/src/main/java/software/amazon/awssdk/http/urlconnection/ProxyConfiguration.java @@ -261,6 +261,7 @@ public interface Builder extends CopyableBuilder { * proxy settings. Surrounding empty spaces around each host name are trimmed, so both {@code "a.com,b.com"} and * {@code "a.com, b.com"} are accepted. Each entry may be an exact host name (e.g. {@code example.com}), a * leading-wildcard suffix (e.g. {@code *.example.com}, which matches {@code example.com} and its subdomains), + * or a leading-dot suffix (e.g. {@code .example.com}, equivalent to {@code *.example.com}), * a single {@code *} (which matches all hosts), or a CIDR range for IP addresses (e.g. {@code 10.0.0.0/8}). * * @param useEnvironmentVariablesValues The option whether to use environment variable values diff --git a/utils/src/main/java/software/amazon/awssdk/utils/http/SdkHttpUtils.java b/utils/src/main/java/software/amazon/awssdk/utils/http/SdkHttpUtils.java index 487610762340..9e2a47eebc34 100644 --- a/utils/src/main/java/software/amazon/awssdk/utils/http/SdkHttpUtils.java +++ b/utils/src/main/java/software/amazon/awssdk/utils/http/SdkHttpUtils.java @@ -436,10 +436,15 @@ public static Set parseNonProxyHostsProperty() { } private static Set extractNonProxyHosts(String nonProxyHosts) { + return extractNonProxyHosts(nonProxyHosts, UnaryOperator.identity()); + } + + private static Set extractNonProxyHosts(String nonProxyHosts, UnaryOperator tokenMapper) { if (nonProxyHosts != null && !isEmpty(nonProxyHosts)) { return Arrays.stream(nonProxyHosts.split("\\|")) .map(String::trim) .map(String::toLowerCase) + .map(tokenMapper) .map(s -> StringUtils.replace(s, "*", ".*?")) .collect(Collectors.toSet()); } @@ -450,6 +455,6 @@ public static Set parseNonProxyHostsEnvironmentVariable() { String hosts = ProxyEnvironmentSetting.NO_PROXY.getStringValue() .map(noProxyHost -> noProxyHost.replace(",", "|")) .orElse(null); - return extractNonProxyHosts(hosts); + return extractNonProxyHosts(hosts, host -> host.startsWith(".") ? "*" + host : host); } } diff --git a/utils/src/test/java/software/amazon/awssdk/utils/SdkHttpUtilsTest.java b/utils/src/test/java/software/amazon/awssdk/utils/SdkHttpUtilsTest.java index a7055d65b25e..f58c00da5130 100644 --- a/utils/src/test/java/software/amazon/awssdk/utils/SdkHttpUtilsTest.java +++ b/utils/src/test/java/software/amazon/awssdk/utils/SdkHttpUtilsTest.java @@ -318,6 +318,16 @@ void parseListOfNonProxyHostWithPipesAndWildCard(){ .collect(Collectors.toSet())); } + @Test + void parseListOfNonProxyHostWithLeadingDotSuffix() { + ENVIRONMENT_VARIABLE_HELPER.set("no_proxy", "example.com, .company.internal,*.greedy.org"); + + Set strings = SdkHttpUtils.parseNonProxyHostsEnvironmentVariable(); + + assertThat(strings).isEqualTo(Stream.of("example.com", ".*?.company.internal", ".*?.greedy.org") + .collect(Collectors.toSet())); + } + @Test void parseNonProxyHostsProperty_regexPathStillRewritesWildcard() { String previous = System.getProperty("http.nonProxyHosts"); @@ -334,6 +344,21 @@ void parseNonProxyHostsProperty_regexPathStillRewritesWildcard() { } } + @Test + void parseNonProxyHostsProperty_leadingDotIsNotNormalized() { + String previous = System.getProperty("http.nonProxyHosts"); + System.setProperty("http.nonProxyHosts", ".company.internal"); + try { + assertThat(SdkHttpUtils.parseNonProxyHostsProperty()).containsExactly(".company.internal"); + } finally { + if (previous == null) { + System.clearProperty("http.nonProxyHosts"); + } else { + System.setProperty("http.nonProxyHosts", previous); + } + } + } + @Test void parseListOfNonProxyHostWithCommaSpace_trimsSurroundingWhitespace(){ String multipleHostNames = "example.com, *greedy.org, 192.168.1.1";