From fdf74582140a1011dac0b5bb9a7c5fdec397bcd2 Mon Sep 17 00:00:00 2001 From: Dongie Agnir Date: Mon, 24 Nov 2025 15:43:59 -0800 Subject: [PATCH] Add support for preemptive Basic auth for proxy Similar to #6333. Note implementations differ because Apache 5.x actually adds support simply configuring the BasicScheme with preemptive auth that wasn't present in 4.x. --- ...gfix-Apache5HTTPClientPreview-7ba481e.json | 6 +++ .../apache5/internal/utils/Apache5Utils.java | 1 + .../Apache5HttpClientProxyAuthTest.java | 53 ++++++++++++++++--- 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 .changes/next-release/bugfix-Apache5HTTPClientPreview-7ba481e.json diff --git a/.changes/next-release/bugfix-Apache5HTTPClientPreview-7ba481e.json b/.changes/next-release/bugfix-Apache5HTTPClientPreview-7ba481e.json new file mode 100644 index 00000000000..45be48f0935 --- /dev/null +++ b/.changes/next-release/bugfix-Apache5HTTPClientPreview-7ba481e.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "Apache5 HTTP Client (Preview)", + "contributor": "", + "description": "Fix bug where preemptive Basic authentication was not honored for proxies. Similar to fix for Apache 4.x in [#6333](https://github.com/aws/aws-sdk-java-v2/issues/6333)." +} diff --git a/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/internal/utils/Apache5Utils.java b/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/internal/utils/Apache5Utils.java index d559808e784..b333ad76245 100644 --- a/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/internal/utils/Apache5Utils.java +++ b/http-clients/apache5-client/src/main/java/software/amazon/awssdk/http/apache5/internal/utils/Apache5Utils.java @@ -123,6 +123,7 @@ private static void addPreemptiveAuthenticationProxy(HttpClientContext clientCon AuthCache authCache = new BasicAuthCache(); // Generate BASIC scheme object and add it to the local auth cache BasicScheme basicAuth = new BasicScheme(); + basicAuth.initPreemptive(credsProvider.getCredentials(newAuthScope(proxyConfiguration), clientContext)); authCache.put(targetHost, basicAuth); clientContext.setCredentialsProvider(credsProvider); diff --git a/http-clients/apache5-client/src/test/java/software/amazon/awssdk/http/apache5/Apache5HttpClientProxyAuthTest.java b/http-clients/apache5-client/src/test/java/software/amazon/awssdk/http/apache5/Apache5HttpClientProxyAuthTest.java index 0f7649d4d67..56706e05097 100644 --- a/http-clients/apache5-client/src/test/java/software/amazon/awssdk/http/apache5/Apache5HttpClientProxyAuthTest.java +++ b/http-clients/apache5-client/src/test/java/software/amazon/awssdk/http/apache5/Apache5HttpClientProxyAuthTest.java @@ -19,10 +19,12 @@ import static com.github.tomakehurst.wiremock.client.WireMock.any; import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.matching; import static org.assertj.core.api.Assertions.assertThat; import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import java.net.URI; import java.util.Base64; @@ -66,6 +68,43 @@ public void teardown() { } } + @Test + public void proxyAuthentication_whenPreemptiveAuthEnabled_shouldSendProxyAuthorizationHeader() throws Exception { + mockProxy.stubFor(any(anyUrl()) + .withHeader("Proxy-Authorization", equalTo(BASIC_PROXY_AUTH_HEADER)) + .willReturn(aResponse() + .withStatus(200) + .withBody("Success"))); + + // Create HTTP client with preemptive proxy authentication enabled + httpClient = Apache5HttpClient.builder() + .proxyConfiguration(ProxyConfiguration.builder() + .endpoint(URI.create("http://localhost:" + mockProxy.port())) + .username("testuser") + .password("testpass") + .preemptiveBasicAuthenticationEnabled(true) + .build()) + .build(); + + // Create a request + SdkHttpRequest request = SdkHttpRequest.builder() + .method(SdkHttpMethod.GET) + .uri(URI.create("http://example.com/test")) + .build(); + + HttpExecuteRequest executeRequest = HttpExecuteRequest.builder() + .request(request) + .build(); + + // Execute the request - should succeed with preemptive auth header + HttpExecuteResponse response = httpClient.prepareRequest(executeRequest).call(); + assertThat(response.httpResponse().statusCode()).isEqualTo(200); + + mockProxy.verify(1, anyRequestedFor(anyUrl())); + mockProxy.verify(WireMock.getRequestedFor(anyUrl()) + .withHeader("Proxy-Authorization", equalTo(BASIC_PROXY_AUTH_HEADER))); + } + @Test public void proxyAuthentication_whenPreemptiveAuthDisabled_shouldUseChallengeResponseAuth() throws Exception { // First request without auth header should get 407 @@ -83,13 +122,13 @@ public void proxyAuthentication_whenPreemptiveAuthDisabled_shouldUseChallengeRes // Create HTTP client with preemptive proxy authentication disabled httpClient = Apache5HttpClient.builder() - .proxyConfiguration(ProxyConfiguration.builder() - .endpoint(URI.create("http://localhost:" + mockProxy.port())) - .username("testuser") - .password("testpass") - .preemptiveBasicAuthenticationEnabled(false) - .build()) - .build(); + .proxyConfiguration(ProxyConfiguration.builder() + .endpoint(URI.create("http://localhost:" + mockProxy.port())) + .username("testuser") + .password("testpass") + .preemptiveBasicAuthenticationEnabled(false) + .build()) + .build(); // Create a request SdkHttpRequest request = SdkHttpRequest.builder()