-
Notifications
You must be signed in to change notification settings - Fork 937
Description
Describe the bug
When using S3AsyncClient (Netty-based) to connect to LocalStack or other local services, the SDK builds virtual-hosted–style URLs such as:
http://my-bucket.localhost:4566.
Even with proxy exclusions like NO_PROXY=localhost,127.0.0.1 or .nonProxyHosts(Set.of("localhost", "127.0.0.1")), the SDK still routes the request through the configured proxy. This is unexpected and leads to failures when behind a corporate proxy.
This behavior breaks most LocalStack-based development workflows and contradicts standard expectations for proxy exclusion handling.
Expected Behavior
When configuring proxy exclusions using NO_PROXY=localhost,127.0.0.1 or setting .nonProxyHosts(Set.of("localhost", "127.0.0.1")), the SDK should not route any requests to localhost through a proxy, regardless of whether the hostname is exactly localhost or a subdomain like my-bucket.localhost.
Since localhost and its subdomains resolve to the loopback interface and are intended for local communication, proxying such requests defeats the purpose and typically results in connection failures, especially in development environments that rely on local containers or mock services like LocalStack.
Therefore, if the final resolved host is:
localhost
127.0.0.1
or ends with .localhost
…it should be excluded from proxy usage entirely.
This matches standard behavior across tools, libraries, and languages where NO_PROXY=localhost implicitly applies to any *.localhost subdomain.
Current Behavior
When using S3AsyncClient with the default virtual-hosted–style addressing, the SDK generates URLs like http://my-bucket.localhost:4566, where the bucket name becomes part of the host.
Internally, the SDK evaluates whether to use a proxy by checking if the full host — in this case, my-bucket.localhost — matches any entry in NO_PROXY or nonProxyHosts.
However, since my-bucket.localhost does not match localhost or 127.0.0.1 exactly, the SDK assumes it should use the configured proxy. As a result, requests to LocalStack (or other local services) are incorrectly routed through the proxy, leading to errors such as proxy tunnel failures or timeouts.
This happens even though the user has clearly configured NO_PROXY=localhost, and would reasonably expect this to apply to any address that resolves to the loopback interface, including subdomains like my-bucket.localhost.
Reproduction Steps
1 - Start LocalStack on http://localhost:4566 in a proxy environment
2 - Configure environment variables:
HTTP_PROXY=http://proxy.mycompany.com:3128
HTTPS_PROXY=http://proxy.mycompany.com:3128
NO_PROXY=localhost,127.0.0.1
3 - Create a client using:
S3AsyncClient.builder().endpointOverride(URI.create("http://localhost:4566")).region(Region.US_EAST_1).build()
4 - Call any operation, for example:
client.getObject(GetObjectRequest.builder().bucket("my-bucket").key("test.txt").build(), AsyncResponseTransformer.toBytes())
5 - Even though localhost is excluded, the SDK routes the request to the proxy. This results in errors such as:
Unable to establish tunnel for channel: my-bucket.localhost
or
504 Gateway Timeout if the proxy cannot resolve or reach my-bucket.localhost
Possible Solution
Add .*.localhost to nonProxyHosts
Disable proxy usage explicitly when targeting localhost
Force path-style addressing (if feasible)
Treat any host ending with .localhost as a proxy exclusion
If localhost is listed in NO_PROXY or nonProxyHosts, implicitly exclude all *.localhost as well
Allow an easy toggle to enforce path-style addressing
Improve documentation regarding proxy behavior with virtual-hosted addressing and localhost
Additional Information/Context
No response
AWS Java SDK version used
2.23.14
JDK version used
17.0.5
Operating System and version
CentOS Red Hat Enterprise Linux release 8.10 (Ootpa) Kernel: 4.18.0-553.50.1.el8_10.x86_64