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.core.internal.interceptor; 17 18 import static software.amazon.awssdk.core.ClientType.ASYNC; 19 import static software.amazon.awssdk.core.ClientType.SYNC; 20 import static software.amazon.awssdk.core.internal.util.HttpChecksumUtils.getAlgorithmChecksumValuePair; 21 22 import java.io.InputStream; 23 import java.nio.ByteBuffer; 24 import java.util.Optional; 25 import java.util.function.Predicate; 26 import org.reactivestreams.Publisher; 27 import software.amazon.awssdk.annotations.SdkInternalApi; 28 import software.amazon.awssdk.core.ClientType; 29 import software.amazon.awssdk.core.checksums.Algorithm; 30 import software.amazon.awssdk.core.checksums.ChecksumSpecs; 31 import software.amazon.awssdk.core.checksums.ChecksumValidation; 32 import software.amazon.awssdk.core.checksums.SdkChecksum; 33 import software.amazon.awssdk.core.interceptor.Context; 34 import software.amazon.awssdk.core.interceptor.ExecutionAttributes; 35 import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; 36 import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; 37 import software.amazon.awssdk.core.internal.async.ChecksumValidatingPublisher; 38 import software.amazon.awssdk.core.internal.io.ChecksumValidatingInputStream; 39 import software.amazon.awssdk.core.internal.util.HttpChecksumResolver; 40 import software.amazon.awssdk.core.internal.util.HttpChecksumUtils; 41 import software.amazon.awssdk.utils.Pair; 42 43 /** 44 * Interceptor to intercepts Sync and Async responses. 45 * The Http Checksum is computed and validated with the one that is passed in the header of the response. 46 */ 47 @SdkInternalApi 48 public final class HttpChecksumValidationInterceptor implements ExecutionInterceptor { 49 50 private static final Predicate<ExecutionAttributes> IS_FORCE_SKIPPED_VALIDATION = 51 ex -> ChecksumValidation.FORCE_SKIP.equals( 52 ex.getOptionalAttribute(SdkExecutionAttribute.HTTP_RESPONSE_CHECKSUM_VALIDATION).orElse(null)); 53 54 @Override modifyHttpResponseContent(Context.ModifyHttpResponse context, ExecutionAttributes executionAttributes)55 public Optional<InputStream> modifyHttpResponseContent(Context.ModifyHttpResponse context, 56 ExecutionAttributes executionAttributes) { 57 58 ChecksumSpecs resolvedChecksumSpecs = HttpChecksumResolver.getResolvedChecksumSpecs(executionAttributes); 59 if (resolvedChecksumSpecs != null && 60 isFlexibleChecksumValidationForResponse(executionAttributes, resolvedChecksumSpecs, SYNC)) { 61 62 Pair<Algorithm, String> algorithmChecksumPair = getAlgorithmChecksumValuePair( 63 context.httpResponse(), resolvedChecksumSpecs); 64 updateContextWithChecksumValidationStatus(executionAttributes, algorithmChecksumPair); 65 66 if (algorithmChecksumPair != null && context.responseBody().isPresent()) { 67 return Optional.of(new ChecksumValidatingInputStream( 68 context.responseBody().get(), SdkChecksum.forAlgorithm(algorithmChecksumPair.left()), 69 algorithmChecksumPair.right())); 70 } 71 } 72 return context.responseBody(); 73 } 74 75 @Override modifyAsyncHttpResponseContent(Context.ModifyHttpResponse context, ExecutionAttributes executionAttributes)76 public Optional<Publisher<ByteBuffer>> modifyAsyncHttpResponseContent(Context.ModifyHttpResponse context, 77 ExecutionAttributes executionAttributes) { 78 ChecksumSpecs resolvedChecksumSpecs = HttpChecksumResolver.getResolvedChecksumSpecs(executionAttributes); 79 if (resolvedChecksumSpecs != null && 80 isFlexibleChecksumValidationForResponse(executionAttributes, resolvedChecksumSpecs, ASYNC)) { 81 Pair<Algorithm, String> algorithmChecksumPair = getAlgorithmChecksumValuePair(context.httpResponse(), 82 resolvedChecksumSpecs); 83 updateContextWithChecksumValidationStatus(executionAttributes, algorithmChecksumPair); 84 if (algorithmChecksumPair != null && context.responsePublisher().isPresent()) { 85 return Optional.of(new ChecksumValidatingPublisher(context.responsePublisher().get(), 86 SdkChecksum.forAlgorithm(algorithmChecksumPair.left()), 87 algorithmChecksumPair.right())); 88 } 89 } 90 return context.responsePublisher(); 91 } 92 updateContextWithChecksumValidationStatus(ExecutionAttributes executionAttributes, Pair<Algorithm, String> algorithmChecksumPair)93 private void updateContextWithChecksumValidationStatus(ExecutionAttributes executionAttributes, 94 Pair<Algorithm, String> algorithmChecksumPair) { 95 if (algorithmChecksumPair == null || algorithmChecksumPair.left() == null) { 96 executionAttributes.putAttribute(SdkExecutionAttribute.HTTP_RESPONSE_CHECKSUM_VALIDATION, 97 ChecksumValidation.CHECKSUM_ALGORITHM_NOT_FOUND); 98 } else { 99 executionAttributes.putAttribute(SdkExecutionAttribute.HTTP_RESPONSE_CHECKSUM_VALIDATION, 100 ChecksumValidation.VALIDATED); 101 executionAttributes.putAttribute(SdkExecutionAttribute.HTTP_CHECKSUM_VALIDATION_ALGORITHM, 102 algorithmChecksumPair.left()); 103 } 104 } 105 isFlexibleChecksumValidationForResponse(ExecutionAttributes executionAttributes, ChecksumSpecs checksumSpecs, ClientType clientType)106 private boolean isFlexibleChecksumValidationForResponse(ExecutionAttributes executionAttributes, 107 ChecksumSpecs checksumSpecs, 108 ClientType clientType) { 109 110 return HttpChecksumUtils.isHttpChecksumValidationEnabled(checksumSpecs) && 111 executionAttributes.getAttribute(SdkExecutionAttribute.CLIENT_TYPE).equals(clientType) && 112 !IS_FORCE_SKIPPED_VALIDATION.test(executionAttributes); 113 } 114 }