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.http; 17 18 import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely; 19 20 import java.util.Optional; 21 import java.util.zip.GZIPInputStream; 22 import software.amazon.awssdk.annotations.SdkProtectedApi; 23 import software.amazon.awssdk.core.internal.util.Crc32ChecksumValidatingInputStream; 24 import software.amazon.awssdk.http.AbortableInputStream; 25 import software.amazon.awssdk.http.SdkHttpFullResponse; 26 27 /** 28 * Validate and decompress input data if necessary. 29 */ 30 @SdkProtectedApi 31 public final class Crc32Validation { 32 Crc32Validation()33 private Crc32Validation() { 34 } 35 validate(boolean calculateCrc32FromCompressedData, SdkHttpFullResponse httpResponse)36 public static SdkHttpFullResponse validate(boolean calculateCrc32FromCompressedData, 37 SdkHttpFullResponse httpResponse) { 38 39 if (!httpResponse.content().isPresent()) { 40 return httpResponse; 41 } 42 43 return httpResponse.toBuilder().content( 44 process(calculateCrc32FromCompressedData, httpResponse, 45 httpResponse.content().get())).build(); 46 } 47 process(boolean calculateCrc32FromCompressedData, SdkHttpFullResponse httpResponse, AbortableInputStream content)48 private static AbortableInputStream process(boolean calculateCrc32FromCompressedData, 49 SdkHttpFullResponse httpResponse, 50 AbortableInputStream content) { 51 Optional<Long> crc32Checksum = getCrc32Checksum(httpResponse); 52 53 if (shouldDecompress(httpResponse)) { 54 if (calculateCrc32FromCompressedData && crc32Checksum.isPresent()) { 55 return decompressing(crc32Validating(content, crc32Checksum.get())); 56 } 57 58 if (crc32Checksum.isPresent()) { 59 return crc32Validating(decompressing(content), crc32Checksum.get()); 60 } 61 62 return decompressing(content); 63 64 } 65 66 return crc32Checksum.map(aLong -> crc32Validating(content, aLong)).orElse(content); 67 } 68 crc32Validating(AbortableInputStream source, long expectedChecksum)69 private static AbortableInputStream crc32Validating(AbortableInputStream source, long expectedChecksum) { 70 return AbortableInputStream.create(new Crc32ChecksumValidatingInputStream(source, expectedChecksum), source); 71 } 72 getCrc32Checksum(SdkHttpFullResponse httpResponse)73 private static Optional<Long> getCrc32Checksum(SdkHttpFullResponse httpResponse) { 74 return httpResponse.firstMatchingHeader("x-amz-crc32") 75 .map(Long::valueOf); 76 } 77 shouldDecompress(SdkHttpFullResponse httpResponse)78 private static boolean shouldDecompress(SdkHttpFullResponse httpResponse) { 79 return httpResponse.firstMatchingHeader("Content-Encoding") 80 .filter(e -> e.equals("gzip")) 81 .isPresent(); 82 } 83 decompressing(AbortableInputStream source)84 private static AbortableInputStream decompressing(AbortableInputStream source) { 85 return AbortableInputStream.create(invokeSafely(() -> new GZIPInputStream(source)), source); 86 } 87 } 88