Skip to content

Commit 05f060e

Browse files
authored
Skip modifying User-Agent in ApplyUserAgentStage if the user has already passed custom User-Agent in the request (#6614)
* Skip modifying User-Agent in ApplyUserAgentStage if the user has already passed custom User-Agent in the request * Added changes to handle EmptyString and Empty list to add SDK User Agent * Skip User-Agent Stage only if Additional headers configured on client option * Support for putHeaders on Request override config * update review comments
1 parent 45fd850 commit 05f060e

File tree

4 files changed

+475
-1
lines changed

4 files changed

+475
-1
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "ApplyUserAgentStage will not overwrite the custom User-Agent"
6+
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Collection;
2626
import java.util.Collections;
2727
import java.util.List;
28+
import java.util.Map;
2829
import java.util.Optional;
2930
import software.amazon.awssdk.annotations.SdkInternalApi;
3031
import software.amazon.awssdk.core.ApiName;
@@ -66,10 +67,38 @@ public ApplyUserAgentStage(HttpClientDependencies dependencies) {
6667
@Override
6768
public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request,
6869
RequestExecutionContext context) throws Exception {
70+
71+
if (hasUserAgentInAdditionalHeaders() || hasUserAgentInRequestConfig(context)) {
72+
return request;
73+
}
6974
String headerValue = finalizeUserAgent(context);
7075
return request.putHeader(HEADER_USER_AGENT, headerValue);
7176
}
7277

78+
/**
79+
* Checks if User-Agent header is present in ADDITIONAL_HTTP_HEADERS configuration.
80+
* We skip adding user-agent in the ApplyUserAgentStage if user has set "User-Agent" header in additional header of client
81+
*/
82+
private boolean hasUserAgentInAdditionalHeaders() {
83+
Map<String, List<String>> additionalHeaders = clientConfig.option(SdkClientOption.ADDITIONAL_HTTP_HEADERS);
84+
if (additionalHeaders == null) {
85+
return false;
86+
}
87+
return additionalHeaders.containsKey(HEADER_USER_AGENT);
88+
}
89+
90+
/**
91+
* Checks if User-Agent header is present in request override configs.
92+
* We skip adding user-agent in the ApplyUserAgentStage if user has set "User-Agent" header at request level
93+
*/
94+
private boolean hasUserAgentInRequestConfig(RequestExecutionContext context) {
95+
Map<String, List<String>> requestHeaders = context.requestConfig().headers();
96+
if (requestHeaders == null) {
97+
return false;
98+
}
99+
return requestHeaders.containsKey(HEADER_USER_AGENT);
100+
}
101+
73102
/**
74103
* The final value sent in the user agent header consists of
75104
* <ol>

core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStageTest.java

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.SPACE;
2626

2727
import java.util.Arrays;
28+
import java.util.LinkedHashMap;
2829
import java.util.List;
30+
import java.util.Map;
2931
import java.util.concurrent.CompletableFuture;
3032
import org.junit.Test;
3133
import org.junit.runner.RunWith;
@@ -152,6 +154,94 @@ public void when_identityContainsProvider_authSourceIsPresent() throws Exception
152154
assertThat(userAgentHeaders.get(0)).contains("m/w");
153155
}
154156

157+
@Test
158+
public void when_userAgentHeaderAlreadyPresent_AndSdkOptionAdditionalHeaderNotPresent_doesNotOverwrite() throws Exception {
159+
ApplyUserAgentStage stage = new ApplyUserAgentStage(dependencies(clientUserAgent()));
160+
161+
String existingUserAgent = "CustomUserAgent/1.0";
162+
SdkHttpFullRequest.Builder requestWithExistingHeader = SdkHttpFullRequest.builder()
163+
.putHeader(HEADER_USER_AGENT, existingUserAgent);
164+
165+
RequestExecutionContext ctx = requestExecutionContext(executionAttributes(IDENTITY_WITHOUT_SOURCE), noOpRequest());
166+
SdkHttpFullRequest.Builder result = stage.execute(requestWithExistingHeader, ctx);
167+
168+
List<String> userAgentHeaders = result.headers().get(HEADER_USER_AGENT);
169+
assertThat(userAgentHeaders).isNotNull().hasSize(1);
170+
assertThat(userAgentHeaders.get(0)).startsWith("aws-sdk-java");
171+
}
172+
173+
@Test
174+
public void when_userAgentHeaderPresentButEmpty_sdkAddsUserAgent() throws Exception {
175+
ApplyUserAgentStage stage = new ApplyUserAgentStage(dependencies(clientUserAgent()));
176+
177+
SdkHttpFullRequest.Builder requestWithEmptyHeader = SdkHttpFullRequest.builder()
178+
.putHeader(HEADER_USER_AGENT, "");
179+
180+
RequestExecutionContext ctx = requestExecutionContext(executionAttributes(IDENTITY_WITHOUT_SOURCE), noOpRequest());
181+
SdkHttpFullRequest.Builder result = stage.execute(requestWithEmptyHeader, ctx);
182+
183+
List<String> userAgentHeaders = result.headers().get(HEADER_USER_AGENT);
184+
assertThat(userAgentHeaders).isNotNull().hasSize(1);
185+
assertThat(userAgentHeaders.get(0)).startsWith("aws-sdk-java");
186+
}
187+
188+
@Test
189+
public void when_userAgentHeaderPresentButNull_sdkAddsHeader() throws Exception {
190+
ApplyUserAgentStage stage = new ApplyUserAgentStage(dependencies(clientUserAgent()));
191+
String headerValue = null;
192+
SdkHttpFullRequest.Builder requestWithNullHeader = SdkHttpFullRequest.builder()
193+
.putHeader(HEADER_USER_AGENT, headerValue);
194+
195+
RequestExecutionContext ctx = requestExecutionContext(executionAttributes(IDENTITY_WITHOUT_SOURCE), noOpRequest());
196+
SdkHttpFullRequest.Builder result = stage.execute(requestWithNullHeader, ctx);
197+
198+
List<String> userAgentHeaders = result.headers().get(HEADER_USER_AGENT);
199+
assertThat(userAgentHeaders).isNotNull().hasSize(1);
200+
assertThat(userAgentHeaders.get(0)).startsWith("aws-sdk-java");
201+
}
202+
203+
@Test
204+
public void when_userAgentHeaderAbsent_sdkAddsHeader() throws Exception {
205+
ApplyUserAgentStage stage = new ApplyUserAgentStage(dependencies(clientUserAgent()));
206+
207+
SdkHttpFullRequest.Builder requestWithoutHeader = SdkHttpFullRequest.builder();
208+
209+
RequestExecutionContext ctx = requestExecutionContext(executionAttributes(IDENTITY_WITHOUT_SOURCE), noOpRequest());
210+
SdkHttpFullRequest.Builder result = stage.execute(requestWithoutHeader, ctx);
211+
212+
List<String> userAgentHeaders = result.headers().get(HEADER_USER_AGENT);
213+
assertThat(userAgentHeaders).isNotNull().hasSize(1);
214+
assertThat(userAgentHeaders.get(0)).startsWith("aws-sdk-java");
215+
}
216+
217+
@Test
218+
public void when_userAgentInAdditionalHeaders_doesNotOverwriteUserAgent() throws Exception {
219+
Map<String, List<String>> headerMap = new LinkedHashMap<>();
220+
headerMap.put(HEADER_USER_AGENT, Arrays.asList("CustomAgent/1.0", "AnotherAgent/2.0"));
221+
222+
SdkClientConfiguration clientConfiguration =
223+
SdkClientConfiguration.builder()
224+
.option(SdkClientOption.CLIENT_USER_AGENT, clientUserAgent())
225+
.option(SdkClientOption.ADDITIONAL_HTTP_HEADERS, headerMap)
226+
.build();
227+
HttpClientDependencies httpClientDependencies = HttpClientDependencies.builder()
228+
.clientConfiguration(clientConfiguration)
229+
.build();
230+
231+
ApplyUserAgentStage stage = new ApplyUserAgentStage(httpClientDependencies);
232+
233+
SdkHttpFullRequest.Builder request = SdkHttpFullRequest.builder();
234+
235+
RequestExecutionContext ctx = requestExecutionContext(executionAttributes(IDENTITY_WITHOUT_SOURCE), noOpRequest());
236+
SdkHttpFullRequest.Builder result = stage.execute(request, ctx);
237+
238+
// ApplyUserAgentStage should skip adding User-Agent since it's in ADDITIONAL_HTTP_HEADERS
239+
// The actual merging happens in MergeCustomHeadersStage
240+
List<String> userAgentHeaders = result.headers().get(HEADER_USER_AGENT);
241+
assertThat(userAgentHeaders).isNull();
242+
}
243+
244+
155245
private static HttpClientDependencies dependencies(String clientUserAgent) {
156246
return dependencies(clientUserAgent, null, null);
157247
}
@@ -219,6 +309,5 @@ private RequestExecutionContext requestExecutionContext(ExecutionAttributes exec
219309
return RequestExecutionContext.builder()
220310
.executionContext(executionContext)
221311
.originalRequest(request).build();
222-
223312
}
224313
}

0 commit comments

Comments
 (0)