From a47f90d830dd379f449bbe36b5046f549d4025ba Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Wed, 22 Oct 2025 15:46:38 -0700 Subject: [PATCH 1/7] Add business metric support for all flexible checksums --- .../pipeline/stages/ApplyUserAgentStage.java | 19 ++ .../useragent/BusinessMetricsUtils.java | 58 +++++ .../useragent/BusinessMetricFeatureId.java | 11 +- .../FlexibleChecksumBusinessMetricTest.java | 218 ++++++++++++++++++ 4 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/FlexibleChecksumBusinessMetricTest.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 index 6cbb536ad43b..6106fb895863 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 @@ -32,10 +32,12 @@ 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.internal.useragent.BusinessMetricsUtils; import software.amazon.awssdk.core.useragent.AdditionalMetadata; import software.amazon.awssdk.core.useragent.BusinessMetricCollection; import software.amazon.awssdk.http.SdkHttpFullRequest; @@ -151,6 +153,8 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe } businessMetrics.merge(metricsFromApiNames); + ChecksumBusinessMetrics(executionAttributes, businessMetrics); + credentialProviderBusinessMetrics(executionAttributes).ifPresent(businessMetrics::merge); if (businessMetrics.recordedMetrics().isEmpty()) { @@ -160,6 +164,21 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe return Optional.of(businessMetrics.asBoundedString()); } + private static void ChecksumBusinessMetrics(ExecutionAttributes executionAttributes, + BusinessMetricCollection businessMetrics) { + BusinessMetricsUtils.resolveRequestChecksumCalculationMetric( + executionAttributes.getAttribute(SdkInternalExecutionAttribute.REQUEST_CHECKSUM_CALCULATION)) + .ifPresent(businessMetrics::addMetric); + + BusinessMetricsUtils.resolveResponseChecksumValidationMetric( + executionAttributes.getAttribute(SdkInternalExecutionAttribute.RESPONSE_CHECKSUM_VALIDATION)) + .ifPresent(businessMetrics::addMetric); + + BusinessMetricsUtils.resolveChecksumSpecsMetric( + executionAttributes.getAttribute(SdkExecutionAttribute.RESOLVED_CHECKSUM_SPECS)) + .ifPresent(businessMetrics::addMetric); + } + private static Optional> credentialProviderBusinessMetrics( ExecutionAttributes executionAttributes) { return Optional.ofNullable( diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java index ffff00586e93..cefffac6d743 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java @@ -17,6 +17,11 @@ import java.util.Optional; import software.amazon.awssdk.annotations.SdkInternalApi; +import software.amazon.awssdk.checksums.DefaultChecksumAlgorithm; +import software.amazon.awssdk.checksums.spi.ChecksumAlgorithm; +import software.amazon.awssdk.core.checksums.ChecksumSpecs; +import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; +import software.amazon.awssdk.core.checksums.ResponseChecksumValidation; import software.amazon.awssdk.core.retry.RetryMode; import software.amazon.awssdk.core.retry.RetryPolicy; import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId; @@ -55,4 +60,57 @@ public static Optional resolveRetryMode(RetryPolicy retryPolicy, RetrySt } return Optional.empty(); } + + public static Optional resolveRequestChecksumCalculationMetric( + RequestChecksumCalculation requestChecksumCalculation) { + if (requestChecksumCalculation == RequestChecksumCalculation.WHEN_SUPPORTED) { + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED.value()); + } + if (requestChecksumCalculation == RequestChecksumCalculation.WHEN_REQUIRED) { + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED.value()); + } + return Optional.empty(); + } + + public static Optional resolveResponseChecksumValidationMetric( + ResponseChecksumValidation responseChecksumValidation) { + if (responseChecksumValidation == ResponseChecksumValidation.WHEN_SUPPORTED) { + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED.value()); + } + if (responseChecksumValidation == ResponseChecksumValidation.WHEN_REQUIRED) { + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED.value()); + } + return Optional.empty(); + } + + public static Optional resolveChecksumAlgorithmMetric(ChecksumAlgorithm algorithm) { + if (algorithm == null) { + return Optional.empty(); + } + + String algorithmId = algorithm.algorithmId(); + if (algorithmId.equals(DefaultChecksumAlgorithm.CRC32.algorithmId())) { + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_CRC32.value()); + } + if (algorithmId.equals(DefaultChecksumAlgorithm.CRC32C.algorithmId())) { + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_CRC32C.value()); + } + if (algorithmId.equals(DefaultChecksumAlgorithm.CRC64NVME.algorithmId())) { + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_CRC64.value()); + } + if (algorithmId.equals(DefaultChecksumAlgorithm.SHA1.algorithmId())) { + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_SHA1.value()); + } + if (algorithmId.equals(DefaultChecksumAlgorithm.SHA256.algorithmId())) { + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_SHA256.value()); + } + return Optional.empty(); + } + + public static Optional resolveChecksumSpecsMetric(ChecksumSpecs checksumSpecs) { + if (checksumSpecs != null && checksumSpecs.algorithmV2() != null) { + return resolveChecksumAlgorithmMetric(checksumSpecs.algorithmV2()); + } + return Optional.empty(); + } } 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 df481f975d50..71dc129371d8 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,O,S,U-c + * Unimplemented metrics: I,J,K,O * Unsupported metrics (these will never be added): A,H */ @SdkProtectedApi @@ -41,6 +41,15 @@ public enum BusinessMetricFeatureId { ACCOUNT_ID_MODE_DISABLED("Q"), ACCOUNT_ID_MODE_REQUIRED("R"), RESOLVED_ACCOUNT_ID("T"), + FLEXIBLE_CHECKSUMS_REQ_CRC32("U"), + FLEXIBLE_CHECKSUMS_REQ_CRC32C("V"), + FLEXIBLE_CHECKSUMS_REQ_CRC64("W"), + FLEXIBLE_CHECKSUMS_REQ_SHA1("X"), + FLEXIBLE_CHECKSUMS_REQ_SHA256("Y"), + FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED("Z"), + FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED("a"), + FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED("b"), + FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED("c"), DDB_MAPPER("d"), BEARER_SERVICE_ENV_VARS("3"), CREDENTIALS_CODE("e"), diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/FlexibleChecksumBusinessMetricTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/FlexibleChecksumBusinessMetricTest.java new file mode 100644 index 000000000000..92f42e26da62 --- /dev/null +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/FlexibleChecksumBusinessMetricTest.java @@ -0,0 +1,218 @@ +/* + * 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 java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; +import software.amazon.awssdk.core.checksums.ResponseChecksumValidation; +import software.amazon.awssdk.core.sync.RequestBody; +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.ProtocolRestJsonClient; +import software.amazon.awssdk.services.protocolrestjson.model.ChecksumAlgorithm; +import software.amazon.awssdk.testutils.service.http.MockSyncHttpClient; +import software.amazon.awssdk.utils.StringInputStream; + +/** + * Test class to verify that flexible checksum business metrics are correctly included + * in the User-Agent header when checksum algorithms are used. + */ +class FlexibleChecksumBusinessMetricTest { + 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_noChecksumConfigurationIsSet_defaultConfigMetricsAreAdded() { + ProtocolRestJsonClient client = ProtocolRestJsonClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockHttpClient) + .build(); + + client.allTypes(r -> {}); + String userAgent = getUserAgentFromLastRequest(); + + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply("Z")); + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply("b")); + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("a")); + assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("c")); + } + + @ParameterizedTest + @MethodSource("checksumAlgorithmTestCases") + void when_checksumAlgorithmIsUsed_correctMetricIsAdded(ChecksumAlgorithm algorithm, String expectedMetric) { + ProtocolRestJsonClient client = ProtocolRestJsonClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockHttpClient) + .build(); + + client.putOperationWithChecksum(r -> r.checksumAlgorithm(algorithm), + RequestBody.fromString("test content")); + + String userAgent = getUserAgentFromLastRequest(); + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedMetric)); + } + + static Stream checksumAlgorithmTestCases() { + return Stream.of( + Arguments.of(ChecksumAlgorithm.CRC32, "U"), + Arguments.of(ChecksumAlgorithm.CRC32_C, "V"), + Arguments.of(ChecksumAlgorithm.CRC64_NVME, "W"), + Arguments.of(ChecksumAlgorithm.SHA1, "X"), + Arguments.of(ChecksumAlgorithm.SHA256, "Y") + ); + } + + @ParameterizedTest + @MethodSource("checksumConfigurationTestCases") + void when_checksumConfigurationIsSet_correctMetricIsAdded(RequestChecksumCalculation requestConfig, + ResponseChecksumValidation responseConfig, + String expectedRequestMetric, + String expectedResponseMetric) { + ProtocolRestJsonClient client = ProtocolRestJsonClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockHttpClient) + .requestChecksumCalculation(requestConfig) + .responseChecksumValidation(responseConfig) + .build(); + + client.allTypes(r -> {}); + + String userAgent = getUserAgentFromLastRequest(); + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedRequestMetric)); + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedResponseMetric)); + } + + static Stream checksumConfigurationTestCases() { + return Stream.of( + Arguments.of(RequestChecksumCalculation.WHEN_SUPPORTED, + ResponseChecksumValidation.WHEN_SUPPORTED, "Z", "b"), + Arguments.of(RequestChecksumCalculation.WHEN_REQUIRED, + ResponseChecksumValidation.WHEN_REQUIRED, "a", "c"), + Arguments.of(RequestChecksumCalculation.WHEN_REQUIRED, + ResponseChecksumValidation.WHEN_SUPPORTED, "a", "b"), + Arguments.of(RequestChecksumCalculation.WHEN_SUPPORTED, + ResponseChecksumValidation.WHEN_REQUIRED, "Z", "c") + ); + } + + @ParameterizedTest + @MethodSource("checksumConfigurationWithAlgorithmTestCases") + void when_checksumConfigurationAndAlgorithmAreSet_correctMetricsAreAdded( + RequestChecksumCalculation requestConfig, + ResponseChecksumValidation responseConfig, + ChecksumAlgorithm algorithm, + String expectedRequestMetric, + String expectedResponseMetric, + String expectedAlgorithmMetric) { + + ProtocolRestJsonClient client = ProtocolRestJsonClient.builder() + .region(Region.US_WEST_2) + .credentialsProvider(CREDENTIALS_PROVIDER) + .httpClient(mockHttpClient) + .requestChecksumCalculation(requestConfig) + .responseChecksumValidation(responseConfig) + .build(); + + client.putOperationWithChecksum(r -> r.checksumAlgorithm(algorithm), + RequestBody.fromString("test content")); + + String userAgent = getUserAgentFromLastRequest(); + + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedRequestMetric)); + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedResponseMetric)); + assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedAlgorithmMetric)); + } + + static Stream checksumConfigurationWithAlgorithmTestCases() { + return Stream.of( + Arguments.of(RequestChecksumCalculation.WHEN_SUPPORTED, + ResponseChecksumValidation.WHEN_SUPPORTED, + ChecksumAlgorithm.CRC32, "Z", "b", "U"), + Arguments.of(RequestChecksumCalculation.WHEN_SUPPORTED, + ResponseChecksumValidation.WHEN_SUPPORTED, + ChecksumAlgorithm.CRC32_C, "Z", "b", "V"), + Arguments.of(RequestChecksumCalculation.WHEN_SUPPORTED, + ResponseChecksumValidation.WHEN_SUPPORTED, + ChecksumAlgorithm.SHA256, "Z", "b", "Y"), + + Arguments.of(RequestChecksumCalculation.WHEN_REQUIRED, + ResponseChecksumValidation.WHEN_REQUIRED, + ChecksumAlgorithm.CRC32, "a", "c", "U"), + Arguments.of(RequestChecksumCalculation.WHEN_REQUIRED, + ResponseChecksumValidation.WHEN_REQUIRED, + ChecksumAlgorithm.CRC64_NVME, "a", "c", "W"), + Arguments.of(RequestChecksumCalculation.WHEN_REQUIRED, + ResponseChecksumValidation.WHEN_REQUIRED, + ChecksumAlgorithm.SHA1, "a", "c", "X"), + + Arguments.of(RequestChecksumCalculation.WHEN_REQUIRED, + ResponseChecksumValidation.WHEN_SUPPORTED, + ChecksumAlgorithm.CRC32_C, "a", "b", "V"), + Arguments.of(RequestChecksumCalculation.WHEN_REQUIRED, + ResponseChecksumValidation.WHEN_SUPPORTED, + ChecksumAlgorithm.SHA256, "a", "b", "Y"), + + Arguments.of(RequestChecksumCalculation.WHEN_SUPPORTED, + ResponseChecksumValidation.WHEN_REQUIRED, + ChecksumAlgorithm.CRC64_NVME, "Z", "c", "W"), + Arguments.of(RequestChecksumCalculation.WHEN_SUPPORTED, + ResponseChecksumValidation.WHEN_REQUIRED, + ChecksumAlgorithm.SHA1, "Z", "c", "X") + ); + } + + 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 4ada9c27ac2d7ef58ca6fce9d9b1eee1d1add1c5 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Wed, 22 Oct 2025 15:56:51 -0700 Subject: [PATCH 2/7] Add changelog --- .changes/next-release/feature-AWSSDKforJavav2-c120920.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/next-release/feature-AWSSDKforJavav2-c120920.json diff --git a/.changes/next-release/feature-AWSSDKforJavav2-c120920.json b/.changes/next-release/feature-AWSSDKforJavav2-c120920.json new file mode 100644 index 000000000000..894951d46e76 --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-c120920.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Adds business metrics for flexible checksum algorithms and configurations" +} From 21db14fb3abcd7e6f55d5e95d90307d494df295f Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Wed, 22 Oct 2025 16:18:35 -0700 Subject: [PATCH 3/7] Fixing checkstyle errors --- .../internal/http/pipeline/stages/ApplyUserAgentStage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 6106fb895863..472bd4ad2be5 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 @@ -153,7 +153,7 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe } businessMetrics.merge(metricsFromApiNames); - ChecksumBusinessMetrics(executionAttributes, businessMetrics); + checksumBusinessMetrics(executionAttributes, businessMetrics); credentialProviderBusinessMetrics(executionAttributes).ifPresent(businessMetrics::merge); @@ -164,7 +164,7 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe return Optional.of(businessMetrics.asBoundedString()); } - private static void ChecksumBusinessMetrics(ExecutionAttributes executionAttributes, + private static void checksumBusinessMetrics(ExecutionAttributes executionAttributes, BusinessMetricCollection businessMetrics) { BusinessMetricsUtils.resolveRequestChecksumCalculationMetric( executionAttributes.getAttribute(SdkInternalExecutionAttribute.REQUEST_CHECKSUM_CALCULATION)) From b9b8a51ea3a3cf55ef1e644f109c80ce63adba7a Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Fri, 24 Oct 2025 15:24:18 -0700 Subject: [PATCH 4/7] Address PR feedback --- .../pipeline/stages/ApplyUserAgentStage.java | 18 ----------- .../pipeline/stages/HttpChecksumStage.java | 32 +++++++++++++++++-- 2 files changed, 30 insertions(+), 20 deletions(-) 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 472bd4ad2be5..e4af2e744bfd 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 @@ -32,12 +32,10 @@ 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.internal.useragent.BusinessMetricsUtils; import software.amazon.awssdk.core.useragent.AdditionalMetadata; import software.amazon.awssdk.core.useragent.BusinessMetricCollection; import software.amazon.awssdk.http.SdkHttpFullRequest; @@ -153,8 +151,6 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe } businessMetrics.merge(metricsFromApiNames); - checksumBusinessMetrics(executionAttributes, businessMetrics); - credentialProviderBusinessMetrics(executionAttributes).ifPresent(businessMetrics::merge); if (businessMetrics.recordedMetrics().isEmpty()) { @@ -164,20 +160,6 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe return Optional.of(businessMetrics.asBoundedString()); } - private static void checksumBusinessMetrics(ExecutionAttributes executionAttributes, - BusinessMetricCollection businessMetrics) { - BusinessMetricsUtils.resolveRequestChecksumCalculationMetric( - executionAttributes.getAttribute(SdkInternalExecutionAttribute.REQUEST_CHECKSUM_CALCULATION)) - .ifPresent(businessMetrics::addMetric); - - BusinessMetricsUtils.resolveResponseChecksumValidationMetric( - executionAttributes.getAttribute(SdkInternalExecutionAttribute.RESPONSE_CHECKSUM_VALIDATION)) - .ifPresent(businessMetrics::addMetric); - - BusinessMetricsUtils.resolveChecksumSpecsMetric( - executionAttributes.getAttribute(SdkExecutionAttribute.RESOLVED_CHECKSUM_SPECS)) - .ifPresent(businessMetrics::addMetric); - } private static Optional> credentialProviderBusinessMetrics( ExecutionAttributes executionAttributes) { diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/HttpChecksumStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/HttpChecksumStage.java index ca4b4d8f7f2c..80efb64c03d2 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/HttpChecksumStage.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/HttpChecksumStage.java @@ -49,7 +49,9 @@ import software.amazon.awssdk.core.internal.http.RequestExecutionContext; import software.amazon.awssdk.core.internal.http.pipeline.MutableRequestToRequestPipeline; import software.amazon.awssdk.core.internal.io.AwsUnsignedChunkedEncodingInputStream; +import software.amazon.awssdk.core.internal.useragent.BusinessMetricsUtils; import software.amazon.awssdk.core.internal.util.HttpChecksumUtils; +import software.amazon.awssdk.core.useragent.BusinessMetricCollection; import software.amazon.awssdk.http.ContentStreamProvider; import software.amazon.awssdk.http.Header; import software.amazon.awssdk.http.SdkHttpFullRequest; @@ -79,11 +81,16 @@ public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, Re ensurePayloadChecksumStorePresent(context.executionAttributes()); + SdkHttpFullRequest.Builder result; if (sraSigningEnabled(context)) { - return sraChecksum(request, context); + result = sraChecksum(request, context); + } else { + result = legacyChecksum(request, context); } - return legacyChecksum(request, context); + recordChecksumBusinessMetrics(context.executionAttributes()); + + return result; } private SdkHttpFullRequest.Builder legacyChecksum(SdkHttpFullRequest.Builder request, RequestExecutionContext context) { @@ -351,6 +358,27 @@ private PayloadChecksumStore getPayloadChecksumStore(ExecutionAttributes executi return executionAttributes.getAttribute(CHECKSUM_STORE); } + private void recordChecksumBusinessMetrics(ExecutionAttributes executionAttributes) { + BusinessMetricCollection businessMetrics = + executionAttributes.getAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS); + + if (businessMetrics == null) { + return; + } + + BusinessMetricsUtils.resolveRequestChecksumCalculationMetric( + executionAttributes.getAttribute(SdkInternalExecutionAttribute.REQUEST_CHECKSUM_CALCULATION)) + .ifPresent(businessMetrics::addMetric); + + BusinessMetricsUtils.resolveResponseChecksumValidationMetric( + executionAttributes.getAttribute(SdkInternalExecutionAttribute.RESPONSE_CHECKSUM_VALIDATION)) + .ifPresent(businessMetrics::addMetric); + + BusinessMetricsUtils.resolveChecksumSpecsMetric( + executionAttributes.getAttribute(RESOLVED_CHECKSUM_SPECS)) + .ifPresent(businessMetrics::addMetric); + } + static final class ChecksumCalculatingStreamProvider implements ContentStreamProvider { private final ContentStreamProvider underlyingInputStreamProvider; private final String checksumHeaderForTrailer; From c05de37da7cbc1d652df59de9d55548966cf7943 Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Fri, 24 Oct 2025 15:26:31 -0700 Subject: [PATCH 5/7] Removing extra line --- .../core/internal/http/pipeline/stages/ApplyUserAgentStage.java | 1 - 1 file changed, 1 deletion(-) 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 e4af2e744bfd..6cbb536ad43b 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 @@ -160,7 +160,6 @@ private static Optional getBusinessMetricsString(ExecutionAttributes exe return Optional.of(businessMetrics.asBoundedString()); } - private static Optional> credentialProviderBusinessMetrics( ExecutionAttributes executionAttributes) { return Optional.ofNullable( From 7dac554d8e68eb60a6636f71f2a95ac15c92416e Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Fri, 24 Oct 2025 16:23:05 -0700 Subject: [PATCH 6/7] Review comments --- .../pipeline/stages/HttpChecksumStage.java | 8 +++-- .../useragent/BusinessMetricsUtils.java | 33 ++++++++----------- .../FlexibleChecksumBusinessMetricTest.java | 21 +++++++----- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/HttpChecksumStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/HttpChecksumStage.java index 80efb64c03d2..c8521d3a09ef 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/HttpChecksumStage.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/HttpChecksumStage.java @@ -374,9 +374,11 @@ private void recordChecksumBusinessMetrics(ExecutionAttributes executionAttribut executionAttributes.getAttribute(SdkInternalExecutionAttribute.RESPONSE_CHECKSUM_VALIDATION)) .ifPresent(businessMetrics::addMetric); - BusinessMetricsUtils.resolveChecksumSpecsMetric( - executionAttributes.getAttribute(RESOLVED_CHECKSUM_SPECS)) - .ifPresent(businessMetrics::addMetric); + ChecksumSpecs checksumSpecs = executionAttributes.getAttribute(RESOLVED_CHECKSUM_SPECS); + if (checksumSpecs != null && checksumSpecs.algorithmV2() != null) { + BusinessMetricsUtils.resolveChecksumAlgorithmMetric(checksumSpecs.algorithmV2()) + .ifPresent(businessMetrics::addMetric); + } } static final class ChecksumCalculatingStreamProvider implements ContentStreamProvider { diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java index cefffac6d743..69e6cd6bf5f2 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java @@ -19,7 +19,6 @@ import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.checksums.DefaultChecksumAlgorithm; import software.amazon.awssdk.checksums.spi.ChecksumAlgorithm; -import software.amazon.awssdk.core.checksums.ChecksumSpecs; import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; import software.amazon.awssdk.core.checksums.ResponseChecksumValidation; import software.amazon.awssdk.core.retry.RetryMode; @@ -63,24 +62,26 @@ public static Optional resolveRetryMode(RetryPolicy retryPolicy, RetrySt public static Optional resolveRequestChecksumCalculationMetric( RequestChecksumCalculation requestChecksumCalculation) { - if (requestChecksumCalculation == RequestChecksumCalculation.WHEN_SUPPORTED) { - return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED.value()); + switch (requestChecksumCalculation) { + case WHEN_SUPPORTED: + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED.value()); + case WHEN_REQUIRED: + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED.value()); + default: + return Optional.empty(); } - if (requestChecksumCalculation == RequestChecksumCalculation.WHEN_REQUIRED) { - return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED.value()); - } - return Optional.empty(); } public static Optional resolveResponseChecksumValidationMetric( ResponseChecksumValidation responseChecksumValidation) { - if (responseChecksumValidation == ResponseChecksumValidation.WHEN_SUPPORTED) { - return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED.value()); + switch (responseChecksumValidation) { + case WHEN_SUPPORTED: + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED.value()); + case WHEN_REQUIRED: + return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED.value()); + default: + return Optional.empty(); } - if (responseChecksumValidation == ResponseChecksumValidation.WHEN_REQUIRED) { - return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED.value()); - } - return Optional.empty(); } public static Optional resolveChecksumAlgorithmMetric(ChecksumAlgorithm algorithm) { @@ -107,10 +108,4 @@ public static Optional resolveChecksumAlgorithmMetric(ChecksumAlgorithm return Optional.empty(); } - public static Optional resolveChecksumSpecsMetric(ChecksumSpecs checksumSpecs) { - if (checksumSpecs != null && checksumSpecs.algorithmV2() != null) { - return resolveChecksumAlgorithmMetric(checksumSpecs.algorithmV2()); - } - return Optional.empty(); - } } diff --git a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/FlexibleChecksumBusinessMetricTest.java b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/FlexibleChecksumBusinessMetricTest.java index 92f42e26da62..6766d6df048a 100644 --- a/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/FlexibleChecksumBusinessMetricTest.java +++ b/test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/FlexibleChecksumBusinessMetricTest.java @@ -68,10 +68,11 @@ void when_noChecksumConfigurationIsSet_defaultConfigMetricsAreAdded() { client.allTypes(r -> {}); String userAgent = getUserAgentFromLastRequest(); - assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply("Z")); - assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply("b")); - assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("a")); - assertThat(userAgent).doesNotMatch(METRIC_SEARCH_PATTERN.apply("c")); + assertThat(userAgent) + .matches(METRIC_SEARCH_PATTERN.apply("Z")) + .matches(METRIC_SEARCH_PATTERN.apply("b")) + .doesNotMatch(METRIC_SEARCH_PATTERN.apply("a")) + .doesNotMatch(METRIC_SEARCH_PATTERN.apply("c")); } @ParameterizedTest @@ -117,8 +118,9 @@ void when_checksumConfigurationIsSet_correctMetricIsAdded(RequestChecksumCalcula client.allTypes(r -> {}); String userAgent = getUserAgentFromLastRequest(); - assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedRequestMetric)); - assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedResponseMetric)); + assertThat(userAgent) + .matches(METRIC_SEARCH_PATTERN.apply(expectedRequestMetric)) + .matches(METRIC_SEARCH_PATTERN.apply(expectedResponseMetric)); } static Stream checksumConfigurationTestCases() { @@ -157,9 +159,10 @@ void when_checksumConfigurationAndAlgorithmAreSet_correctMetricsAreAdded( String userAgent = getUserAgentFromLastRequest(); - assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedRequestMetric)); - assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedResponseMetric)); - assertThat(userAgent).matches(METRIC_SEARCH_PATTERN.apply(expectedAlgorithmMetric)); + assertThat(userAgent) + .matches(METRIC_SEARCH_PATTERN.apply(expectedRequestMetric)) + .matches(METRIC_SEARCH_PATTERN.apply(expectedResponseMetric)) + .matches(METRIC_SEARCH_PATTERN.apply(expectedAlgorithmMetric)); } static Stream checksumConfigurationWithAlgorithmTestCases() { From 5d6d4a51e5f79acb51eb1e8e546e7e819a0fd59b Mon Sep 17 00:00:00 2001 From: Saranya Somepalli Date: Fri, 24 Oct 2025 21:27:51 -0700 Subject: [PATCH 7/7] Fixing failed tests --- .../core/internal/useragent/BusinessMetricsUtils.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java index 69e6cd6bf5f2..17d242939d23 100644 --- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java +++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/useragent/BusinessMetricsUtils.java @@ -62,6 +62,9 @@ public static Optional resolveRetryMode(RetryPolicy retryPolicy, RetrySt public static Optional resolveRequestChecksumCalculationMetric( RequestChecksumCalculation requestChecksumCalculation) { + if (requestChecksumCalculation == null) { + return Optional.empty(); + } switch (requestChecksumCalculation) { case WHEN_SUPPORTED: return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED.value()); @@ -74,6 +77,9 @@ public static Optional resolveRequestChecksumCalculationMetric( public static Optional resolveResponseChecksumValidationMetric( ResponseChecksumValidation responseChecksumValidation) { + if (responseChecksumValidation == null) { + return Optional.empty(); + } switch (responseChecksumValidation) { case WHEN_SUPPORTED: return Optional.of(BusinessMetricFeatureId.FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED.value());