1
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
30 @SdkProtectedApi
31 public final class Crc32Validation {
32
33 private Crc32Validation() {
34 }
35
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
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
69 private static AbortableInputStream crc32Validating(AbortableInputStream source, long expectedChecksum) {
70 return AbortableInputStream.create(new Crc32ChecksumValidatingInputStream(source, expectedChecksum), source);
71 }
72
73 private static Optional<Long> getCrc32Checksum(SdkHttpFullResponse httpResponse) {
74 return httpResponse.firstMatchingHeader("x-amz-crc32")
75 .map(Long::valueOf);
76 }
77
78 private static boolean shouldDecompress(SdkHttpFullResponse httpResponse) {
79 return httpResponse.firstMatchingHeader("Content-Encoding")
80 .filter(e -> e.equals("gzip"))
81 .isPresent();
82 }
83
84 private static AbortableInputStream decompressing(AbortableInputStream source) {
85 return AbortableInputStream.create(invokeSafely(() -> new GZIPInputStream(source)), source);
86 }
87 }
88