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";