Skip to content

Commit e699665

Browse files
committed
Fix proxy preemotive authentication failure
1 parent c241f5e commit e699665

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/internal/utils/ApacheUtils.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import org.apache.http.impl.auth.BasicScheme;
3131
import org.apache.http.impl.client.BasicAuthCache;
3232
import org.apache.http.impl.client.BasicCredentialsProvider;
33+
import org.apache.http.auth.AUTH;
34+
import org.apache.http.message.BasicHeader;
3335
import software.amazon.awssdk.annotations.SdkInternalApi;
3436
import software.amazon.awssdk.http.apache.ProxyConfiguration;
3537
import software.amazon.awssdk.utils.Logger;
@@ -149,6 +151,11 @@ private static void addPreemptiveAuthenticationProxy(HttpClientContext clientCon
149151
AuthCache authCache = new BasicAuthCache();
150152
// Generate BASIC scheme object and add it to the local auth cache
151153
BasicScheme basicAuth = new BasicScheme();
154+
try {
155+
basicAuth.processChallenge(new BasicHeader(AUTH.PROXY_AUTH, "BASIC realm=default"));
156+
} catch (Exception e) {
157+
logger.warn(() -> "Failed to process synthetic challenge for preemptive proxy authentication: " + e.getMessage());
158+
}
152159
authCache.put(targetHost, basicAuth);
153160

154161
clientContext.setCredentialsProvider(credsProvider);
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.http.apache;
17+
18+
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
19+
import static com.github.tomakehurst.wiremock.client.WireMock.any;
20+
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
21+
import static com.github.tomakehurst.wiremock.client.WireMock.matching;
22+
import static org.assertj.core.api.Assertions.assertThat;
23+
24+
import com.github.tomakehurst.wiremock.WireMockServer;
25+
import com.github.tomakehurst.wiremock.client.WireMock;
26+
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
27+
import java.net.URI;
28+
import org.junit.jupiter.api.AfterEach;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.Test;
31+
import software.amazon.awssdk.http.HttpExecuteRequest;
32+
import software.amazon.awssdk.http.HttpExecuteResponse;
33+
import software.amazon.awssdk.http.SdkHttpClient;
34+
import software.amazon.awssdk.http.SdkHttpMethod;
35+
import software.amazon.awssdk.http.SdkHttpRequest;
36+
37+
/**
38+
* Tests proxy preemptive authentication functionality.
39+
*
40+
* Verifies that when preemptiveBasicAuthenticationEnabled(true) is configured,
41+
* the Proxy-Authorization header is sent with the first request to the proxy.
42+
*/
43+
public class ProxyPreemptiveAuthTest {
44+
45+
private WireMockServer proxyServer;
46+
private SdkHttpClient httpClient;
47+
48+
@BeforeEach
49+
public void setup() {
50+
proxyServer = new WireMockServer(WireMockConfiguration.options().dynamicPort());
51+
proxyServer.start();
52+
}
53+
54+
@AfterEach
55+
public void teardown() {
56+
if (httpClient != null) {
57+
httpClient.close();
58+
}
59+
if (proxyServer != null) {
60+
proxyServer.stop();
61+
}
62+
}
63+
64+
@Test
65+
public void testPreemptiveAuthenticationSendsProxyAuthorizationHeader() throws Exception {
66+
proxyServer.stubFor(any(anyUrl())
67+
.withHeader("Proxy-Authorization", matching("Basic .+"))
68+
.willReturn(aResponse()
69+
.withStatus(200)
70+
.withBody("Success")));
71+
72+
// Create HTTP client with preemptive proxy authentication enabled
73+
httpClient = ApacheHttpClient.builder()
74+
.proxyConfiguration(ProxyConfiguration.builder()
75+
.endpoint(URI.create("http://localhost:" + proxyServer.port()))
76+
.username("testuser")
77+
.password("testpass")
78+
.preemptiveBasicAuthenticationEnabled(true)
79+
.build())
80+
.build();
81+
82+
// Create a request
83+
SdkHttpRequest request = SdkHttpRequest.builder()
84+
.method(SdkHttpMethod.GET)
85+
.uri(URI.create("http://example.com/test"))
86+
.build();
87+
88+
HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
89+
.request(request)
90+
.build();
91+
92+
// Execute the request - should succeed with preemptive auth header
93+
HttpExecuteResponse response = httpClient.prepareRequest(executeRequest).call();
94+
assertThat(response.httpResponse().statusCode()).isEqualTo(200);
95+
96+
// Verify that the proxy received the request with Proxy-Authorization header
97+
proxyServer.verify(WireMock.getRequestedFor(anyUrl())
98+
.withHeader("Proxy-Authorization", matching("Basic .+")));
99+
}
100+
}

0 commit comments

Comments
 (0)