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 java.util.Optional; 19 import java.util.regex.Pattern; 20 import software.amazon.awssdk.annotations.SdkInternalApi; 21 import software.amazon.awssdk.core.SdkResponse; 22 import software.amazon.awssdk.core.checksums.Algorithm; 23 import software.amazon.awssdk.core.checksums.ChecksumSpecs; 24 import software.amazon.awssdk.core.checksums.ChecksumValidation; 25 import software.amazon.awssdk.core.interceptor.Context; 26 import software.amazon.awssdk.core.interceptor.ExecutionAttributes; 27 import software.amazon.awssdk.core.interceptor.ExecutionInterceptor; 28 import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute; 29 import software.amazon.awssdk.core.internal.util.HttpChecksumResolver; 30 import software.amazon.awssdk.core.internal.util.HttpChecksumUtils; 31 import software.amazon.awssdk.http.SdkHttpResponse; 32 import software.amazon.awssdk.services.s3.model.GetObjectRequest; 33 import software.amazon.awssdk.services.s3.model.GetObjectResponse; 34 import software.amazon.awssdk.utils.Pair; 35 36 /** 37 * Interceptor for {@link GetObjectRequest} messages. 38 */ 39 @SdkInternalApi 40 public class GetObjectInterceptor implements ExecutionInterceptor { 41 42 public static final Pattern MULTIPART_CHECKSUM_PATTERN = Pattern.compile(".*-\\d+$"); 43 44 @Override afterTransmission(Context.AfterTransmission context, ExecutionAttributes executionAttributes)45 public void afterTransmission(Context.AfterTransmission context, ExecutionAttributes executionAttributes) { 46 47 if (!(context.request() instanceof GetObjectRequest)) { 48 return; 49 } 50 ChecksumSpecs resolvedChecksumSpecs = HttpChecksumResolver.getResolvedChecksumSpecs(executionAttributes); 51 52 if (HttpChecksumUtils.isHttpChecksumValidationEnabled(resolvedChecksumSpecs)) { 53 Pair<Algorithm, String> algorithmChecksumValuePair = 54 HttpChecksumUtils.getAlgorithmChecksumValuePair(context.httpResponse(), resolvedChecksumSpecs); 55 56 // Multipart uploaded objects the received Checksum is the checksum of checksums of individual parts and part number. 57 if (algorithmChecksumValuePair != null && 58 algorithmChecksumValuePair.right() != null) { 59 if (MULTIPART_CHECKSUM_PATTERN.matcher(algorithmChecksumValuePair.right()).matches()) { 60 executionAttributes.putAttribute(SdkExecutionAttribute.HTTP_RESPONSE_CHECKSUM_VALIDATION, 61 ChecksumValidation.FORCE_SKIP); 62 } 63 } 64 } 65 } 66 67 @Override modifyResponse(Context.ModifyResponse context, ExecutionAttributes executionAttributes)68 public SdkResponse modifyResponse(Context.ModifyResponse context, ExecutionAttributes executionAttributes) { 69 SdkResponse response = context.response(); 70 if (!(response instanceof GetObjectResponse)) { 71 return response; 72 } 73 74 return fixContentRange(response, context.httpResponse()); 75 } 76 77 /** 78 * S3 currently returns content-range in two possible headers: Content-Range or x-amz-content-range based on the x-amz-te in 79 * the request. This will check the x-amz-content-range if the modeled header (Content-Range) wasn't populated. 80 */ fixContentRange(SdkResponse sdkResponse, SdkHttpResponse httpResponse)81 private SdkResponse fixContentRange(SdkResponse sdkResponse, SdkHttpResponse httpResponse) { 82 // Use the modeled content range header, if the service returned it. 83 GetObjectResponse getObjectResponse = (GetObjectResponse) sdkResponse; 84 if (getObjectResponse.contentRange() != null) { 85 return getObjectResponse; 86 } 87 88 // If the service didn't use the modeled content range header, check the x-amz-content-range header. 89 Optional<String> xAmzContentRange = httpResponse.firstMatchingHeader("x-amz-content-range"); 90 if (!xAmzContentRange.isPresent()) { 91 return getObjectResponse; 92 } 93 94 return getObjectResponse.copy(r -> r.contentRange(xAmzContentRange.get())); 95 } 96 } 97