diff --git a/.changes/next-release/feature-AWSSDKforJavav2-c710394.json b/.changes/next-release/feature-AWSSDKforJavav2-c710394.json new file mode 100644 index 000000000000..f84725c00fbe --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-c710394.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "HTTPS requests now skip payload signing by default for improved performance. HTTP and signing-dependent features are unaffected." +} diff --git a/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/internal/signer/util/ChecksumUtil.java b/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/internal/signer/util/ChecksumUtil.java index 03a55eb55cdd..3fec77faf11a 100644 --- a/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/internal/signer/util/ChecksumUtil.java +++ b/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/internal/signer/util/ChecksumUtil.java @@ -134,23 +134,31 @@ public static boolean useChunkEncoding(boolean payloadSigningEnabled, boolean ch public static boolean isPayloadSigning(BaseSignRequest request) { boolean isAnonymous = CredentialUtils.isAnonymous(request.identity()); - boolean isPayloadSigningEnabled = request.requireProperty(PAYLOAD_SIGNING_ENABLED, true); + Boolean payloadSigningEnabled = request.property(PAYLOAD_SIGNING_ENABLED); boolean isEncrypted = "https".equals(request.request().protocol()); + boolean hasPayload = request.payload().isPresent(); if (isAnonymous) { return false; } - // presigning requests should always have a null payload, and should always be unsigned-payload - if (!isEncrypted && request.payload().isPresent()) { - if (!isPayloadSigningEnabled) { + if (payloadSigningEnabled != null) { + // presigning requests should always have a null payload, and should always be unsigned-payload + if (!isEncrypted && hasPayload && !payloadSigningEnabled) { log.debug(() -> "Payload signing was disabled for an HTTP request with a payload. " + "Signing will be enabled. Use HTTPS for unsigned payloads."); + return true; } - return true; + return payloadSigningEnabled; } - return isPayloadSigningEnabled; + // For HTTPS, skip payload signing by default + if (isEncrypted) { + return false; + } + + // For HTTP, sign payload by default + return hasPayload; } public static boolean isEventStreaming(SdkHttpRequest request) { diff --git a/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4FamilyHttpSigner.java b/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4FamilyHttpSigner.java index 5041047cb509..ffefc7fe1b2c 100644 --- a/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4FamilyHttpSigner.java +++ b/core/http-auth-aws/src/main/java/software/amazon/awssdk/http/auth/aws/signer/AwsV4FamilyHttpSigner.java @@ -67,13 +67,13 @@ public interface AwsV4FamilyHttpSigner extends HttpSigner SignerProperty.create(AwsV4FamilyHttpSigner.class, "ExpirationDuration"); /** - * Whether to indicate that a payload is signed or not. This property defaults to true. This can be set false to disable - * payload signing. + * Whether to indicate that a payload is signed or not. *

- * When this value is true and {@link #CHUNK_ENCODING_ENABLED} is false, the whole payload must be read to generate - * the payload signature. For very large payloads, this could impact memory usage and call latency. Some services - * support this value being disabled, especially over HTTPS where SSL provides some of its own protections against - * payload tampering. + * Default behavior (when not explicitly set): + *

*/ SignerProperty PAYLOAD_SIGNING_ENABLED = SignerProperty.create(AwsV4FamilyHttpSigner.class, "PayloadSigningEnabled"); diff --git a/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/crt/internal/signer/DefaultAwsCrtV4aHttpSignerTest.java b/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/crt/internal/signer/DefaultAwsCrtV4aHttpSignerTest.java index c33248638b67..f6d470367a3d 100644 --- a/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/crt/internal/signer/DefaultAwsCrtV4aHttpSignerTest.java +++ b/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/crt/internal/signer/DefaultAwsCrtV4aHttpSignerTest.java @@ -140,7 +140,7 @@ void sign_withBasicRequest_shouldSignWithHeaders() { assertThat(signedRequest.request().firstMatchingHeader("X-Amz-Date")).hasValue("20200803T174823Z"); assertThat(signedRequest.request().firstMatchingHeader("X-Amz-Region-Set")).hasValue("aws-global"); assertThat(signedRequest.request().firstMatchingHeader("Authorization")).isPresent(); - assertThat(signedRequest.request().firstMatchingHeader("x-amz-content-sha256")).contains(PAYLOAD_SHA256_HEX); + assertThat(signedRequest.request().firstMatchingHeader("x-amz-content-sha256")).hasValue("UNSIGNED-PAYLOAD"); } @Test @@ -172,7 +172,7 @@ void signAsync_withBasicRequest_shouldSignWithHeaders() { assertThat(signedRequest.request().firstMatchingHeader("X-Amz-Date")).hasValue("20200803T174823Z"); assertThat(signedRequest.request().firstMatchingHeader("X-Amz-Region-Set")).hasValue("aws-global"); assertThat(signedRequest.request().firstMatchingHeader("Authorization")).isPresent(); - assertThat(signedRequest.request().firstMatchingHeader("x-amz-content-sha256")).contains(PAYLOAD_SHA256_HEX); + assertThat(signedRequest.request().firstMatchingHeader("x-amz-content-sha256")).hasValue("UNSIGNED-PAYLOAD"); } @Test @@ -564,6 +564,7 @@ void sign_WithChunkEncodingTrue_DelegatesToAwsChunkedPayloadSigner() { .putHeader(Header.CONTENT_LENGTH, "20"), signRequest -> signRequest .putProperty(CHUNK_ENCODING_ENABLED, true) + .putProperty(PAYLOAD_SIGNING_ENABLED, true) ); SignedRequest signedRequest = signer.sign(request); @@ -587,6 +588,7 @@ void signAsync_WithChunkEncodingTrue_DelegatesToAwsChunkedPayloadSigner() { .putHeader(Header.CONTENT_LENGTH, contentLength), signRequest -> signRequest .putProperty(CHUNK_ENCODING_ENABLED, true) + .putProperty(PAYLOAD_SIGNING_ENABLED, true) ); AsyncSignedRequest signedRequest = signer.signAsync(request).join(); @@ -610,6 +612,7 @@ void sign_WithChunkEncodingTrueAndChecksumAlgorithm_DelegatesToAwsChunkedPayload signRequest -> signRequest .putProperty(CHUNK_ENCODING_ENABLED, true) .putProperty(CHECKSUM_ALGORITHM, CRC32) + .putProperty(PAYLOAD_SIGNING_ENABLED, true) ); SignedRequest signedRequest = signer.sign(request); @@ -636,6 +639,7 @@ void signAsync_WithChunkEncodingTrueAndChecksumAlgorithm_DelegatesToAwsChunkedPa signRequest -> signRequest .putProperty(CHUNK_ENCODING_ENABLED, true) .putProperty(CHECKSUM_ALGORITHM, CRC32) + .putProperty(PAYLOAD_SIGNING_ENABLED, true) ); AsyncSignedRequest signedRequest = signer.signAsync(request).join(); diff --git a/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/internal/signer/DefaultAwsV4HttpSignerTest.java b/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/internal/signer/DefaultAwsV4HttpSignerTest.java index da4b0c1115fe..e65f4d23e58c 100644 --- a/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/internal/signer/DefaultAwsV4HttpSignerTest.java +++ b/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/internal/signer/DefaultAwsV4HttpSignerTest.java @@ -38,6 +38,7 @@ import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.List; +import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -319,6 +320,7 @@ void sign_WithEventStreamContentTypeWithoutHttpAuthAwsEventStreamModule_throws() httpRequest -> httpRequest .putHeader("Content-Type", "application/vnd.amazon.eventstream"), signRequest -> { + signRequest.putProperty(PAYLOAD_SIGNING_ENABLED, true); } ); @@ -339,6 +341,7 @@ void signAsync_WithEventStreamContentTypeWithoutHttpAuthAwsEventStreamModule_thr httpRequest -> httpRequest .putHeader("Content-Type", "application/vnd.amazon.eventstream"), signRequest -> { + signRequest.putProperty(PAYLOAD_SIGNING_ENABLED, true); } ); @@ -372,6 +375,7 @@ void signAsync_WithEventStreamContentType_DelegatesToEventStreamPayloadSigner() httpRequest -> httpRequest .putHeader("Content-Type", "application/vnd.amazon.eventstream"), signRequest -> { + signRequest.putProperty(PAYLOAD_SIGNING_ENABLED, true); } ); @@ -414,6 +418,7 @@ void sign_WithChunkEncodingTrue_DelegatesToAwsChunkedPayloadSigner() { .putHeader(Header.CONTENT_LENGTH, "20"), signRequest -> signRequest .putProperty(CHUNK_ENCODING_ENABLED, true) + .putProperty(PAYLOAD_SIGNING_ENABLED, true) ); SignedRequest signedRequest = signer.sign(request); @@ -432,6 +437,7 @@ void signAsync_WithChunkEncodingTrue_DelegatesToAwsChunkedPayloadSigner_futureBe .putHeader(Header.CONTENT_LENGTH, "20"), signRequest -> signRequest .putProperty(CHUNK_ENCODING_ENABLED, true) + .putProperty(PAYLOAD_SIGNING_ENABLED, true) ); AsyncSignedRequest signedRequest = signer.signAsync(request).join(); @@ -452,6 +458,7 @@ void sign_WithChunkEncodingTrueAndChecksumAlgorithm_DelegatesToAwsChunkedPayload signRequest -> signRequest .putProperty(CHUNK_ENCODING_ENABLED, true) .putProperty(CHECKSUM_ALGORITHM, CRC32) + .putProperty(PAYLOAD_SIGNING_ENABLED, true) ); SignedRequest signedRequest = signer.sign(request); @@ -472,6 +479,7 @@ void signAsync_WithChunkEncodingTrueAndChecksumAlgorithm_DelegatesToAwsChunkedPa signRequest -> signRequest .putProperty(CHUNK_ENCODING_ENABLED, true) .putProperty(CHECKSUM_ALGORITHM, CRC32) + .putProperty(PAYLOAD_SIGNING_ENABLED, true) ); AsyncSignedRequest signedRequest = signer.signAsync(request).join(); @@ -979,6 +987,103 @@ void signAsync_WithPayloadSigningFalse_chunkEncodingTrue_noContentLengthHeader_t .hasMessageContaining("Content-Length header must be specified"); } + @Test + void sign_WithHttpsAndNoProperties_UsesUnsignedPayload() { + SignRequest request = generateBasicRequest( + AwsCredentialsIdentity.create("access", "secret"), + httpRequest -> { + }, + signRequest -> { + } + ); + + SignedRequest signedRequest = signer.sign(request); + + assertThat(signedRequest.request().firstMatchingHeader("x-amz-content-sha256")).hasValue("UNSIGNED-PAYLOAD"); + } + + @Test + void asyncSign_WithHttpsAndNoProperties_UsesUnsignedPayload() { + AsyncSignRequest request = generateBasicAsyncRequest( + AwsCredentialsIdentity.create("access", "secret"), + httpRequest -> { + }, + signRequest -> { + } + ); + + AsyncSignedRequest signedRequest = signer.signAsync(request).join(); + + assertThat(signedRequest.request().firstMatchingHeader("x-amz-content-sha256")).hasValue("UNSIGNED-PAYLOAD"); + } + + @Test + void sign_WithHttpsExplicitSigning_SignsPayload() { + SignRequest request = generateBasicRequest( + AwsCredentialsIdentity.create("access", "secret"), + httpRequest -> { + }, + signRequest -> { + signRequest.putProperty(PAYLOAD_SIGNING_ENABLED, true); + } + ); + + byte[] sha256Value = computeChecksum(SHA256, testPayload()); + SignedRequest signedRequest = signer.sign(request); + + assertThat(signedRequest.request().firstMatchingHeader("x-amz-content-sha256")).hasValue(BinaryUtils.toHex(sha256Value)); + } + + @Test + void asyncSign_WithHttpsExplicitSigning_SignsPayload() { + AsyncSignRequest request = generateBasicAsyncRequest( + AwsCredentialsIdentity.create("access", "secret"), + httpRequest -> { + }, + signRequest -> { + signRequest.putProperty(PAYLOAD_SIGNING_ENABLED, true); + } + ); + + byte[] sha256Value = computeChecksum(SHA256, testPayload()); + AsyncSignedRequest signedRequest = signer.signAsync(request).join(); + + assertThat(signedRequest.request().firstMatchingHeader("x-amz-content-sha256")).hasValue(BinaryUtils.toHex(sha256Value)); + } + + @Test + void sign_WithHTTPAndNoProperties_SignsPayload() { + SignRequest request = generateBasicRequest( + AwsCredentialsIdentity.create("access", "secret"), + httpRequest -> { + httpRequest.uri(URI.create("http://demo.us-east-1.amazonaws.com")); + }, + signRequest -> { + } + ); + + byte[] sha256Value = computeChecksum(SHA256, testPayload()); + SignedRequest signedRequest = signer.sign(request); + + assertThat(signedRequest.request().firstMatchingHeader("x-amz-content-sha256")).hasValue(BinaryUtils.toHex(sha256Value)); + } + + @Test + void asyncSign_WithHTTPAndNoProperties_SignsPayload() { + AsyncSignRequest request = generateBasicAsyncRequest( + AwsCredentialsIdentity.create("access", "secret"), + httpRequest -> { + httpRequest.uri(URI.create("http://demo.us-east-1.amazonaws.com")); + }, + signRequest -> { + } + ); + + byte[] sha256Value = computeChecksum(SHA256, testPayload()); + AsyncSignedRequest signedRequest = signer.signAsync(request).join(); + + assertThat(signedRequest.request().firstMatchingHeader("x-amz-content-sha256")).hasValue(BinaryUtils.toHex(sha256Value)); + } private static byte[] computeChecksum(ChecksumAlgorithm algorithm, byte[] data) { SdkChecksum checksum = SdkChecksum.forAlgorithm(algorithm); diff --git a/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/internal/signer/util/ChecksumUtilTest.java b/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/internal/signer/util/ChecksumUtilTest.java index 88f220ba83d2..0c406dc975bf 100644 --- a/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/internal/signer/util/ChecksumUtilTest.java +++ b/core/http-auth-aws/src/test/java/software/amazon/awssdk/http/auth/aws/internal/signer/util/ChecksumUtilTest.java @@ -24,20 +24,30 @@ import static software.amazon.awssdk.checksums.DefaultChecksumAlgorithm.MD5; import static software.amazon.awssdk.checksums.DefaultChecksumAlgorithm.SHA1; import static software.amazon.awssdk.checksums.DefaultChecksumAlgorithm.SHA256; +import static software.amazon.awssdk.http.auth.aws.TestUtils.generateBasicRequest; import static software.amazon.awssdk.http.auth.aws.internal.signer.util.ChecksumUtil.checksumHeaderName; import static software.amazon.awssdk.http.auth.aws.internal.signer.util.ChecksumUtil.fromChecksumAlgorithm; +import static software.amazon.awssdk.http.auth.aws.internal.signer.util.ChecksumUtil.isPayloadSigning; import static software.amazon.awssdk.http.auth.aws.internal.signer.util.ChecksumUtil.readAll; +import static software.amazon.awssdk.http.auth.aws.signer.AwsV4HttpSigner.PAYLOAD_SIGNING_ENABLED; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; +import java.net.URI; +import java.util.stream.Stream; 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 org.mockito.Mockito; import software.amazon.awssdk.checksums.SdkChecksum; import software.amazon.awssdk.checksums.internal.Crc32Checksum; import software.amazon.awssdk.checksums.internal.Crc64NvmeChecksum; import software.amazon.awssdk.checksums.internal.DigestAlgorithmChecksum; +import software.amazon.awssdk.http.auth.spi.signer.SignRequest; +import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; public class ChecksumUtilTest { @@ -87,4 +97,32 @@ public void readAll_throwNonIoException_shouldNotWrap() throws IOException { Mockito.when(mock.read(Mockito.any(byte[].class))).thenThrow(npe); assertThatThrownBy(() -> readAll(mock)).isEqualTo(npe); } + + @ParameterizedTest(name = "{0}") + @MethodSource("payloadSigningTestCases") + void isPayloadSigning_returnsExpectedValue(String description, Boolean propertyValue, + String protocol, boolean expected) { + SignRequest request = generateBasicRequest( + AwsCredentialsIdentity.create("access", "secret"), + httpRequest -> httpRequest.uri(URI.create(protocol + "://demo.us-east-1.amazonaws.com")), + signRequest -> { + if (propertyValue != null) { + signRequest.putProperty(PAYLOAD_SIGNING_ENABLED, propertyValue); + } + } + ); + + assertEquals(expected, isPayloadSigning(request)); + } + + private static Stream payloadSigningTestCases() { + return Stream.of( + Arguments.of("Explicit true overrides HTTPS", true, "https", true), + Arguments.of("Explicit false with HTTPS", false, "https", false), + Arguments.of("HTTPS with no property skips signing", null, "https", false), + Arguments.of("HTTP with no property signs payload", null, "http", true) + ); + } + + } diff --git a/services/docdb/src/test/java/software/amazon/awssdk/services/docdb/internal/PresignRequestHandlerTest.java b/services/docdb/src/test/java/software/amazon/awssdk/services/docdb/internal/PresignRequestHandlerTest.java index 62cb53e9b5c6..2e3aae122cd1 100644 --- a/services/docdb/src/test/java/software/amazon/awssdk/services/docdb/internal/PresignRequestHandlerTest.java +++ b/services/docdb/src/test/java/software/amazon/awssdk/services/docdb/internal/PresignRequestHandlerTest.java @@ -219,7 +219,7 @@ static String fixedTimePresignedUrl() { "&X-Amz-SignedHeaders=host" + "&X-Amz-Credential=foo%2F20161221%2Fus-east-1%2Frds%2Faws4_request" + "&X-Amz-Expires=604800" + - "&X-Amz-Signature=00822ebbba95e2e6ac09112aa85621fbef060a596e3e1480f9f4ac61493e9821"; + "&X-Amz-Signature=c2ef95311d2d530c13b478b1bea9b8fcec9bc8f2160645b9533b648fe7fd2371"; } private Map> rawQueryParameters(SdkHttpFullRequest request) { diff --git a/services/neptune/src/test/java/software/amazon/awssdk/services/neptune/internal/PresignRequestHandlerTest.java b/services/neptune/src/test/java/software/amazon/awssdk/services/neptune/internal/PresignRequestHandlerTest.java index e6058d6aa08b..671bfefbf575 100644 --- a/services/neptune/src/test/java/software/amazon/awssdk/services/neptune/internal/PresignRequestHandlerTest.java +++ b/services/neptune/src/test/java/software/amazon/awssdk/services/neptune/internal/PresignRequestHandlerTest.java @@ -223,7 +223,7 @@ static String fixedTimePresignedUrl() { "&X-Amz-SignedHeaders=host" + "&X-Amz-Credential=foo%2F20161221%2Fus-east-1%2Frds%2Faws4_request" + "&X-Amz-Expires=604800" + - "&X-Amz-Signature=00822ebbba95e2e6ac09112aa85621fbef060a596e3e1480f9f4ac61493e9821"; + "&X-Amz-Signature=c2ef95311d2d530c13b478b1bea9b8fcec9bc8f2160645b9533b648fe7fd2371"; } private Map> rawQueryParameters(SdkHttpFullRequest request) { diff --git a/services/rds/src/test/java/software/amazon/awssdk/services/rds/internal/PresignRequestHandlerTest.java b/services/rds/src/test/java/software/amazon/awssdk/services/rds/internal/PresignRequestHandlerTest.java index fff1e649fc40..abdcb26c3003 100644 --- a/services/rds/src/test/java/software/amazon/awssdk/services/rds/internal/PresignRequestHandlerTest.java +++ b/services/rds/src/test/java/software/amazon/awssdk/services/rds/internal/PresignRequestHandlerTest.java @@ -240,7 +240,7 @@ static String fixedTimePresignedUrl() { "&X-Amz-SignedHeaders=host" + "&X-Amz-Credential=foo%2F20161221%2Fus-east-1%2Frds%2Faws4_request" + "&X-Amz-Expires=604800" + - "&X-Amz-Signature=00822ebbba95e2e6ac09112aa85621fbef060a596e3e1480f9f4ac61493e9821"; + "&X-Amz-Signature=c2ef95311d2d530c13b478b1bea9b8fcec9bc8f2160645b9533b648fe7fd2371"; } private Map> rawQueryParameters(SdkHttpFullRequest request) { diff --git a/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/signer/DynamoDbPayloadSigningBenchmark.java b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/signer/DynamoDbPayloadSigningBenchmark.java new file mode 100644 index 000000000000..c9ad4baac691 --- /dev/null +++ b/test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/signer/DynamoDbPayloadSigningBenchmark.java @@ -0,0 +1,94 @@ +/* + * 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.benchmark.signer; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.benchmark.utils.MockHttpClient; +import software.amazon.awssdk.core.SdkBytes; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.dynamodb.DynamoDbClient; +import software.amazon.awssdk.services.dynamodb.model.AttributeValue; +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest; +import software.amazon.awssdk.services.dynamodb.model.PutItemResponse; + +@State(Scope.Thread) +@BenchmarkMode(Mode.SampleTime) +@OutputTimeUnit(TimeUnit.MICROSECONDS) +@Warmup(iterations = 5) +@Measurement(iterations = 5) +@Fork(2) +public class DynamoDbPayloadSigningBenchmark { + + @Param({"10240", "409600"}) + private int payloadSize; + + private DynamoDbClient client; + private PutItemRequest putRequest; + + @Setup(Level.Trial) + public void setup() { + client = DynamoDbClient.builder() + .region(Region.US_EAST_1) + .endpointOverride(URI.create("https://dynamodb.us-east-1.amazonaws.com")) + .credentialsProvider(StaticCredentialsProvider.create( + AwsBasicCredentials.create("AKIATEST", "testSecretKey"))) + .httpClient(new MockHttpClient("{}", "{}")) + .build(); + + byte[] data = new byte[payloadSize]; + new Random(42).nextBytes(data); + + Map item = new HashMap<>(); + item.put("id", AttributeValue.builder().s("test-id").build()); + item.put("data", AttributeValue.builder().b(SdkBytes.fromByteArray(data)).build()); + + putRequest = PutItemRequest.builder() + .tableName("test-table") + .item(item) + .build(); + } + + @TearDown(Level.Trial) + public void tearDown() { + client.close(); + } + + @Benchmark + public void putItem(Blackhole blackhole) { + PutItemResponse response = client.putItem(putRequest); + blackhole.consume(response); + } +}