Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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": "ApplyUserAgentStage will not overwrite the custom User-Agent"
}
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,38 @@ public ApplyUserAgentStage(HttpClientDependencies dependencies) {
@Override
public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request,
RequestExecutionContext context) throws Exception {

if (hasUserAgentInAdditionalHeaders() || hasUserAgentInRequestConfig(context)) {
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);
}

/**
* Checks if User-Agent header is present in request override configs.
* We skip adding user-agent in the ApplyUserAgentStage if user has set "User-Agent" header at request level
*/
private boolean hasUserAgentInRequestConfig(RequestExecutionContext context) {
Map<String, List<String>> requestHeaders = context.requestConfig().headers();
if (requestHeaders == null) {
return false;
}
return requestHeaders.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