1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.services.s3.internal.handlers; 17 18 import static software.amazon.awssdk.core.ClientType.ASYNC; 19 import static software.amazon.awssdk.services.s3.internal.checksums.ChecksumConstant.CONTENT_LENGTH_HEADER; 20 import static software.amazon.awssdk.services.s3.internal.checksums.ChecksumsEnabledValidator.CHECKSUM; 21 import static software.amazon.awssdk.services.s3.internal.checksums.ChecksumsEnabledValidator.getObjectChecksumEnabledPerResponse; 22 import static software.amazon.awssdk.services.s3.internal.checksums.ChecksumsEnabledValidator.responseChecksumIsValid; 23 import static software.amazon.awssdk.services.s3.internal.checksums.ChecksumsEnabledValidator.shouldRecordChecksum; 24 import static software.amazon.awssdk.services.s3.internal.checksums.ChecksumsEnabledValidator.validatePutObjectChecksum; 25 26 import java.nio.ByteBuffer; 27 import java.util.Optional; 28 import org.reactivestreams.Publisher; 29 import software.amazon.awssdk.annotations.SdkInternalApi; 30 import software.amazon.awssdk.core.async.AsyncRequestBody; 31 import software.amazon.awssdk.core.checksums.Md5Checksum; 32 import software.amazon.awssdk.core.checksums.SdkChecksum; 33 import software.amazon.awssdk.core.interceptor.Context; 34 import software.amazon.awssdk.core.interceptor.ExecutionAttribute; 35 import software.amazon.awssdk.core.interceptor.ExecutionAttributes; 36 import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; 37 import software.amazon.awssdk.services.s3.internal.checksums.ChecksumCalculatingAsyncRequestBody; 38 import software.amazon.awssdk.services.s3.internal.checksums.S3ChecksumValidatingPublisher; 39 import software.amazon.awssdk.services.s3.model.PutObjectResponse; 40 41 @SdkInternalApi 42 public final class AsyncChecksumValidationInterceptor implements ExecutionInterceptor { 43 private static ExecutionAttribute<Boolean> ASYNC_RECORDING_CHECKSUM = new ExecutionAttribute<>("asyncRecordingChecksum"); 44 45 @Override modifyAsyncHttpContent(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes)46 public Optional<AsyncRequestBody> modifyAsyncHttpContent(Context.ModifyHttpRequest context, 47 ExecutionAttributes executionAttributes) { 48 boolean shouldRecordChecksum = shouldRecordChecksum(context.request(), ASYNC, executionAttributes, context.httpRequest()); 49 50 if (shouldRecordChecksum && context.asyncRequestBody().isPresent()) { 51 SdkChecksum checksum = new Md5Checksum(); 52 executionAttributes.putAttribute(ASYNC_RECORDING_CHECKSUM, true); 53 executionAttributes.putAttribute(CHECKSUM, checksum); 54 return Optional.of(new ChecksumCalculatingAsyncRequestBody(context.httpRequest(), 55 context.asyncRequestBody().get(), 56 checksum)); 57 } 58 59 return context.asyncRequestBody(); 60 } 61 62 @Override modifyAsyncHttpResponseContent(Context.ModifyHttpResponse context, ExecutionAttributes executionAttributes)63 public Optional<Publisher<ByteBuffer>> modifyAsyncHttpResponseContent(Context.ModifyHttpResponse context, 64 ExecutionAttributes executionAttributes) { 65 if (getObjectChecksumEnabledPerResponse(context.request(), context.httpResponse()) 66 && context.responsePublisher().isPresent()) { 67 long contentLength = context.httpResponse() 68 .firstMatchingHeader(CONTENT_LENGTH_HEADER) 69 .map(Long::parseLong) 70 .orElse(0L); 71 72 SdkChecksum checksum = new Md5Checksum(); 73 executionAttributes.putAttribute(CHECKSUM, checksum); 74 if (contentLength > 0) { 75 return Optional.of(new S3ChecksumValidatingPublisher(context.responsePublisher().get(), checksum, contentLength)); 76 } 77 } 78 79 return context.responsePublisher(); 80 } 81 82 @Override afterUnmarshalling(Context.AfterUnmarshalling context, ExecutionAttributes executionAttributes)83 public void afterUnmarshalling(Context.AfterUnmarshalling context, ExecutionAttributes executionAttributes) { 84 boolean recordingChecksum = Boolean.TRUE.equals(executionAttributes.getAttribute(ASYNC_RECORDING_CHECKSUM)); 85 boolean responseChecksumIsValid = responseChecksumIsValid(context.httpResponse()); 86 87 if (recordingChecksum && responseChecksumIsValid) { 88 validatePutObjectChecksum((PutObjectResponse) context.response(), executionAttributes); 89 } 90 } 91 } 92