Skip to content

Commit 4cb9210

Browse files
authored
Reuse checksums in legacy signing codepath (#6444)
* Reuse checksums in legacy signing codepath This commit adds support for reusing calculated payload checksums over retries in the legacy (i.e. non-SRA) signing codepaths. * S3 testing with non-SRA
1 parent 110e858 commit 4cb9210

File tree

8 files changed

+288
-63
lines changed

8 files changed

+288
-63
lines changed

core/auth/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@
9393
<artifactId>http-auth-spi</artifactId>
9494
<version>${awsjavasdk.version}</version>
9595
</dependency>
96+
<dependency>
97+
<groupId>software.amazon.awssdk</groupId>
98+
<artifactId>checksums-spi</artifactId>
99+
<version>${awsjavasdk.version}</version>
100+
</dependency>
96101
<dependency>
97102
<groupId>software.amazon.eventstream</groupId>
98103
<artifactId>eventstream</artifactId>

core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/chunkedencoding/AwsSignedChunkedEncodingInputStream.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
import java.io.InputStream;
2020
import java.nio.charset.StandardCharsets;
2121
import software.amazon.awssdk.annotations.SdkInternalApi;
22+
import software.amazon.awssdk.checksums.spi.ChecksumAlgorithm;
2223
import software.amazon.awssdk.core.checksums.Algorithm;
2324
import software.amazon.awssdk.core.checksums.SdkChecksum;
2425
import software.amazon.awssdk.core.exception.SdkClientException;
2526
import software.amazon.awssdk.core.internal.chunked.AwsChunkedEncodingConfig;
2627
import software.amazon.awssdk.core.internal.io.AwsChunkedEncodingInputStream;
28+
import software.amazon.awssdk.http.auth.spi.signer.PayloadChecksumStore;
2729
import software.amazon.awssdk.utils.BinaryUtils;
2830

2931
/**
@@ -60,12 +62,15 @@ public final class AwsSignedChunkedEncodingInputStream extends AwsChunkedEncodin
6062
* @param config The configuration allows the user to customize chunk size and buffer size.
6163
* See {@link AwsChunkedEncodingConfig} for default values.
6264
*/
63-
private AwsSignedChunkedEncodingInputStream(InputStream in, SdkChecksum sdkChecksum,
65+
private AwsSignedChunkedEncodingInputStream(InputStream in,
66+
ChecksumAlgorithm checksumAlgorithm,
67+
SdkChecksum sdkChecksum,
68+
PayloadChecksumStore checksumStore,
6469
String checksumHeaderForTrailer,
6570
String headerSignature,
6671
AwsChunkSigner chunkSigner,
6772
AwsChunkedEncodingConfig config) {
68-
super(in, sdkChecksum, checksumHeaderForTrailer, config);
73+
super(in, checksumAlgorithm, sdkChecksum, checksumStore, checksumHeaderForTrailer, config);
6974
this.chunkSigner = chunkSigner;
7075
this.previousChunkSignature = headerSignature;
7176
this.headerSignature = headerSignature;
@@ -103,9 +108,14 @@ public Builder awsChunkSigner(AwsChunkSigner awsChunkSigner) {
103108

104109
public AwsSignedChunkedEncodingInputStream build() {
105110

106-
return new AwsSignedChunkedEncodingInputStream(this.inputStream, this.sdkChecksum, this.checksumHeaderForTrailer,
111+
return new AwsSignedChunkedEncodingInputStream(this.inputStream,
112+
this.checksumAlgorithm,
113+
this.sdkChecksum,
114+
this.checksumStore,
115+
this.checksumHeaderForTrailer,
107116
this.headerSignature,
108-
this.awsChunkSigner, this.awsChunkedEncodingConfig);
117+
this.awsChunkSigner,
118+
this.awsChunkedEncodingConfig);
109119
}
110120
}
111121

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/checksums/LegacyPayloadChecksumCache.java

Lines changed: 0 additions & 37 deletions
This file was deleted.

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/HttpChecksumStage.java

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ public HttpChecksumStage(ClientType clientType) {
7777
public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, RequestExecutionContext context)
7878
throws Exception {
7979

80+
ensurePayloadChecksumStorePresent(context.executionAttributes());
81+
8082
if (sraSigningEnabled(context)) {
81-
ensurePayloadChecksumStorePresent(context.executionAttributes());
8283
return sraChecksum(request, context);
8384
}
8485

@@ -87,9 +88,10 @@ public SdkHttpFullRequest.Builder execute(SdkHttpFullRequest.Builder request, Re
8788

8889
private SdkHttpFullRequest.Builder legacyChecksum(SdkHttpFullRequest.Builder request, RequestExecutionContext context) {
8990
ChecksumSpecs resolvedChecksumSpecs = getResolvedChecksumSpecs(context.executionAttributes());
91+
PayloadChecksumStore checksumStore = getPayloadChecksumStore(context.executionAttributes());
9092

9193
if (md5ChecksumRequired(request, context)) {
92-
addMd5ChecksumInHeader(request);
94+
addMd5ChecksumInHeader(request, checksumStore);
9395
return request;
9496
}
9597

@@ -99,7 +101,7 @@ private SdkHttpFullRequest.Builder legacyChecksum(SdkHttpFullRequest.Builder req
99101
}
100102

101103
if (flexibleChecksumInHeaderRequired(context, resolvedChecksumSpecs)) {
102-
addFlexibleChecksumInHeader(request, context, resolvedChecksumSpecs);
104+
addFlexibleChecksumInHeader(request, context, resolvedChecksumSpecs, checksumStore);
103105
return request;
104106
}
105107

@@ -174,10 +176,14 @@ private boolean md5ChecksumRequired(SdkHttpFullRequest.Builder request, RequestE
174176
* request body to use that buffered content. We obviously don't want to do that for giant streams, so we haven't opted to do
175177
* that yet.
176178
*/
177-
private void addMd5ChecksumInHeader(SdkHttpFullRequest.Builder request) {
179+
private void addMd5ChecksumInHeader(SdkHttpFullRequest.Builder request, PayloadChecksumStore checksumStore) {
178180
try {
179-
String payloadMd5 = Md5Utils.md5AsBase64(request.contentStreamProvider().newStream());
180-
request.putHeader(Header.CONTENT_MD5, payloadMd5);
181+
byte[] payloadMd5 = checksumStore.getChecksumValue(DefaultChecksumAlgorithm.MD5);
182+
if (payloadMd5 == null) {
183+
payloadMd5 = Md5Utils.computeMD5Hash(request.contentStreamProvider().newStream());
184+
checksumStore.putChecksumValue(DefaultChecksumAlgorithm.MD5, payloadMd5);
185+
}
186+
request.putHeader(Header.CONTENT_MD5, BinaryUtils.toBase64(payloadMd5));
181187
} catch (IOException e) {
182188
throw new UncheckedIOException(e);
183189
}
@@ -237,7 +243,11 @@ private void addFlexibleChecksumInTrailer(SdkHttpFullRequest.Builder request, Re
237243
int chunkSize = 0;
238244

239245
if (clientType == ClientType.SYNC) {
240-
request.contentStreamProvider(new ChecksumCalculatingStreamProvider(request.contentStreamProvider(), checksumSpecs));
246+
request.contentStreamProvider(
247+
new ChecksumCalculatingStreamProvider(request.contentStreamProvider(),
248+
checksumSpecs,
249+
getPayloadChecksumStore(context.executionAttributes())
250+
));
241251
originalContentLength =
242252
context.executionContext().interceptorContext().requestBody().get().optionalContentLength().orElse(0L);
243253
chunkSize = DEFAULT_CHUNK_SIZE;
@@ -311,13 +321,19 @@ private boolean flexibleChecksumInHeaderRequired(RequestExecutionContext context
311321
* that yet.
312322
*/
313323
private void addFlexibleChecksumInHeader(SdkHttpFullRequest.Builder request, RequestExecutionContext context,
314-
ChecksumSpecs checksumSpecs) {
324+
ChecksumSpecs checksumSpecs, PayloadChecksumStore checksumStore) {
315325
try {
316326
Algorithm legacyAlgorithm = checksumSpecs.algorithm();
317-
String payloadChecksum = BinaryUtils.toBase64(HttpChecksumUtils.computeChecksum(
318-
context.executionContext().interceptorContext().requestBody().get().contentStreamProvider().newStream(),
319-
legacyAlgorithm));
320-
request.putHeader(checksumSpecs.headerName(), payloadChecksum);
327+
ChecksumAlgorithm newAlgorithm = HttpChecksumUtils.toNewChecksumAlgorithm(legacyAlgorithm);
328+
byte[] payloadChecksum = checksumStore.getChecksumValue(newAlgorithm);
329+
if (payloadChecksum == null) {
330+
payloadChecksum = HttpChecksumUtils.computeChecksum(
331+
context.executionContext().interceptorContext().requestBody().get().contentStreamProvider().newStream(),
332+
legacyAlgorithm);
333+
checksumStore.putChecksumValue(newAlgorithm, payloadChecksum);
334+
}
335+
String headerValue = BinaryUtils.toBase64(payloadChecksum);
336+
request.putHeader(checksumSpecs.headerName(), headerValue);
321337
} catch (IOException e) {
322338
throw new UncheckedIOException(e);
323339
}
@@ -339,24 +355,31 @@ static final class ChecksumCalculatingStreamProvider implements ContentStreamPro
339355
private final ContentStreamProvider underlyingInputStreamProvider;
340356
private final String checksumHeaderForTrailer;
341357
private final ChecksumSpecs checksumSpecs;
358+
private final PayloadChecksumStore checksumStore;
342359
private InputStream currentStream;
360+
private final ChecksumAlgorithm checksumAlgorithm;
343361
private software.amazon.awssdk.core.checksums.SdkChecksum sdkChecksum;
344362

345363
ChecksumCalculatingStreamProvider(ContentStreamProvider underlyingInputStreamProvider,
346-
ChecksumSpecs checksumSpecs) {
364+
ChecksumSpecs checksumSpecs,
365+
PayloadChecksumStore checksumStore) {
347366
this.underlyingInputStreamProvider = underlyingInputStreamProvider;
348367
this.sdkChecksum = software.amazon.awssdk.core.checksums.SdkChecksum.forAlgorithm(
349368
checksumSpecs.algorithm());
369+
this.checksumAlgorithm = HttpChecksumUtils.toNewChecksumAlgorithm(checksumSpecs.algorithm());
350370
this.checksumHeaderForTrailer = checksumSpecs.headerName();
351371
this.checksumSpecs = checksumSpecs;
372+
this.checksumStore = checksumStore;
352373
}
353374

354375
@Override
355376
public InputStream newStream() {
356377
closeCurrentStream();
357378
currentStream = AwsUnsignedChunkedEncodingInputStream.builder()
358379
.inputStream(underlyingInputStreamProvider.newStream())
380+
.checksumAlgorithm(checksumAlgorithm)
359381
.sdkChecksum(sdkChecksum)
382+
.checksumStore(checksumStore)
360383
.checksumHeaderForTrailer(checksumHeaderForTrailer)
361384
.build();
362385
return currentStream;

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/io/AwsChunkedEncodingInputStream.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
import java.nio.charset.StandardCharsets;
2121
import java.util.Arrays;
2222
import software.amazon.awssdk.annotations.SdkInternalApi;
23+
import software.amazon.awssdk.checksums.spi.ChecksumAlgorithm;
2324
import software.amazon.awssdk.core.checksums.SdkChecksum;
25+
import software.amazon.awssdk.core.internal.checksums.NoOpPayloadChecksumStore;
2426
import software.amazon.awssdk.core.internal.chunked.AwsChunkedEncodingConfig;
27+
import software.amazon.awssdk.http.auth.spi.signer.PayloadChecksumStore;
2528
import software.amazon.awssdk.utils.Validate;
2629

2730
/**
@@ -45,7 +48,9 @@ public abstract class AwsChunkedEncodingInputStream extends AwsChunkedInputStrea
4548
protected boolean isTrailingTerminated = true;
4649
private final int chunkSize;
4750
private final int maxBufferSize;
51+
private final ChecksumAlgorithm checksumAlgorithm;
4852
private final SdkChecksum sdkChecksum;
53+
private final PayloadChecksumStore checksumStore;
4954
private boolean isLastTrailingCrlf;
5055

5156
/**
@@ -58,7 +63,10 @@ public abstract class AwsChunkedEncodingInputStream extends AwsChunkedInputStrea
5863
* See {@link AwsChunkedEncodingConfig} for default values.
5964
*/
6065
protected AwsChunkedEncodingInputStream(InputStream in,
61-
SdkChecksum sdkChecksum, String checksumHeaderForTrailer,
66+
ChecksumAlgorithm checksumAlgorithm,
67+
SdkChecksum sdkChecksum,
68+
PayloadChecksumStore checksumStore,
69+
String checksumHeaderForTrailer,
6270
AwsChunkedEncodingConfig config) {
6371
AwsChunkedEncodingConfig awsChunkedEncodingConfig = config == null ? AwsChunkedEncodingConfig.create() : config;
6472

@@ -78,14 +86,18 @@ protected AwsChunkedEncodingInputStream(InputStream in,
7886
if (maxBufferSize < chunkSize) {
7987
throw new IllegalArgumentException("Max buffer size should not be less than chunk size");
8088
}
89+
this.checksumAlgorithm = checksumAlgorithm;
8190
this.sdkChecksum = sdkChecksum;
91+
this.checksumStore = checksumStore == null ? NoOpPayloadChecksumStore.create() : checksumStore;
8292
this.checksumHeaderForTrailer = checksumHeaderForTrailer;
8393
}
8494

8595
protected abstract static class Builder<T extends Builder> {
8696

8797
protected InputStream inputStream;
98+
protected ChecksumAlgorithm checksumAlgorithm;
8899
protected SdkChecksum sdkChecksum;
100+
protected PayloadChecksumStore checksumStore;
89101
protected String checksumHeaderForTrailer;
90102
protected AwsChunkedEncodingConfig awsChunkedEncodingConfig;
91103

@@ -110,6 +122,11 @@ public T awsChunkedEncodingConfig(AwsChunkedEncodingConfig awsChunkedEncodingCon
110122
return (T) this;
111123
}
112124

125+
public T checksumAlgorithm(ChecksumAlgorithm checksumAlgorithm) {
126+
this.checksumAlgorithm = checksumAlgorithm;
127+
return (T) this;
128+
}
129+
113130
/**
114131
*
115132
* @param sdkChecksum Instance of SdkChecksum, this can be null if we do not want to calculate Checksum
@@ -120,6 +137,11 @@ public T sdkChecksum(SdkChecksum sdkChecksum) {
120137
return (T) this;
121138
}
122139

140+
public T checksumStore(PayloadChecksumStore checksumStore) {
141+
this.checksumStore = checksumStore;
142+
return (T) this;
143+
}
144+
123145
/**
124146
*
125147
* @param checksumHeaderForTrailer String value of Trailer header where checksum will be updated.
@@ -166,7 +188,11 @@ private boolean setUpTrailingChunks() {
166188
return true;
167189
}
168190
if (calculatedChecksum == null) {
169-
calculatedChecksum = sdkChecksum.getChecksumBytes();
191+
calculatedChecksum = checksumStore.getChecksumValue(checksumAlgorithm);
192+
if (calculatedChecksum == null) {
193+
calculatedChecksum = sdkChecksum.getChecksumBytes();
194+
checksumStore.putChecksumValue(checksumAlgorithm, calculatedChecksum);
195+
}
170196
currentChunkIterator = new ChunkContentIterator(createChecksumChunkHeader());
171197
return false;
172198
} else if (!isLastTrailingCrlf) {

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/io/AwsUnsignedChunkedEncodingInputStream.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
import java.io.InputStream;
1919
import java.nio.charset.StandardCharsets;
2020
import software.amazon.awssdk.annotations.SdkInternalApi;
21+
import software.amazon.awssdk.checksums.spi.ChecksumAlgorithm;
2122
import software.amazon.awssdk.core.checksums.SdkChecksum;
2223
import software.amazon.awssdk.core.exception.SdkClientException;
2324
import software.amazon.awssdk.core.internal.chunked.AwsChunkedEncodingConfig;
25+
import software.amazon.awssdk.http.auth.spi.signer.PayloadChecksumStore;
2426
import software.amazon.awssdk.utils.BinaryUtils;
2527

2628
/**
@@ -29,10 +31,13 @@
2931
@SdkInternalApi
3032
public class AwsUnsignedChunkedEncodingInputStream extends AwsChunkedEncodingInputStream {
3133

32-
private AwsUnsignedChunkedEncodingInputStream(InputStream in, AwsChunkedEncodingConfig awsChunkedEncodingConfig,
34+
private AwsUnsignedChunkedEncodingInputStream(InputStream in,
35+
AwsChunkedEncodingConfig awsChunkedEncodingConfig,
36+
ChecksumAlgorithm checksumAlgorithm,
3337
SdkChecksum sdkChecksum,
38+
PayloadChecksumStore checksumStore,
3439
String checksumHeaderForTrailer) {
35-
super(in, sdkChecksum, checksumHeaderForTrailer, awsChunkedEncodingConfig);
40+
super(in, checksumAlgorithm, sdkChecksum, checksumStore, checksumHeaderForTrailer, awsChunkedEncodingConfig);
3641
}
3742

3843
public static Builder builder() {
@@ -85,8 +90,12 @@ protected byte[] createChecksumChunkHeader() {
8590
public static final class Builder extends AwsChunkedEncodingInputStream.Builder<Builder> {
8691
public AwsUnsignedChunkedEncodingInputStream build() {
8792
return new AwsUnsignedChunkedEncodingInputStream(
88-
this.inputStream, this.awsChunkedEncodingConfig,
89-
this.sdkChecksum, this.checksumHeaderForTrailer);
93+
this.inputStream,
94+
this.awsChunkedEncodingConfig,
95+
this.checksumAlgorithm,
96+
this.sdkChecksum,
97+
this.checksumStore,
98+
this.checksumHeaderForTrailer);
9099
}
91100
}
92101
}

0 commit comments

Comments
 (0)