1
15
16 package software.amazon.awssdk.services.s3.internal.handlers;
17
18 import static software.amazon.awssdk.core.ClientType.SYNC;
19 import static software.amazon.awssdk.services.s3.checksums.ChecksumConstant.CONTENT_LENGTH_HEADER;
20 import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.CHECKSUM;
21 import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.getObjectChecksumEnabledPerResponse;
22 import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.responseChecksumIsValid;
23 import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.shouldRecordChecksum;
24 import static software.amazon.awssdk.services.s3.checksums.ChecksumsEnabledValidator.validatePutObjectChecksum;
25 import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;
26
27 import java.io.InputStream;
28 import java.util.Optional;
29 import software.amazon.awssdk.annotations.SdkInternalApi;
30 import software.amazon.awssdk.core.checksums.Md5Checksum;
31 import software.amazon.awssdk.core.checksums.SdkChecksum;
32 import software.amazon.awssdk.core.interceptor.Context;
33 import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
34 import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
35 import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
36 import software.amazon.awssdk.core.sync.RequestBody;
37 import software.amazon.awssdk.http.ContentStreamProvider;
38 import software.amazon.awssdk.services.s3.checksums.ChecksumCalculatingInputStream;
39 import software.amazon.awssdk.services.s3.checksums.ChecksumValidatingInputStream;
40 import software.amazon.awssdk.services.s3.model.PutObjectResponse;
41
42 @SdkInternalApi
43 public final class SyncChecksumValidationInterceptor implements ExecutionInterceptor {
44 private static ExecutionAttribute<Boolean> SYNC_RECORDING_CHECKSUM = new ExecutionAttribute<>("syncRecordingChecksum");
45
46 @Override
47 public Optional<RequestBody> modifyHttpContent(Context.ModifyHttpRequest context,
48 ExecutionAttributes executionAttributes) {
49 if (shouldRecordChecksum(context.request(), SYNC, executionAttributes, context.httpRequest())
50 && context.requestBody().isPresent()) {
51 SdkChecksum checksum = new Md5Checksum();
52 executionAttributes.putAttribute(CHECKSUM, checksum);
53 executionAttributes.putAttribute(SYNC_RECORDING_CHECKSUM, true);
54
55 RequestBody requestBody = context.requestBody().get();
56
57 ChecksumCalculatingStreamProvider streamProvider =
58 new ChecksumCalculatingStreamProvider(requestBody.contentStreamProvider(), checksum);
59
60 return Optional.of(RequestBody.fromContentProvider(streamProvider,
61 requestBody.contentLength(),
62 requestBody.contentType()));
63 }
64
65 return context.requestBody();
66 }
67
68 @Override
69 public Optional<InputStream> modifyHttpResponseContent(Context.ModifyHttpResponse context,
70 ExecutionAttributes executionAttributes) {
71 if (getObjectChecksumEnabledPerResponse(context.request(), context.httpResponse())
72 && context.responseBody().isPresent()) {
73
74 SdkChecksum checksum = new Md5Checksum();
75
76 long contentLength = context.httpResponse()
77 .firstMatchingHeader(CONTENT_LENGTH_HEADER)
78 .map(Long::parseLong)
79 .orElse(0L);
80
81 if (contentLength > 0) {
82 return Optional.of(new ChecksumValidatingInputStream(context.responseBody().get(), checksum, contentLength));
83 }
84 }
85
86 return context.responseBody();
87 }
88
89 @Override
90 public void afterUnmarshalling(Context.AfterUnmarshalling context, ExecutionAttributes executionAttributes) {
91 boolean recordingChecksum = Boolean.TRUE.equals(executionAttributes.getAttribute(SYNC_RECORDING_CHECKSUM));
92 boolean responseChecksumIsValid = responseChecksumIsValid(context.httpResponse());
93
94 if (recordingChecksum && responseChecksumIsValid) {
95 validatePutObjectChecksum((PutObjectResponse) context.response(), executionAttributes);
96 }
97 }
98
99 static final class ChecksumCalculatingStreamProvider implements ContentStreamProvider {
100 private final SdkChecksum checksum;
101 private InputStream currentStream;
102 private final ContentStreamProvider underlyingInputStreamProvider;
103
104 ChecksumCalculatingStreamProvider(ContentStreamProvider underlyingInputStreamProvider, SdkChecksum checksum) {
105 this.underlyingInputStreamProvider = underlyingInputStreamProvider;
106 this.checksum = checksum;
107 }
108
109 @Override
110 public InputStream newStream() {
111 closeCurrentStream();
112 currentStream = invokeSafely(() -> new ChecksumCalculatingInputStream(underlyingInputStreamProvider.newStream(),
113 checksum));
114 return currentStream;
115 }
116
117 private void closeCurrentStream() {
118 checksum.reset();
119 if (currentStream != null) {
120 invokeSafely(currentStream::close);
121 currentStream = null;
122 }
123 }
124 }
125
126 }
127