From e0c2f8b75861048bfedd10f7381563c937a6a883 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Mon, 20 Oct 2025 11:31:32 -0700 Subject: [PATCH 01/14] Add business metric support for Sigv4A auth scheme --- .../feature-AWSSDKforJavav2-7c6c7a4.json | 6 + .../scheme/AuthSchemeInterceptorSpec.java | 10 ++ ...-sigv4a-value-auth-scheme-interceptor.java | 5 + ...ith-allowlist-auth-scheme-interceptor.java | 6 +- ...out-allowlist-auth-scheme-interceptor.java | 5 + .../useragent/BusinessMetricFeatureId.java | 3 +- .../Sigv4aBusinessMetricUserAgentTest.java | 145 ++++++++++++++++++ 7 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 .changes/next-release/feature-AWSSDKforJavav2-7c6c7a4.json create mode 100644 test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java diff --git a/.changes/next-release/feature-AWSSDKforJavav2-7c6c7a4.json b/.changes/next-release/feature-AWSSDKforJavav2-7c6c7a4.json new file mode 100644 index 000000000000..62044809461b --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-7c6c7a4.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Add business metric support for Sigv4A auth scheme to track when an operation is called using Sigv4A signing" +} diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java index 043bf74ba9d9..530b26329fff 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java @@ -158,6 +158,16 @@ private MethodSpec generateBeforeExecution() { builder.addStatement("recordEnvironmentTokenBusinessMetric(selectedAuthScheme, " + "executionAttributes)"); } + + if (authSchemeSpecUtils.hasSigV4aSupport()) { + builder.beginControlFlow("if (selectedAuthScheme != null && " + + "selectedAuthScheme.authSchemeOption().schemeId().equals($S))", + "aws.auth#sigv4a") + .addStatement("executionAttributes.getAttribute($T.BUSINESS_METRICS)" + + ".addMetric($T.SIGV4A_SIGNING.value())", + SdkInternalExecutionAttribute.class, BusinessMetricFeatureId.class) + .endControlFlow(); + } return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java index 70b901338096..04bf0f6fa9c3 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java @@ -20,6 +20,7 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; @@ -49,6 +50,10 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) { + executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS).addMetric( + BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + } } private List resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java index dcc6418d2e89..17a5db41df17 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java @@ -20,6 +20,7 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; @@ -52,6 +53,10 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) { + executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS).addMetric( + BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + } } private List resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { @@ -102,7 +107,6 @@ private QueryAuthSchemeParams authSchemeParams(SdkRequest request, ExecutionAttr executionAttributes.getOptionalAttribute(AwsExecutionAttribute.AWS_SIGV4A_SIGNING_REGION_SET) .filter(regionSet -> !CollectionUtils.isNullOrEmpty(regionSet)) .ifPresent(nonEmptyRegionSet -> builder.regionSet(RegionSet.create(nonEmptyRegionSet))); - if (builder instanceof QueryEndpointResolverAware.Builder) { EndpointProvider endpointProvider = executionAttributes.getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER); if (endpointProvider instanceof QueryEndpointProvider) { diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java index e9405f2fbb67..3f142a7b9072 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java @@ -20,6 +20,7 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; @@ -52,6 +53,10 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) { + executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS).addMetric( + BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + } } private List resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java index 884f57bf5691..4e68a3d32b7e 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/useragent/BusinessMetricFeatureId.java @@ -22,7 +22,7 @@ /** * An enum class representing a short form of identity providers to record in the UA string. * - * Unimplemented metrics: I,J,K,M,O,S,U-c + * Unimplemented metrics: I,J,K,M,O,U-c * Unsupported metrics (these will never be added): A,H */ @SdkProtectedApi @@ -39,6 +39,7 @@ public enum BusinessMetricFeatureId { ACCOUNT_ID_MODE_PREFERRED("P"), ACCOUNT_ID_MODE_DISABLED("Q"), ACCOUNT_ID_MODE_REQUIRED("R"), + SIGV4A_SIGNING("S"), RESOLVED_ACCOUNT_ID("T"), DDB_MAPPER("d"), BEARER_SERVICE_ENV_VARS("3"), diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java new file mode 100644 index 000000000000..c2f8bb17e846 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java @@ -0,0 +1,145 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.services; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.awssdk.core.useragent.BusinessMetricCollection.METRIC_SEARCH_PATTERN; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; +import software.amazon.awssdk.http.AbortableInputStream; +import software.amazon.awssdk.http.HttpExecuteResponse; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonAsyncClient; +import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonClient; +import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthAsyncClient; +import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthClient; +import software.amazon.awssdk.testutils.service.http.MockAsyncHttpClient; +import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient; +import software.amazon.awssdk.utils.StringInputStream; + +/** + * Test class to verify that SIGV4A_SIGNING business metric is correctly included + * in the User-Agent header when operation called using Sigv4A signing. + */ +class Sigv4aBusinessMetricUserAgentTest { + private static final String USER_AGENT_HEADER_NAME = "User-Agent"; + private static final StaticCredentialsProvider CREDENTIALS_PROVIDER = + StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid")); + + private MockSyncHttpClient mockHttpClient; + private MockAsyncHttpClient mockAsyncHttpClient; + + @BeforeEach + public void setup() { + mockHttpClient = new MockSyncHttpClient(); + mockHttpClient.stubNextResponse(mockResponse()); + + mockAsyncHttpClient = new MockAsyncHttpClient(); + mockAsyncHttpClient.stubNextResponse(mockResponse()); + } + + @Test + void when_sigv4aServiceIsUsed_correctMetricIsAdded() { + Sigv4AauthClient client = Sigv4AauthClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockHttpClient) + .build(); + + client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); + + String userAgent = getUserAgentFromLastRequest(); + System.out.println("SigV4a service User-Agent: " + userAgent); + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + } + + @Test + void when_sigv4aServiceIsUsedAsync_correctMetricIsAdded() { + Sigv4AauthAsyncClient asyncClient = Sigv4AauthAsyncClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockAsyncHttpClient) + .build(); + + asyncClient.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")).join(); + + String userAgent = getUserAgentFromLastAsyncRequest(); + System.out.println("SigV4a async service User-Agent: " + userAgent); + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + } + + @Test + void when_regularServiceIsUsed_sigv4aMetricIsNotAdded() { + ProtocolRestJsonClient client = ProtocolRestJsonClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockHttpClient) + .build(); + + client.allTypes(r -> {}); + + String userAgent = getUserAgentFromLastRequest(); + System.out.println("Regular service User-Agent: " + userAgent); + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + } + + @Test + void when_regularServiceIsUsedAsync_sigv4aMetricIsNotAdded() { + ProtocolRestJsonAsyncClient asyncClient = ProtocolRestJsonAsyncClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockAsyncHttpClient) + .build(); + + asyncClient.allTypes(r -> {}).join(); + + String userAgent = getUserAgentFromLastAsyncRequest(); + System.out.println("Regular async service User-Agent: " + userAgent); + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + } + + private String getUserAgentFromLastRequest() { + SdkHttpRequest lastRequest = mockHttpClient.getLastRequest(); + assertThat(lastRequest).isNotNull(); + + List userAgentHeaders = lastRequest.headers().get(USER_AGENT_HEADER_NAME); + assertThat(userAgentHeaders).isNotNull().hasSize(1); + return userAgentHeaders.get(0); + } + + private String getUserAgentFromLastAsyncRequest() { + SdkHttpRequest lastRequest = mockAsyncHttpClient.getLastRequest(); + assertThat(lastRequest).isNotNull(); + + List userAgentHeaders = lastRequest.headers().get(USER_AGENT_HEADER_NAME); + assertThat(userAgentHeaders).isNotNull().hasSize(1); + return userAgentHeaders.get(0); + } + + private static HttpExecuteResponse mockResponse() { + return HttpExecuteResponse.builder() + .response(SdkHttpResponse.builder().statusCode(200).build()) + .responseBody(AbortableInputStream.create(new StringInputStream("{}"))) + .build(); + } +} From 558ac662f2a059ba5002babccafb4b6715fdbacc Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Mon, 20 Oct 2025 13:00:40 -0700 Subject: [PATCH 02/14] Fixing failing tests --- .../poet/auth/scheme/AuthSchemeInterceptorSpec.java | 10 +++++++--- .../ops-auth-sigv4a-value-auth-scheme-interceptor.java | 8 ++++++-- ...-params-with-allowlist-auth-scheme-interceptor.java | 8 ++++++-- ...rams-without-allowlist-auth-scheme-interceptor.java | 8 ++++++-- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java index 530b26329fff..ab5e01c04516 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java @@ -163,9 +163,13 @@ private MethodSpec generateBeforeExecution() { builder.beginControlFlow("if (selectedAuthScheme != null && " + "selectedAuthScheme.authSchemeOption().schemeId().equals($S))", "aws.auth#sigv4a") - .addStatement("executionAttributes.getAttribute($T.BUSINESS_METRICS)" - + ".addMetric($T.SIGV4A_SIGNING.value())", - SdkInternalExecutionAttribute.class, BusinessMetricFeatureId.class) + .addStatement("$T businessMetrics = executionAttributes.getAttribute($T.BUSINESS_METRICS)", + ClassName.get("software.amazon.awssdk.core.useragent", "BusinessMetricCollection"), + SdkInternalExecutionAttribute.class) + .beginControlFlow("if (businessMetrics != null)") + .addStatement("businessMetrics.addMetric($T.SIGV4A_SIGNING.value())", + BusinessMetricFeatureId.class) + .endControlFlow() .endControlFlow(); } return builder.build(); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java index 04bf0f6fa9c3..76228f5deeb0 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java @@ -20,6 +20,7 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricCollection; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; @@ -51,8 +52,11 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) { - executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS).addMetric( - BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + BusinessMetricCollection businessMetrics = executionAttributes + .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); + if (businessMetrics != null) { + businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + } } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java index 17a5db41df17..d74286509c00 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java @@ -20,6 +20,7 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricCollection; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; @@ -54,8 +55,11 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) { - executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS).addMetric( - BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + BusinessMetricCollection businessMetrics = executionAttributes + .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); + if (businessMetrics != null) { + businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + } } } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java index 3f142a7b9072..2157ea7aad6d 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java @@ -20,6 +20,7 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricCollection; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; @@ -54,8 +55,11 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) { - executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS).addMetric( - BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + BusinessMetricCollection businessMetrics = executionAttributes + .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); + if (businessMetrics != null) { + businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + } } } From 823f0bdb8d937a3bcbac0af62e475a2c7481fc8f Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Tue, 21 Oct 2025 16:58:53 -0700 Subject: [PATCH 03/14] Address PR feedback --- .../scheme/AuthSchemeInterceptorSpec.java | 6 +- ...-sigv4a-value-auth-scheme-interceptor.java | 4 +- ...ith-allowlist-auth-scheme-interceptor.java | 4 +- ...out-allowlist-auth-scheme-interceptor.java | 4 +- .../Sigv4aBusinessMetricUserAgentTest.java | 44 ++++++++++ test/crt-unavailable-tests/pom.xml | 6 ++ .../sigv4aonly/service-2.json | 45 ++++++++++ ...igv4aCrtUnavailableBusinessMetricTest.java | 85 +++++++++++++++++++ 8 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 test/crt-unavailable-tests/src/main/resources/codegen-resources/sigv4aonly/service-2.json create mode 100644 test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java index ab5e01c04516..1fead6507881 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java @@ -161,8 +161,10 @@ private MethodSpec generateBeforeExecution() { if (authSchemeSpecUtils.hasSigV4aSupport()) { builder.beginControlFlow("if (selectedAuthScheme != null && " - + "selectedAuthScheme.authSchemeOption().schemeId().equals($S))", - "aws.auth#sigv4a") + + "selectedAuthScheme.authSchemeOption().schemeId().equals($S) && " + + "!$T.isSignerOverridden(context.request(), executionAttributes))", + "aws.auth#sigv4a", + ClassName.get("software.amazon.awssdk.awscore.util", "SignerOverrideUtils")) .addStatement("$T businessMetrics = executionAttributes.getAttribute($T.BUSINESS_METRICS)", ClassName.get("software.amazon.awssdk.core.useragent", "BusinessMetricCollection"), SdkInternalExecutionAttribute.class) diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java index 76228f5deeb0..3e469aaac9e7 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java @@ -10,6 +10,7 @@ import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.awscore.util.SignerOverrideUtils; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.exception.SdkException; @@ -51,7 +52,8 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); - if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) { + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a") + && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { BusinessMetricCollection businessMetrics = executionAttributes .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); if (businessMetrics != null) { diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java index d74286509c00..147a209712ab 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java @@ -10,6 +10,7 @@ import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.awscore.util.SignerOverrideUtils; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.exception.SdkException; @@ -54,7 +55,8 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); - if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) { + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a") + && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { BusinessMetricCollection businessMetrics = executionAttributes .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); if (businessMetrics != null) { diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java index 2157ea7aad6d..637dbc6177ae 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java @@ -10,6 +10,7 @@ import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.awscore.util.SignerOverrideUtils; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.exception.SdkException; @@ -54,7 +55,8 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); - if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a")) { + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a") + && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { BusinessMetricCollection businessMetrics = executionAttributes .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); if (businessMetrics != null) { diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java index c2f8bb17e846..8a4b8ade3bda 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java @@ -23,6 +23,9 @@ import org.junit.jupiter.api.Test; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.auth.signer.Aws4Signer; +import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.http.HttpExecuteResponse; @@ -33,9 +36,11 @@ import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonClient; import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthAsyncClient; import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthClient; +import software.amazon.awssdk.services.sigv4aauth.auth.scheme.Sigv4AauthAuthSchemeProvider; import software.amazon.awssdk.testutils.service.http.MockAsyncHttpClient; import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient; import software.amazon.awssdk.utils.StringInputStream; +import java.util.Arrays; /** * Test class to verify that SIGV4A_SIGNING business metric is correctly included @@ -118,6 +123,45 @@ void when_regularServiceIsUsedAsync_sigv4aMetricIsNotAdded() { assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); } + @Test + void when_signerIsOverridden_sigv4aMetricIsNotAdded() { + Sigv4AauthClient client = Sigv4AauthClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .overrideConfiguration(ClientOverrideConfiguration.builder() + .putAdvancedOption(SdkAdvancedClientOption.SIGNER, Aws4Signer.create()) + .build()) + .httpClient(mockHttpClient) + .build(); + + client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); + + String userAgent = getUserAgentFromLastRequest(); + System.out.println("Signer override User-Agent: " + userAgent); + + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + } + + + @Test + void when_authSchemeProviderOverridesSigv4aOrder_sigv4IsSelected() { + Sigv4AauthClient client = Sigv4AauthClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .authSchemeProvider(Sigv4AauthAuthSchemeProvider. + defaultProvider( + Arrays.asList("sigv4","sigv4a"))) + .httpClient(mockHttpClient) + .build(); + + client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); + + String userAgent = getUserAgentFromLastRequest(); + System.out.println("User-Agent: " + userAgent); + + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + } + private String getUserAgentFromLastRequest() { SdkHttpRequest lastRequest = mockHttpClient.getLastRequest(); assertThat(lastRequest).isNotNull(); diff --git a/test/crt-unavailable-tests/pom.xml b/test/crt-unavailable-tests/pom.xml index d0c363b871c5..0b2795b929ea 100644 --- a/test/crt-unavailable-tests/pom.xml +++ b/test/crt-unavailable-tests/pom.xml @@ -106,6 +106,12 @@ checksums ${awsjavasdk.version} + + software.amazon.awssdk + service-test-utils + ${awsjavasdk.version} + test + diff --git a/test/crt-unavailable-tests/src/main/resources/codegen-resources/sigv4aonly/service-2.json b/test/crt-unavailable-tests/src/main/resources/codegen-resources/sigv4aonly/service-2.json new file mode 100644 index 000000000000..14b1bda4d718 --- /dev/null +++ b/test/crt-unavailable-tests/src/main/resources/codegen-resources/sigv4aonly/service-2.json @@ -0,0 +1,45 @@ +{ + "version":"2.0", + "metadata":{ + "apiVersion":"2016-03-11", + "endpointPrefix":"internalconfig", + "jsonVersion":"1.1", + "protocol":"rest-json", + "serviceAbbreviation":"AwsSigv4aMultiAuthService", + "serviceFullName":"AWS Multi Auth Service", + "serviceId":"Sigv4aauth", + "targetPrefix":"Sigv4aauth", + "auth":["aws.auth#sigv4a","aws.auth#sigv4"], + "timestampFormat":"unixTimestamp", + "uid":"restjson-2016-03-11" + }, + "operations":{ + "simpleOperationWithNoEndpointParams":{ + "name":"simpleOperationWithNoEndpointParams", + "http":{ + "method":"POST", + "requestUri":"/2016-03-11/simpleOperationWithNoEndpointParams" + }, + "input":{"shape":"SampleRequest"} + }, + "simpleOperationWithEndpointParams":{ + "name":"simpleOperationWithEndpointParams", + "http":{ + "method":"POST", + "requestUri":"/2016-03-11/multiAuthWithOnlySigv4aAndSigv4" + }, + "input":{"shape":"SampleRequest"} + } + }, + "shapes": { + "SampleRequest": { + "type": "structure", + "members": { + "StringMember": { + "shape": "String" + } + } + }, + "String":{"type":"string"} + } +} diff --git a/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java b/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java new file mode 100644 index 000000000000..d5f7f3068e75 --- /dev/null +++ b/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java @@ -0,0 +1,85 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.services; + +import static org.assertj.core.api.Assertions.assertThat; +import static software.amazon.awssdk.core.useragent.BusinessMetricCollection.METRIC_SEARCH_PATTERN; + +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; +import software.amazon.awssdk.http.AbortableInputStream; +import software.amazon.awssdk.http.HttpExecuteResponse; +import software.amazon.awssdk.http.SdkHttpRequest; +import software.amazon.awssdk.http.SdkHttpResponse; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthClient; +import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient; +import software.amazon.awssdk.utils.StringInputStream; + +/** + * Test class to verify that SIGV4A_SIGNING business metric is NOT included + * in the User-Agent header when CRT is not available on the classpath. + */ +class Sigv4aCrtUnavailableBusinessMetricTest { + private static final String USER_AGENT_HEADER_NAME = "User-Agent"; + private static final StaticCredentialsProvider CREDENTIALS_PROVIDER = + StaticCredentialsProvider.create(AwsBasicCredentials.create("akid", "skid")); + + private MockSyncHttpClient mockHttpClient; + + @BeforeEach + public void setup() { + mockHttpClient = new MockSyncHttpClient(); + mockHttpClient.stubNextResponse(mockResponse()); + } + + @Test + void when_crtUnavailable_sigv4aFallsBackToSigv4_noSigv4aMetric() { + + Sigv4AauthClient client = Sigv4AauthClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockHttpClient) + .build(); + + client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); + + String userAgent = getUserAgentFromLastRequest(); + System.out.println("CRT unavailable User-Agent: " + userAgent); + + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + } + + private String getUserAgentFromLastRequest() { + SdkHttpRequest lastRequest = mockHttpClient.getLastRequest(); + assertThat(lastRequest).isNotNull(); + + List userAgentHeaders = lastRequest.headers().get(USER_AGENT_HEADER_NAME); + assertThat(userAgentHeaders).isNotNull().hasSize(1); + return userAgentHeaders.get(0); + } + + private static HttpExecuteResponse mockResponse() { + return HttpExecuteResponse.builder() + .response(SdkHttpResponse.builder().statusCode(200).build()) + .responseBody(AbortableInputStream.create(new StringInputStream("{}"))) + .build(); + } +} From cd2136b9db3a1351a8c2655a3eaa69310517ae90 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Tue, 21 Oct 2025 17:22:07 -0700 Subject: [PATCH 04/14] Additional changes --- .../main/resources/codegen-resources/sigv4aonly/service-2.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json index 0a5178d0c183..11dfe9e7fa08 100644 --- a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json @@ -9,7 +9,7 @@ "serviceFullName":"AWS Multi Auth Service", "serviceId":"Sigv4aauth", "targetPrefix":"Sigv4aauth", - "auth":["aws.auth#sigv4a"], + "auth":["aws.auth#sigv4a","aws.auth#sigv4"], "timestampFormat":"unixTimestamp", "uid":"restjson-2016-03-11" }, From 3d7dab8f868f5d7f3112fbc4532e211f18501671 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Wed, 22 Oct 2025 10:27:47 -0700 Subject: [PATCH 05/14] Fixing test failures --- .../scheme/AuthSchemeInterceptorSpec.java | 5 ++-- ...-sigv4a-value-auth-scheme-interceptor.java | 3 ++- ...ith-allowlist-auth-scheme-interceptor.java | 3 ++- ...out-allowlist-auth-scheme-interceptor.java | 3 ++- .../sigv4aonly/service-2.json | 2 +- .../Sigv4aBusinessMetricUserAgentTest.java | 25 ++++++++----------- ...igv4aCrtUnavailableBusinessMetricTest.java | 4 +-- 7 files changed, 21 insertions(+), 24 deletions(-) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java index 1fead6507881..c4f9e768eaa3 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java @@ -51,6 +51,7 @@ import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; @@ -161,9 +162,9 @@ private MethodSpec generateBeforeExecution() { if (authSchemeSpecUtils.hasSigV4aSupport()) { builder.beginControlFlow("if (selectedAuthScheme != null && " - + "selectedAuthScheme.authSchemeOption().schemeId().equals($S) && " + + "selectedAuthScheme.authSchemeOption().schemeId().equals($T.SCHEME_ID) && " + "!$T.isSignerOverridden(context.request(), executionAttributes))", - "aws.auth#sigv4a", + AwsV4aAuthScheme.class, ClassName.get("software.amazon.awssdk.awscore.util", "SignerOverrideUtils")) .addStatement("$T businessMetrics = executionAttributes.getAttribute($T.BUSINESS_METRICS)", ClassName.get("software.amazon.awssdk.core.useragent", "BusinessMetricCollection"), diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java index 3e469aaac9e7..39c345eeb7b9 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java @@ -23,6 +23,7 @@ import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.core.useragent.BusinessMetricCollection; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; @@ -52,7 +53,7 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); - if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a") + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { BusinessMetricCollection businessMetrics = executionAttributes .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java index 147a209712ab..a17b61f81f53 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java @@ -24,6 +24,7 @@ import software.amazon.awssdk.core.useragent.BusinessMetricCollection; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; @@ -55,7 +56,7 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); - if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a") + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { BusinessMetricCollection businessMetrics = executionAttributes .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java index 637dbc6177ae..caef3f2c59ca 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java @@ -24,6 +24,7 @@ import software.amazon.awssdk.core.useragent.BusinessMetricCollection; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; @@ -55,7 +56,7 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); - if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals("aws.auth#sigv4a") + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { BusinessMetricCollection businessMetrics = executionAttributes .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); diff --git a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json index 11dfe9e7fa08..0a5178d0c183 100644 --- a/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json +++ b/test/codegen-generated-classes-test/src/main/resources/codegen-resources/sigv4aonly/service-2.json @@ -9,7 +9,7 @@ "serviceFullName":"AWS Multi Auth Service", "serviceId":"Sigv4aauth", "targetPrefix":"Sigv4aauth", - "auth":["aws.auth#sigv4a","aws.auth#sigv4"], + "auth":["aws.auth#sigv4a"], "timestampFormat":"unixTimestamp", "uid":"restjson-2016-03-11" }, diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java index 8a4b8ade3bda..9a56da764ed5 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java @@ -26,17 +26,17 @@ import software.amazon.awssdk.auth.signer.Aws4Signer; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.http.HttpExecuteResponse; import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.SdkHttpResponse; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.sigv4aauth.auth.scheme.Sigv4AauthAuthSchemeProvider; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonAsyncClient; import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonClient; import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthAsyncClient; import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthClient; -import software.amazon.awssdk.services.sigv4aauth.auth.scheme.Sigv4AauthAuthSchemeProvider; import software.amazon.awssdk.testutils.service.http.MockAsyncHttpClient; import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient; import software.amazon.awssdk.utils.StringInputStream; @@ -75,22 +75,22 @@ void when_sigv4aServiceIsUsed_correctMetricIsAdded() { String userAgent = getUserAgentFromLastRequest(); System.out.println("SigV4a service User-Agent: " + userAgent); - assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply("S")); } @Test void when_sigv4aServiceIsUsedAsync_correctMetricIsAdded() { Sigv4AauthAsyncClient asyncClient = Sigv4AauthAsyncClient.builder() - .region(Region.US_WEST_2) - .credentialsProvider(CREDENTIALS_PROVIDER) - .httpClient(mockAsyncHttpClient) - .build(); + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockAsyncHttpClient) + .build(); asyncClient.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")).join(); String userAgent = getUserAgentFromLastAsyncRequest(); System.out.println("SigV4a async service User-Agent: " + userAgent); - assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply("S")); } @Test @@ -105,7 +105,7 @@ void when_regularServiceIsUsed_sigv4aMetricIsNotAdded() { String userAgent = getUserAgentFromLastRequest(); System.out.println("Regular service User-Agent: " + userAgent); - assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("S")); } @Test @@ -120,7 +120,7 @@ void when_regularServiceIsUsedAsync_sigv4aMetricIsNotAdded() { String userAgent = getUserAgentFromLastAsyncRequest(); System.out.println("Regular async service User-Agent: " + userAgent); - assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("S")); } @Test @@ -135,14 +135,11 @@ void when_signerIsOverridden_sigv4aMetricIsNotAdded() { .build(); client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); - String userAgent = getUserAgentFromLastRequest(); - System.out.println("Signer override User-Agent: " + userAgent); assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); } - @Test void when_authSchemeProviderOverridesSigv4aOrder_sigv4IsSelected() { Sigv4AauthClient client = Sigv4AauthClient.builder() @@ -155,9 +152,7 @@ void when_authSchemeProviderOverridesSigv4aOrder_sigv4IsSelected() { .build(); client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); - String userAgent = getUserAgentFromLastRequest(); - System.out.println("User-Agent: " + userAgent); assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); } diff --git a/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java b/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java index d5f7f3068e75..7c2abc754cad 100644 --- a/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java +++ b/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java @@ -23,7 +23,6 @@ import org.junit.jupiter.api.Test; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; -import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.http.AbortableInputStream; import software.amazon.awssdk.http.HttpExecuteResponse; import software.amazon.awssdk.http.SdkHttpRequest; @@ -62,9 +61,8 @@ void when_crtUnavailable_sigv4aFallsBackToSigv4_noSigv4aMetric() { client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); String userAgent = getUserAgentFromLastRequest(); - System.out.println("CRT unavailable User-Agent: " + userAgent); - assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("S")); } private String getUserAgentFromLastRequest() { From bd7a7a63f8925a8e5f23c6c9885889bf207c36d9 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Wed, 22 Oct 2025 11:16:41 -0700 Subject: [PATCH 06/14] Fixing test failures --- .../Sigv4aBusinessMetricUserAgentTest.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java index 9a56da764ed5..935c042955b1 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java @@ -31,6 +31,8 @@ import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.SdkHttpResponse; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.multiauth.MultiauthClient; +import software.amazon.awssdk.services.multiauth.auth.scheme.MultiauthAuthSchemeProvider; import software.amazon.awssdk.services.sigv4aauth.auth.scheme.Sigv4AauthAuthSchemeProvider; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonAsyncClient; @@ -125,16 +127,16 @@ void when_regularServiceIsUsedAsync_sigv4aMetricIsNotAdded() { @Test void when_signerIsOverridden_sigv4aMetricIsNotAdded() { - Sigv4AauthClient client = Sigv4AauthClient.builder() - .region(Region.US_WEST_2) - .credentialsProvider(CREDENTIALS_PROVIDER) - .overrideConfiguration(ClientOverrideConfiguration.builder() + MultiauthClient client = MultiauthClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .overrideConfiguration(ClientOverrideConfiguration.builder() .putAdvancedOption(SdkAdvancedClientOption.SIGNER, Aws4Signer.create()) .build()) - .httpClient(mockHttpClient) - .build(); + .httpClient(mockHttpClient) + .build(); - client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); + client.multiAuthWithOnlySigv4aAndSigv4(r -> r.stringMember("test")); String userAgent = getUserAgentFromLastRequest(); assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); @@ -142,16 +144,16 @@ void when_signerIsOverridden_sigv4aMetricIsNotAdded() { @Test void when_authSchemeProviderOverridesSigv4aOrder_sigv4IsSelected() { - Sigv4AauthClient client = Sigv4AauthClient.builder() + MultiauthClient client = MultiauthClient.builder() .region(Region.US_WEST_2) .credentialsProvider(CREDENTIALS_PROVIDER) - .authSchemeProvider(Sigv4AauthAuthSchemeProvider. + .authSchemeProvider(MultiauthAuthSchemeProvider. defaultProvider( Arrays.asList("sigv4","sigv4a"))) .httpClient(mockHttpClient) .build(); - client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); + client.multiAuthWithOnlySigv4aAndSigv4(r -> r.stringMember("test")); String userAgent = getUserAgentFromLastRequest(); assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); From f9ded2ecf8dc888e86d600a65be7c6e185b9eca4 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Wed, 22 Oct 2025 11:18:59 -0700 Subject: [PATCH 07/14] Fix test failure --- .../services/Sigv4aBusinessMetricUserAgentTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java index 935c042955b1..bbc568651d7d 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java @@ -33,8 +33,6 @@ import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.multiauth.MultiauthClient; import software.amazon.awssdk.services.multiauth.auth.scheme.MultiauthAuthSchemeProvider; -import software.amazon.awssdk.services.sigv4aauth.auth.scheme.Sigv4AauthAuthSchemeProvider; -import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonAsyncClient; import software.amazon.awssdk.services.protocolrestjson.ProtocolRestJsonClient; import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthAsyncClient; @@ -131,7 +129,8 @@ void when_signerIsOverridden_sigv4aMetricIsNotAdded() { .region(Region.US_WEST_2) .credentialsProvider(CREDENTIALS_PROVIDER) .overrideConfiguration(ClientOverrideConfiguration.builder() - .putAdvancedOption(SdkAdvancedClientOption.SIGNER, Aws4Signer.create()) + .putAdvancedOption(SdkAdvancedClientOption.SIGNER, + Aws4Signer.create()) .build()) .httpClient(mockHttpClient) .build(); @@ -139,7 +138,7 @@ void when_signerIsOverridden_sigv4aMetricIsNotAdded() { client.multiAuthWithOnlySigv4aAndSigv4(r -> r.stringMember("test")); String userAgent = getUserAgentFromLastRequest(); - assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("S")); } @Test @@ -156,7 +155,7 @@ void when_authSchemeProviderOverridesSigv4aOrder_sigv4IsSelected() { client.multiAuthWithOnlySigv4aAndSigv4(r -> r.stringMember("test")); String userAgent = getUserAgentFromLastRequest(); - assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply(BusinessMetricFeatureId.SIGV4A_SIGNING.value())); + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("S")); } private String getUserAgentFromLastRequest() { From db8c797d59e8cf885fc139c70bc5e06eeb834033 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Wed, 22 Oct 2025 11:45:15 -0700 Subject: [PATCH 08/14] Additional changes in test: Removing print statements Renaming few files --- .../Sigv4aBusinessMetricUserAgentTest.java | 4 ---- .../{sigv4aonly => multiauth}/service-2.json | 6 +++--- .../Sigv4aCrtUnavailableBusinessMetricTest.java | 14 +++++++------- 3 files changed, 10 insertions(+), 14 deletions(-) rename test/crt-unavailable-tests/src/main/resources/codegen-resources/{sigv4aonly => multiauth}/service-2.json (90%) diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java index bbc568651d7d..cd7239358536 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/Sigv4aBusinessMetricUserAgentTest.java @@ -74,7 +74,6 @@ void when_sigv4aServiceIsUsed_correctMetricIsAdded() { client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); String userAgent = getUserAgentFromLastRequest(); - System.out.println("SigV4a service User-Agent: " + userAgent); assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply("S")); } @@ -89,7 +88,6 @@ void when_sigv4aServiceIsUsedAsync_correctMetricIsAdded() { asyncClient.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")).join(); String userAgent = getUserAgentFromLastAsyncRequest(); - System.out.println("SigV4a async service User-Agent: " + userAgent); assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply("S")); } @@ -104,7 +102,6 @@ void when_regularServiceIsUsed_sigv4aMetricIsNotAdded() { client.allTypes(r -> {}); String userAgent = getUserAgentFromLastRequest(); - System.out.println("Regular service User-Agent: " + userAgent); assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("S")); } @@ -119,7 +116,6 @@ void when_regularServiceIsUsedAsync_sigv4aMetricIsNotAdded() { asyncClient.allTypes(r -> {}).join(); String userAgent = getUserAgentFromLastAsyncRequest(); - System.out.println("Regular async service User-Agent: " + userAgent); assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("S")); } diff --git a/test/crt-unavailable-tests/src/main/resources/codegen-resources/sigv4aonly/service-2.json b/test/crt-unavailable-tests/src/main/resources/codegen-resources/multiauth/service-2.json similarity index 90% rename from test/crt-unavailable-tests/src/main/resources/codegen-resources/sigv4aonly/service-2.json rename to test/crt-unavailable-tests/src/main/resources/codegen-resources/multiauth/service-2.json index 14b1bda4d718..4fd8c0c18d8b 100644 --- a/test/crt-unavailable-tests/src/main/resources/codegen-resources/sigv4aonly/service-2.json +++ b/test/crt-unavailable-tests/src/main/resources/codegen-resources/multiauth/service-2.json @@ -5,10 +5,10 @@ "endpointPrefix":"internalconfig", "jsonVersion":"1.1", "protocol":"rest-json", - "serviceAbbreviation":"AwsSigv4aMultiAuthService", + "serviceAbbreviation":"AwsMultiAuthService", "serviceFullName":"AWS Multi Auth Service", - "serviceId":"Sigv4aauth", - "targetPrefix":"Sigv4aauth", + "serviceId":"Multiauth", + "targetPrefix":"MultiAuth", "auth":["aws.auth#sigv4a","aws.auth#sigv4"], "timestampFormat":"unixTimestamp", "uid":"restjson-2016-03-11" diff --git a/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java b/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java index 7c2abc754cad..02e3c2e11340 100644 --- a/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java +++ b/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java @@ -28,7 +28,7 @@ import software.amazon.awssdk.http.SdkHttpRequest; import software.amazon.awssdk.http.SdkHttpResponse; import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.services.sigv4aauth.Sigv4AauthClient; +import software.amazon.awssdk.services.multiauth.MultiauthClient; import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient; import software.amazon.awssdk.utils.StringInputStream; @@ -51,12 +51,12 @@ public void setup() { @Test void when_crtUnavailable_sigv4aFallsBackToSigv4_noSigv4aMetric() { - - Sigv4AauthClient client = Sigv4AauthClient.builder() - .region(Region.US_WEST_2) - .credentialsProvider(CREDENTIALS_PROVIDER) - .httpClient(mockHttpClient) - .build(); + + MultiauthClient client = MultiauthClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockHttpClient) + .build(); client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); From 57016a0d30415f8923d4bf266ac345a974b87387 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Wed, 22 Oct 2025 14:25:01 -0700 Subject: [PATCH 09/14] Address review comments --- .../scheme/AuthSchemeInterceptorSpec.java | 16 ------- ...-sigv4a-value-auth-scheme-interceptor.java | 12 ----- ...ith-allowlist-auth-scheme-interceptor.java | 12 ----- ...out-allowlist-auth-scheme-interceptor.java | 12 ----- .../pipeline/stages/ApplyUserAgentStage.java | 46 +++++++++++++++---- 5 files changed, 38 insertions(+), 60 deletions(-) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java index c4f9e768eaa3..40a92b1fe394 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java @@ -159,22 +159,6 @@ private MethodSpec generateBeforeExecution() { builder.addStatement("recordEnvironmentTokenBusinessMetric(selectedAuthScheme, " + "executionAttributes)"); } - - if (authSchemeSpecUtils.hasSigV4aSupport()) { - builder.beginControlFlow("if (selectedAuthScheme != null && " - + "selectedAuthScheme.authSchemeOption().schemeId().equals($T.SCHEME_ID) && " - + "!$T.isSignerOverridden(context.request(), executionAttributes))", - AwsV4aAuthScheme.class, - ClassName.get("software.amazon.awssdk.awscore.util", "SignerOverrideUtils")) - .addStatement("$T businessMetrics = executionAttributes.getAttribute($T.BUSINESS_METRICS)", - ClassName.get("software.amazon.awssdk.core.useragent", "BusinessMetricCollection"), - SdkInternalExecutionAttribute.class) - .beginControlFlow("if (businessMetrics != null)") - .addStatement("businessMetrics.addMetric($T.SIGV4A_SIGNING.value())", - BusinessMetricFeatureId.class) - .endControlFlow() - .endControlFlow(); - } return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java index 39c345eeb7b9..70b901338096 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java @@ -10,7 +10,6 @@ import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; -import software.amazon.awssdk.awscore.util.SignerOverrideUtils; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.exception.SdkException; @@ -21,9 +20,6 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; -import software.amazon.awssdk.core.useragent.BusinessMetricCollection; -import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; -import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; @@ -53,14 +49,6 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); - if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) - && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { - BusinessMetricCollection businessMetrics = executionAttributes - .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); - if (businessMetrics != null) { - businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); - } - } } private List resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java index a17b61f81f53..1edb692058fe 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java @@ -10,7 +10,6 @@ import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; -import software.amazon.awssdk.awscore.util.SignerOverrideUtils; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.exception.SdkException; @@ -21,10 +20,7 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; -import software.amazon.awssdk.core.useragent.BusinessMetricCollection; -import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; -import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; @@ -56,14 +52,6 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); - if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) - && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { - BusinessMetricCollection businessMetrics = executionAttributes - .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); - if (businessMetrics != null) { - businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); - } - } } private List resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java index caef3f2c59ca..e9405f2fbb67 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java @@ -10,7 +10,6 @@ import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; -import software.amazon.awssdk.awscore.util.SignerOverrideUtils; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.exception.SdkException; @@ -21,10 +20,7 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; -import software.amazon.awssdk.core.useragent.BusinessMetricCollection; -import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; -import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; @@ -56,14 +52,6 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); - if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) - && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { - BusinessMetricCollection businessMetrics = executionAttributes - .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); - if (businessMetrics != null) { - businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); - } - } } private List resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java index 6cbb536ad43b..7e7085861ac0 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java @@ -28,17 +28,23 @@ import java.util.Optional; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.core.ApiName; +import software.amazon.awssdk.core.RequestOverrideConfiguration; +import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.http.HttpClientDependencies; import software.amazon.awssdk.core.internal.http.RequestExecutionContext; import software.amazon.awssdk.core.internal.http.pipeline.MutableRequestToRequestPipeline; +import software.amazon.awssdk.core.signer.Signer; import software.amazon.awssdk.core.useragent.AdditionalMetadata; import software.amazon.awssdk.core.useragent.BusinessMetricCollection; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.identity.spi.Identity; import software.amazon.awssdk.utils.CompletableFutureUtils; import software.amazon.awssdk.utils.Logger; @@ -112,7 +118,8 @@ private String finalizeUserAgent(RequestExecutionContext context) { userAgentMetadata.forEach(s -> javaUserAgent.append(SPACE).append(s)); } - Optional businessMetrics = getBusinessMetricsString(context.executionAttributes(), groupedApiNames.right()); + Optional businessMetrics = getBusinessMetricsString(context.executionAttributes(), + groupedApiNames.right(), context); businessMetrics.ifPresent( metrics -> appendSpaceAndField(javaUserAgent, BUSINESS_METADATA, metrics) ); @@ -143,7 +150,8 @@ private static Pair, Collection> groupApiNames(List getBusinessMetricsString(ExecutionAttributes executionAttributes, - Collection metricsFromApiNames) { + Collection metricsFromApiNames, + RequestExecutionContext context) { BusinessMetricCollection businessMetrics = executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); if (businessMetrics == null) { @@ -151,7 +159,11 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe } businessMetrics.merge(metricsFromApiNames); - credentialProviderBusinessMetrics(executionAttributes).ifPresent(businessMetrics::merge); + SelectedAuthScheme selectedAuthScheme = + executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); + + credentialProviderBusinessMetrics(selectedAuthScheme).ifPresent(businessMetrics::merge); + addSigV4aBusinessMetrics(selectedAuthScheme, businessMetrics, context, executionAttributes); if (businessMetrics.recordedMetrics().isEmpty()) { return Optional.empty(); @@ -161,11 +173,9 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe } private static Optional> credentialProviderBusinessMetrics( - ExecutionAttributes executionAttributes) { - return Optional.ofNullable( - executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME)) - .map(selectedAuthScheme -> - CompletableFutureUtils.joinLikeSync(selectedAuthScheme.identity())) + SelectedAuthScheme selectedAuthScheme) { + return Optional.ofNullable(selectedAuthScheme) + .map(scheme -> CompletableFutureUtils.joinLikeSync(scheme.identity())) .flatMap(Identity::providerName) .map(providerName -> { if (StringUtils.isBlank(providerName)) { @@ -175,6 +185,26 @@ private static Optional> credentialProviderBusinessMetrics( }); } + private static boolean isSignerOverridden(RequestExecutionContext context, ExecutionAttributes executionAttributes) { + boolean isClientSignerOverridden = + Boolean.TRUE.equals(executionAttributes.getAttribute(SdkExecutionAttribute.SIGNER_OVERRIDDEN)); + Optional requestSigner = context.originalRequest().overrideConfiguration() + .flatMap(RequestOverrideConfiguration::signer); + return isClientSignerOverridden || requestSigner.isPresent(); + } + + private static void addSigV4aBusinessMetrics(SelectedAuthScheme selectedAuthScheme, + BusinessMetricCollection businessMetrics, + RequestExecutionContext context, + ExecutionAttributes executionAttributes) { + if (selectedAuthScheme != null && + selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) && + !isSignerOverridden(context, executionAttributes)) { + + businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + } + } + /** * This structure is used for external users as well as for internal tracking of features. * It's not governed by a specification. From 7577da9061b4c73a5bf8ea6e9b6e05abf17159fd Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Wed, 22 Oct 2025 14:27:46 -0700 Subject: [PATCH 10/14] Removing unused import --- .../codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java | 1 - ...point-auth-params-with-allowlist-auth-scheme-interceptor.java | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java index 40a92b1fe394..043bf74ba9d9 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java @@ -51,7 +51,6 @@ import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; -import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java index 1edb692058fe..dcc6418d2e89 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java @@ -102,6 +102,7 @@ private QueryAuthSchemeParams authSchemeParams(SdkRequest request, ExecutionAttr executionAttributes.getOptionalAttribute(AwsExecutionAttribute.AWS_SIGV4A_SIGNING_REGION_SET) .filter(regionSet -> !CollectionUtils.isNullOrEmpty(regionSet)) .ifPresent(nonEmptyRegionSet -> builder.regionSet(RegionSet.create(nonEmptyRegionSet))); + if (builder instanceof QueryEndpointResolverAware.Builder) { EndpointProvider endpointProvider = executionAttributes.getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER); if (endpointProvider instanceof QueryEndpointProvider) { From ed69ac0b314655a0caf8d99844615f7da4c9e71e Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Fri, 24 Oct 2025 14:11:35 -0700 Subject: [PATCH 11/14] Move sigv4a business metric logic to auth interceptor to record at auth scheme selection --- .../scheme/AuthSchemeInterceptorSpec.java | 17 ++++++ ...-sigv4a-value-auth-scheme-interceptor.java | 12 +++++ ...ith-allowlist-auth-scheme-interceptor.java | 13 ++++- ...out-allowlist-auth-scheme-interceptor.java | 12 +++++ .../pipeline/stages/ApplyUserAgentStage.java | 52 ++++--------------- 5 files changed, 64 insertions(+), 42 deletions(-) diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java index 043bf74ba9d9..c4f9e768eaa3 100644 --- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java +++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/auth/scheme/AuthSchemeInterceptorSpec.java @@ -51,6 +51,7 @@ import software.amazon.awssdk.core.metrics.CoreMetric; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.scheme.BearerAuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; @@ -158,6 +159,22 @@ private MethodSpec generateBeforeExecution() { builder.addStatement("recordEnvironmentTokenBusinessMetric(selectedAuthScheme, " + "executionAttributes)"); } + + if (authSchemeSpecUtils.hasSigV4aSupport()) { + builder.beginControlFlow("if (selectedAuthScheme != null && " + + "selectedAuthScheme.authSchemeOption().schemeId().equals($T.SCHEME_ID) && " + + "!$T.isSignerOverridden(context.request(), executionAttributes))", + AwsV4aAuthScheme.class, + ClassName.get("software.amazon.awssdk.awscore.util", "SignerOverrideUtils")) + .addStatement("$T businessMetrics = executionAttributes.getAttribute($T.BUSINESS_METRICS)", + ClassName.get("software.amazon.awssdk.core.useragent", "BusinessMetricCollection"), + SdkInternalExecutionAttribute.class) + .beginControlFlow("if (businessMetrics != null)") + .addStatement("businessMetrics.addMetric($T.SIGV4A_SIGNING.value())", + BusinessMetricFeatureId.class) + .endControlFlow() + .endControlFlow(); + } return builder.build(); } diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java index 70b901338096..39c345eeb7b9 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/ops-auth-sigv4a-value-auth-scheme-interceptor.java @@ -10,6 +10,7 @@ import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.awscore.util.SignerOverrideUtils; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.exception.SdkException; @@ -20,6 +21,9 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricCollection; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; @@ -49,6 +53,14 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) + && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { + BusinessMetricCollection businessMetrics = executionAttributes + .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); + if (businessMetrics != null) { + businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + } + } } private List resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java index dcc6418d2e89..a17b61f81f53 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-with-allowlist-auth-scheme-interceptor.java @@ -10,6 +10,7 @@ import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.awscore.util.SignerOverrideUtils; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.exception.SdkException; @@ -20,7 +21,10 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricCollection; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; @@ -52,6 +56,14 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) + && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { + BusinessMetricCollection businessMetrics = executionAttributes + .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); + if (businessMetrics != null) { + businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + } + } } private List resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { @@ -102,7 +114,6 @@ private QueryAuthSchemeParams authSchemeParams(SdkRequest request, ExecutionAttr executionAttributes.getOptionalAttribute(AwsExecutionAttribute.AWS_SIGV4A_SIGNING_REGION_SET) .filter(regionSet -> !CollectionUtils.isNullOrEmpty(regionSet)) .ifPresent(nonEmptyRegionSet -> builder.regionSet(RegionSet.create(nonEmptyRegionSet))); - if (builder instanceof QueryEndpointResolverAware.Builder) { EndpointProvider endpointProvider = executionAttributes.getAttribute(SdkInternalExecutionAttribute.ENDPOINT_PROVIDER); if (endpointProvider instanceof QueryEndpointProvider) { diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java index e9405f2fbb67..caef3f2c59ca 100644 --- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java +++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/auth/scheme/query-endpoint-auth-params-without-allowlist-auth-scheme-interceptor.java @@ -10,6 +10,7 @@ import software.amazon.awssdk.annotations.Generated; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.awscore.AwsExecutionAttribute; +import software.amazon.awssdk.awscore.util.SignerOverrideUtils; import software.amazon.awssdk.core.SdkRequest; import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.exception.SdkException; @@ -20,7 +21,10 @@ import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.util.MetricUtils; import software.amazon.awssdk.core.metrics.CoreMetric; +import software.amazon.awssdk.core.useragent.BusinessMetricCollection; +import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.endpoints.EndpointProvider; +import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.http.auth.aws.signer.RegionSet; import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme; import software.amazon.awssdk.http.auth.spi.scheme.AuthSchemeOption; @@ -52,6 +56,14 @@ public void beforeExecution(Context.BeforeExecution context, ExecutionAttributes List authOptions = resolveAuthOptions(context, executionAttributes); SelectedAuthScheme selectedAuthScheme = selectAuthScheme(authOptions, executionAttributes); putSelectedAuthScheme(executionAttributes, selectedAuthScheme); + if (selectedAuthScheme != null && selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) + && !SignerOverrideUtils.isSignerOverridden(context.request(), executionAttributes)) { + BusinessMetricCollection businessMetrics = executionAttributes + .getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); + if (businessMetrics != null) { + businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); + } + } } private List resolveAuthOptions(Context.BeforeExecution context, ExecutionAttributes executionAttributes) { diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java index 7e7085861ac0..583634c7288c 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java @@ -28,23 +28,17 @@ import java.util.Optional; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.core.ApiName; -import software.amazon.awssdk.core.RequestOverrideConfiguration; -import software.amazon.awssdk.core.SelectedAuthScheme; import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; import software.amazon.awssdk.core.client.config.SdkClientConfiguration; import software.amazon.awssdk.core.client.config.SdkClientOption; import software.amazon.awssdk.core.interceptor.ExecutionAttributes; -import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; import software.amazon.awssdk.core.internal.http.HttpClientDependencies; import software.amazon.awssdk.core.internal.http.RequestExecutionContext; import software.amazon.awssdk.core.internal.http.pipeline.MutableRequestToRequestPipeline; -import software.amazon.awssdk.core.signer.Signer; import software.amazon.awssdk.core.useragent.AdditionalMetadata; import software.amazon.awssdk.core.useragent.BusinessMetricCollection; -import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; import software.amazon.awssdk.http.SdkHttpFullRequest; -import software.amazon.awssdk.http.auth.aws.scheme.AwsV4aAuthScheme; import software.amazon.awssdk.identity.spi.Identity; import software.amazon.awssdk.utils.CompletableFutureUtils; import software.amazon.awssdk.utils.Logger; @@ -53,7 +47,7 @@ /** * A stage for adding the user agent header to the request, after retrieving the current string - * from execution attributes and adding any additional information. + * from execution attributes and adding any additional information. */ @SdkInternalApi public class ApplyUserAgentStage implements MutableRequestToRequestPipeline { @@ -118,8 +112,7 @@ private String finalizeUserAgent(RequestExecutionContext context) { userAgentMetadata.forEach(s -> javaUserAgent.append(SPACE).append(s)); } - Optional businessMetrics = getBusinessMetricsString(context.executionAttributes(), - groupedApiNames.right(), context); + Optional businessMetrics = getBusinessMetricsString(context.executionAttributes(), groupedApiNames.right()); businessMetrics.ifPresent( metrics -> appendSpaceAndField(javaUserAgent, BUSINESS_METADATA, metrics) ); @@ -150,8 +143,7 @@ private static Pair, Collection> groupApiNames(List getBusinessMetricsString(ExecutionAttributes executionAttributes, - Collection metricsFromApiNames, - RequestExecutionContext context) { + Collection metricsFromApiNames) { BusinessMetricCollection businessMetrics = executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); if (businessMetrics == null) { @@ -159,23 +151,21 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe } businessMetrics.merge(metricsFromApiNames); - SelectedAuthScheme selectedAuthScheme = - executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME); + credentialProviderBusinessMetrics(executionAttributes).ifPresent(businessMetrics::merge); - credentialProviderBusinessMetrics(selectedAuthScheme).ifPresent(businessMetrics::merge); - addSigV4aBusinessMetrics(selectedAuthScheme, businessMetrics, context, executionAttributes); - if (businessMetrics.recordedMetrics().isEmpty()) { return Optional.empty(); } - + return Optional.of(businessMetrics.asBoundedString()); } private static Optional> credentialProviderBusinessMetrics( - SelectedAuthScheme selectedAuthScheme) { - return Optional.ofNullable(selectedAuthScheme) - .map(scheme -> CompletableFutureUtils.joinLikeSync(scheme.identity())) + ExecutionAttributes executionAttributes) { + return Optional.ofNullable( + executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME)) + .map(selectedAuthScheme -> + CompletableFutureUtils.joinLikeSync(selectedAuthScheme.identity())) .flatMap(Identity::providerName) .map(providerName -> { if (StringUtils.isBlank(providerName)) { @@ -185,26 +175,6 @@ private static Optional> credentialProviderBusinessMetrics( }); } - private static boolean isSignerOverridden(RequestExecutionContext context, ExecutionAttributes executionAttributes) { - boolean isClientSignerOverridden = - Boolean.TRUE.equals(executionAttributes.getAttribute(SdkExecutionAttribute.SIGNER_OVERRIDDEN)); - Optional requestSigner = context.originalRequest().overrideConfiguration() - .flatMap(RequestOverrideConfiguration::signer); - return isClientSignerOverridden || requestSigner.isPresent(); - } - - private static void addSigV4aBusinessMetrics(SelectedAuthScheme selectedAuthScheme, - BusinessMetricCollection businessMetrics, - RequestExecutionContext context, - ExecutionAttributes executionAttributes) { - if (selectedAuthScheme != null && - selectedAuthScheme.authSchemeOption().schemeId().equals(AwsV4aAuthScheme.SCHEME_ID) && - !isSignerOverridden(context, executionAttributes)) { - - businessMetrics.addMetric(BusinessMetricFeatureId.SIGV4A_SIGNING.value()); - } - } - /** * This structure is used for external users as well as for internal tracking of features. * It's not governed by a specification. @@ -225,4 +195,4 @@ private Optional requestApiNames(List requestApiNames) { .append(apiName.version())); return Optional.of(concatenatedNames.toString()); } -} +} \ No newline at end of file From b69ebe7f6e99884b25439a175912df1022e578e0 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Fri, 24 Oct 2025 14:12:59 -0700 Subject: [PATCH 12/14] Remove changes from ApplyUserAgentStage --- .../pipeline/stages/ApplyUserAgentStage.java | 198 ------------------ 1 file changed, 198 deletions(-) delete mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java deleted file mode 100644 index 583634c7288c..000000000000 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package software.amazon.awssdk.core.internal.http.pipeline.stages; - -import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.BUSINESS_METADATA; -import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.SLASH; -import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.SPACE; -import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.appendSpaceAndField; -import static software.amazon.awssdk.utils.StringUtils.trim; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import software.amazon.awssdk.annotations.SdkInternalApi; -import software.amazon.awssdk.core.ApiName; -import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; -import software.amazon.awssdk.core.client.config.SdkClientConfiguration; -import software.amazon.awssdk.core.client.config.SdkClientOption; -import software.amazon.awssdk.core.interceptor.ExecutionAttributes; -import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; -import software.amazon.awssdk.core.internal.http.HttpClientDependencies; -import software.amazon.awssdk.core.internal.http.RequestExecutionContext; -import software.amazon.awssdk.core.internal.http.pipeline.MutableRequestToRequestPipeline; -import software.amazon.awssdk.core.useragent.AdditionalMetadata; -import software.amazon.awssdk.core.useragent.BusinessMetricCollection; -import software.amazon.awssdk.http.SdkHttpFullRequest; -import software.amazon.awssdk.identity.spi.Identity; -import software.amazon.awssdk.utils.CompletableFutureUtils; -import software.amazon.awssdk.utils.Logger; -import software.amazon.awssdk.utils.Pair; -import software.amazon.awssdk.utils.StringUtils; - -/** - * A stage for adding the user agent header to the request, after retrieving the current string - * from execution attributes and adding any additional information. - */ -@SdkInternalApi -public class ApplyUserAgentStage implements MutableRequestToRequestPipeline { - - public static final String HEADER_USER_AGENT = "User-Agent"; - public static final String SDK_METRICS = "sdk-metrics"; - - private static final Logger log = Logger.loggerFor(ApplyUserAgentStage.class); - - private final SdkClientConfiguration clientConfig; - - public ApplyUserAgentStage(HttpClientDependencies dependencies) { - this.clientConfig = dependencies.clientConfiguration(); - } - - @Override - public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, - RequestExecutionContext context) throws Exception { - String headerValue = finalizeUserAgent(context); - return request.putHeader(HEADER_USER_AGENT, headerValue); - } - - /** - * The final value sent in the user agent header consists of - *
    - *
  1. an optional user provided prefix
  2. - *
  3. SDK user agent values (governed by a common specification)
  4. - *
  5. an optional set of API names, expressed as name/version pairs
  6. - *
  7. an optional user provided suffix
  8. - *
