Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/next-release/bugfix-AWSSDKforJavav2-f9f830e.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "bugfix",
"category": "AWS SDK for Java v2",
"contributor": "",
"description": "Skip User-Agent header modification in ApplyUserAgentStage when custom User-Agent is already provided."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is heavy on internal details. Can we simplify so we just say we don't overwrite the custom User-Agent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks the same, forgot to commit the new file perhaps? 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my bad

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.core.ApiName;
Expand Down Expand Up @@ -66,10 +67,26 @@ public ApplyUserAgentStage(HttpClientDependencies dependencies) {
@Override
public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request,
RequestExecutionContext context) throws Exception {

if (hasUserAgentInAdditionalHeaders()) {
return request;
}
String headerValue = finalizeUserAgent(context);
return request.putHeader(HEADER_USER_AGENT, headerValue);
}

/**
* Checks if User-Agent header is present in ADDITIONAL_HTTP_HEADERS configuration.
* We skip adding user-agent in the ApplyUserAgentStage if user has set "User-Agent" header in additional header of client
*/
private boolean hasUserAgentInAdditionalHeaders() {
Map<String, List<String>> additionalHeaders = clientConfig.option(SdkClientOption.ADDITIONAL_HTTP_HEADERS);
if (additionalHeaders == null) {
return false;
}
return additionalHeaders.containsKey(HEADER_USER_AGENT);
}

/**
* The final value sent in the user agent header consists of
* <ol>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.SPACE;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -152,6 +154,94 @@ public void when_identityContainsProvider_authSourceIsPresent() throws Exception
assertThat(userAgentHeaders.get(0)).contains("m/w");
}

@Test
public void when_userAgentHeaderAlreadyPresent_AndSdkOptionAdditionalHeaderNotPresent_doesNotOverwrite() throws Exception {
ApplyUserAgentStage stage = new ApplyUserAgentStage(dependencies(clientUserAgent()));

String existingUserAgent = "CustomUserAgent/1.0";
SdkHttpFullRequest.Builder requestWithExistingHeader = SdkHttpFullRequest.builder()
.putHeader(HEADER_USER_AGENT, existingUserAgent);

RequestExecutionContext ctx = requestExecutionContext(executionAttributes(IDENTITY_WITHOUT_SOURCE), noOpRequest());
SdkHttpFullRequest.Builder result = stage.execute(requestWithExistingHeader, ctx);

List<String> userAgentHeaders = result.headers().get(HEADER_USER_AGENT);
assertThat(userAgentHeaders).isNotNull().hasSize(1);
assertThat(userAgentHeaders.get(0)).startsWith("aws-sdk-java");
}

@Test
public void when_userAgentHeaderPresentButEmpty_sdkAddsUserAgent() throws Exception {
ApplyUserAgentStage stage = new ApplyUserAgentStage(dependencies(clientUserAgent()));

SdkHttpFullRequest.Builder requestWithEmptyHeader = SdkHttpFullRequest.builder()
.putHeader(HEADER_USER_AGENT, "");

RequestExecutionContext ctx = requestExecutionContext(executionAttributes(IDENTITY_WITHOUT_SOURCE), noOpRequest());
SdkHttpFullRequest.Builder result = stage.execute(requestWithEmptyHeader, ctx);

List<String> userAgentHeaders = result.headers().get(HEADER_USER_AGENT);
assertThat(userAgentHeaders).isNotNull().hasSize(1);
assertThat(userAgentHeaders.get(0)).startsWith("aws-sdk-java");
}

@Test
public void when_userAgentHeaderPresentButNull_sdkAddsHeader() throws Exception {
ApplyUserAgentStage stage = new ApplyUserAgentStage(dependencies(clientUserAgent()));
String headerValue = null;
SdkHttpFullRequest.Builder requestWithNullHeader = SdkHttpFullRequest.builder()
.putHeader(HEADER_USER_AGENT, headerValue);

RequestExecutionContext ctx = requestExecutionContext(executionAttributes(IDENTITY_WITHOUT_SOURCE), noOpRequest());
SdkHttpFullRequest.Builder result = stage.execute(requestWithNullHeader, ctx);

List<String> userAgentHeaders = result.headers().get(HEADER_USER_AGENT);
assertThat(userAgentHeaders).isNotNull().hasSize(1);
assertThat(userAgentHeaders.get(0)).startsWith("aws-sdk-java");
}

@Test
public void when_userAgentHeaderAbsent_sdkAddsHeader() throws Exception {
ApplyUserAgentStage stage = new ApplyUserAgentStage(dependencies(clientUserAgent()));

SdkHttpFullRequest.Builder requestWithoutHeader = SdkHttpFullRequest.builder();

RequestExecutionContext ctx = requestExecutionContext(executionAttributes(IDENTITY_WITHOUT_SOURCE), noOpRequest());
SdkHttpFullRequest.Builder result = stage.execute(requestWithoutHeader, ctx);

List<String> userAgentHeaders = result.headers().get(HEADER_USER_AGENT);
assertThat(userAgentHeaders).isNotNull().hasSize(1);
assertThat(userAgentHeaders.get(0)).startsWith("aws-sdk-java");
}

@Test
public void when_userAgentInAdditionalHeaders_doesNotOverwriteUserAgent() throws Exception {
Map<String, List<String>> headerMap = new LinkedHashMap<>();
headerMap.put(HEADER_USER_AGENT, Arrays.asList("CustomAgent/1.0", "AnotherAgent/2.0"));

SdkClientConfiguration clientConfiguration =
SdkClientConfiguration.builder()
.option(SdkClientOption.CLIENT_USER_AGENT, clientUserAgent())
.option(SdkClientOption.ADDITIONAL_HTTP_HEADERS, headerMap)
.build();
HttpClientDependencies httpClientDependencies = HttpClientDependencies.builder()
.clientConfiguration(clientConfiguration)
.build();

ApplyUserAgentStage stage = new ApplyUserAgentStage(httpClientDependencies);

SdkHttpFullRequest.Builder request = SdkHttpFullRequest.builder();

RequestExecutionContext ctx = requestExecutionContext(executionAttributes(IDENTITY_WITHOUT_SOURCE), noOpRequest());
SdkHttpFullRequest.Builder result = stage.execute(request, ctx);

// ApplyUserAgentStage should skip adding User-Agent since it's in ADDITIONAL_HTTP_HEADERS
// The actual merging happens in MergeCustomHeadersStage
List<String> userAgentHeaders = result.headers().get(HEADER_USER_AGENT);
assertThat(userAgentHeaders).isNull();
}


private static HttpClientDependencies dependencies(String clientUserAgent) {
return dependencies(clientUserAgent, null, null);
}
Expand Down Expand Up @@ -219,6 +309,5 @@ private RequestExecutionContext requestExecutionContext(ExecutionAttributes exec
return RequestExecutionContext.builder()
.executionContext(executionContext)
.originalRequest(request).build();

}
}
Loading
Loading