- *

- * In general, usage of the optional values is discouraged since they do not follow a specification and can make - * the user agent too long. - *

- * The SDK user agent values are constructed from static system values, client level values and request level - * values. This method adds request level values directly after the retrieved SDK client user agent string. - */ - private String finalizeUserAgent(RequestExecutionContext context) { - String clientUserAgent = clientConfig.option(SdkClientOption.CLIENT_USER_AGENT); - if (clientUserAgent == null) { - log.warn(() -> "Client user agent configuration is missing, so request user agent will be incomplete."); - clientUserAgent = ""; - } - - //separate apiNames into opaque customer added values and known values added internally as metrics - Pair, Collection> groupedApiNames = groupApiNames(context.requestConfig().apiNames()); - - //create builder for the user agent string - StringBuilder javaUserAgent = new StringBuilder(); - - String userPrefix = trim(clientConfig.option(SdkAdvancedClientOption.USER_AGENT_PREFIX)); - if (!StringUtils.isEmpty(userPrefix)) { - javaUserAgent.append(userPrefix).append(SPACE); - } - - javaUserAgent.append(clientUserAgent); - - //add useragent metadata from execution context - List userAgentMetadata = - context.executionAttributes().getAttribute(SdkInternalExecutionAttribute.USER_AGENT_METADATA); - if (userAgentMetadata != null) { - userAgentMetadata.forEach(s -> javaUserAgent.append(SPACE).append(s)); - } - - Optional businessMetrics = getBusinessMetricsString(context.executionAttributes(), groupedApiNames.right()); - businessMetrics.ifPresent( - metrics -> appendSpaceAndField(javaUserAgent, BUSINESS_METADATA, metrics) - ); - - //Any ApiName value that isn't known is added to the end of the user agent - Optional apiNames = requestApiNames(groupedApiNames.left()); - apiNames.ifPresent(javaUserAgent::append); - - String userSuffix = trim(clientConfig.option(SdkAdvancedClientOption.USER_AGENT_SUFFIX)); - if (!StringUtils.isEmpty(userSuffix)) { - javaUserAgent.append(SPACE).append(userSuffix); - } - - return javaUserAgent.toString(); - } - - private static Pair, Collection> groupApiNames(List input) { - List customApiNames = new ArrayList<>(); - Collection metricsFromApiNames = new ArrayList<>(); - for (ApiName requestApiName : input) { - if (requestApiName.name().equals(SDK_METRICS)) { - metricsFromApiNames.add(requestApiName.version()); - } else { - customApiNames.add(requestApiName); - } - } - return Pair.of(customApiNames, metricsFromApiNames); - } - - private static Optional getBusinessMetricsString(ExecutionAttributes executionAttributes, - Collection metricsFromApiNames) { - BusinessMetricCollection businessMetrics = - executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); - if (businessMetrics == null) { - businessMetrics = new BusinessMetricCollection(); - } - businessMetrics.merge(metricsFromApiNames); - - credentialProviderBusinessMetrics(executionAttributes).ifPresent(businessMetrics::merge); - - if (businessMetrics.recordedMetrics().isEmpty()) { - return Optional.empty(); - } - - return Optional.of(businessMetrics.asBoundedString()); - } - - private static Optional> credentialProviderBusinessMetrics( - ExecutionAttributes executionAttributes) { - return Optional.ofNullable( - executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME)) - .map(selectedAuthScheme -> - CompletableFutureUtils.joinLikeSync(selectedAuthScheme.identity())) - .flatMap(Identity::providerName) - .map(providerName -> { - if (StringUtils.isBlank(providerName)) { - return Collections.emptyList(); - } - return Collections.singletonList(providerName); - }); - } - - /** - * This structure is used for external users as well as for internal tracking of features. - * It's not governed by a specification. - * Internal usage should be migrated to business metrics or another designated metadata field, - * leaving these values to be completely user-set, in which case the result would in most cases be empty. - *

- * Currently tracking these SDK values (remove from list as they're migrated): - * PAGINATED/sdk-version, hll/s3Multipart, hll/ddb-enh, hll/cw-mp, hll/waiter, hll/cross-region, ft/s3-transfer - */ - private Optional requestApiNames(List requestApiNames) { - if (requestApiNames.isEmpty()) { - return Optional.empty(); - } - StringBuilder concatenatedNames = new StringBuilder(); - requestApiNames.forEach(apiName -> concatenatedNames.append(SPACE) - .append(apiName.name()) - .append(SLASH) - .append(apiName.version())); - return Optional.of(concatenatedNames.toString()); - } -} \ No newline at end of file From 059c869307a2281569e5b9ffec4a1e26a505405e Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Fri, 24 Oct 2025 14:16:18 -0700 Subject: [PATCH 13/14] Adding missed file --- .../pipeline/stages/ApplyUserAgentStage.java | 198 ++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java new file mode 100644 index 000000000000..583634c7288c --- /dev/null +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApplyUserAgentStage.java @@ -0,0 +1,198 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package software.amazon.awssdk.core.internal.http.pipeline.stages; + +import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.BUSINESS_METADATA; +import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.SLASH; +import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.SPACE; +import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.appendSpaceAndField; +import static software.amazon.awssdk.utils.StringUtils.trim; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.core.ApiName; +import software.amazon.awssdk.core.client.config.SdkAdvancedClientOption; +import software.amazon.awssdk.core.client.config.SdkClientConfiguration; +import software.amazon.awssdk.core.client.config.SdkClientOption; +import software.amazon.awssdk.core.interceptor.ExecutionAttributes; +import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute; +import software.amazon.awssdk.core.internal.http.HttpClientDependencies; +import software.amazon.awssdk.core.internal.http.RequestExecutionContext; +import software.amazon.awssdk.core.internal.http.pipeline.MutableRequestToRequestPipeline; +import software.amazon.awssdk.core.useragent.AdditionalMetadata; +import software.amazon.awssdk.core.useragent.BusinessMetricCollection; +import software.amazon.awssdk.http.SdkHttpFullRequest; +import software.amazon.awssdk.identity.spi.Identity; +import software.amazon.awssdk.utils.CompletableFutureUtils; +import software.amazon.awssdk.utils.Logger; +import software.amazon.awssdk.utils.Pair; +import software.amazon.awssdk.utils.StringUtils; + +/** + * A stage for adding the user agent header to the request, after retrieving the current string + * from execution attributes and adding any additional information. + */ +@SdkInternalApi +public class ApplyUserAgentStage implements MutableRequestToRequestPipeline { + + public static final String HEADER_USER_AGENT = "User-Agent"; + public static final String SDK_METRICS = "sdk-metrics"; + + private static final Logger log = Logger.loggerFor(ApplyUserAgentStage.class); + + private final SdkClientConfiguration clientConfig; + + public ApplyUserAgentStage(HttpClientDependencies dependencies) { + this.clientConfig = dependencies.clientConfiguration(); + } + + @Override + public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, + RequestExecutionContext context) throws Exception { + String headerValue = finalizeUserAgent(context); + return request.putHeader(HEADER_USER_AGENT, headerValue); + } + + /** + * The final value sent in the user agent header consists of + *

    + *
  1. an optional user provided prefix
  2. + *
  3. SDK user agent values (governed by a common specification)
  4. + *
  5. an optional set of API names, expressed as name/version pairs
  6. + *
  7. an optional user provided suffix
  8. + *
+ *

+ * In general, usage of the optional values is discouraged since they do not follow a specification and can make + * the user agent too long. + *

+ * The SDK user agent values are constructed from static system values, client level values and request level + * values. This method adds request level values directly after the retrieved SDK client user agent string. + */ + private String finalizeUserAgent(RequestExecutionContext context) { + String clientUserAgent = clientConfig.option(SdkClientOption.CLIENT_USER_AGENT); + if (clientUserAgent == null) { + log.warn(() -> "Client user agent configuration is missing, so request user agent will be incomplete."); + clientUserAgent = ""; + } + + //separate apiNames into opaque customer added values and known values added internally as metrics + Pair, Collection> groupedApiNames = groupApiNames(context.requestConfig().apiNames()); + + //create builder for the user agent string + StringBuilder javaUserAgent = new StringBuilder(); + + String userPrefix = trim(clientConfig.option(SdkAdvancedClientOption.USER_AGENT_PREFIX)); + if (!StringUtils.isEmpty(userPrefix)) { + javaUserAgent.append(userPrefix).append(SPACE); + } + + javaUserAgent.append(clientUserAgent); + + //add useragent metadata from execution context + List userAgentMetadata = + context.executionAttributes().getAttribute(SdkInternalExecutionAttribute.USER_AGENT_METADATA); + if (userAgentMetadata != null) { + userAgentMetadata.forEach(s -> javaUserAgent.append(SPACE).append(s)); + } + + Optional businessMetrics = getBusinessMetricsString(context.executionAttributes(), groupedApiNames.right()); + businessMetrics.ifPresent( + metrics -> appendSpaceAndField(javaUserAgent, BUSINESS_METADATA, metrics) + ); + + //Any ApiName value that isn't known is added to the end of the user agent + Optional apiNames = requestApiNames(groupedApiNames.left()); + apiNames.ifPresent(javaUserAgent::append); + + String userSuffix = trim(clientConfig.option(SdkAdvancedClientOption.USER_AGENT_SUFFIX)); + if (!StringUtils.isEmpty(userSuffix)) { + javaUserAgent.append(SPACE).append(userSuffix); + } + + return javaUserAgent.toString(); + } + + private static Pair, Collection> groupApiNames(List input) { + List customApiNames = new ArrayList<>(); + Collection metricsFromApiNames = new ArrayList<>(); + for (ApiName requestApiName : input) { + if (requestApiName.name().equals(SDK_METRICS)) { + metricsFromApiNames.add(requestApiName.version()); + } else { + customApiNames.add(requestApiName); + } + } + return Pair.of(customApiNames, metricsFromApiNames); + } + + private static Optional getBusinessMetricsString(ExecutionAttributes executionAttributes, + Collection metricsFromApiNames) { + BusinessMetricCollection businessMetrics = + executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); + if (businessMetrics == null) { + businessMetrics = new BusinessMetricCollection(); + } + businessMetrics.merge(metricsFromApiNames); + + credentialProviderBusinessMetrics(executionAttributes).ifPresent(businessMetrics::merge); + + if (businessMetrics.recordedMetrics().isEmpty()) { + return Optional.empty(); + } + + return Optional.of(businessMetrics.asBoundedString()); + } + + private static Optional> credentialProviderBusinessMetrics( + ExecutionAttributes executionAttributes) { + return Optional.ofNullable( + executionAttributes.getAttribute(SdkInternalExecutionAttribute.SELECTED_AUTH_SCHEME)) + .map(selectedAuthScheme -> + CompletableFutureUtils.joinLikeSync(selectedAuthScheme.identity())) + .flatMap(Identity::providerName) + .map(providerName -> { + if (StringUtils.isBlank(providerName)) { + return Collections.emptyList(); + } + return Collections.singletonList(providerName); + }); + } + + /** + * This structure is used for external users as well as for internal tracking of features. + * It's not governed by a specification. + * Internal usage should be migrated to business metrics or another designated metadata field, + * leaving these values to be completely user-set, in which case the result would in most cases be empty. + *

+ * Currently tracking these SDK values (remove from list as they're migrated): + * PAGINATED/sdk-version, hll/s3Multipart, hll/ddb-enh, hll/cw-mp, hll/waiter, hll/cross-region, ft/s3-transfer + */ + private Optional requestApiNames(List requestApiNames) { + if (requestApiNames.isEmpty()) { + return Optional.empty(); + } + StringBuilder concatenatedNames = new StringBuilder(); + requestApiNames.forEach(apiName -> concatenatedNames.append(SPACE) + .append(apiName.name()) + .append(SLASH) + .append(apiName.version())); + return Optional.of(concatenatedNames.toString()); + } +} \ No newline at end of file From ca6352ba300b1bab1bcbbf2697d01b733f62c135 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Fri, 24 Oct 2025 21:36:26 -0700 Subject: [PATCH 14/14] Address review comments --- .../codegen-resources/multiauth/service-2.json | 14 +++----------- .../Sigv4aCrtUnavailableBusinessMetricTest.java | 2 +- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/test/crt-unavailable-tests/src/main/resources/codegen-resources/multiauth/service-2.json b/test/crt-unavailable-tests/src/main/resources/codegen-resources/multiauth/service-2.json index 4fd8c0c18d8b..3777e4192603 100644 --- a/test/crt-unavailable-tests/src/main/resources/codegen-resources/multiauth/service-2.json +++ b/test/crt-unavailable-tests/src/main/resources/codegen-resources/multiauth/service-2.json @@ -14,19 +14,11 @@ "uid":"restjson-2016-03-11" }, "operations":{ - "simpleOperationWithNoEndpointParams":{ - "name":"simpleOperationWithNoEndpointParams", + "operationWithBothSigv4AndSigv4a":{ + "name":"operationWithBothSigv4AndSigv4a", "http":{ "method":"POST", - "requestUri":"/2016-03-11/simpleOperationWithNoEndpointParams" - }, - "input":{"shape":"SampleRequest"} - }, - "simpleOperationWithEndpointParams":{ - "name":"simpleOperationWithEndpointParams", - "http":{ - "method":"POST", - "requestUri":"/2016-03-11/multiAuthWithOnlySigv4aAndSigv4" + "requestUri":"/2016-03-11/operationWithBothSigv4AndSigv4a" }, "input":{"shape":"SampleRequest"} } diff --git a/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java b/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java index 02e3c2e11340..72d150f07948 100644 --- a/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java +++ b/test/crt-unavailable-tests/src/test/java/software/amazon/awssdk/services/Sigv4aCrtUnavailableBusinessMetricTest.java @@ -58,7 +58,7 @@ void when_crtUnavailable_sigv4aFallsBackToSigv4_noSigv4aMetric() { .httpClient(mockHttpClient) .build(); - client.simpleOperationWithNoEndpointParams(r -> r.stringMember("test")); + client.operationWithBothSigv4AndSigv4a(r -> r.stringMember("test")); String userAgent = getUserAgentFromLastRequest();