AWS SDK for C++

AWS SDK for C++ Version 1.11.440

Loading...
Searching...
No Matches
ChecksumInterceptor.h
1
6#pragma once
7
8#include <smithy/interceptor/Interceptor.h>
9
10#include <aws/core/AmazonWebServiceRequest.h>
11#include <aws/core/http/HttpRequest.h>
12#include <aws/core/http/HttpResponse.h>
13#include <aws/core/utils/HashingUtils.h>
14#include <aws/core/utils/crypto/MD5.h>
15#include <aws/core/utils/crypto/CRC32.h>
16#include <aws/core/utils/crypto/Sha256.h>
17#include <aws/core/utils/crypto/Sha1.h>
18#include <aws/core/utils/crypto/PrecalculatedHash.h>
19#include <aws/core/platform/Environment.h>
20
21#include <iomanip>
22
23namespace smithy
24{
25 namespace client
26 {
27 static const char AWS_SMITHY_CLIENT_CHECKSUM[] = "AwsSmithyClientChecksums";
28
29 static const char CHECKSUM_CONTENT_MD5_HEADER[] = "content-md5";
30
32 {
33 public:
42
43 ~ChecksumInterceptor() override = default;
46 ChecksumInterceptor(ChecksumInterceptor&& other) noexcept = default;
49
50 static std::shared_ptr<Aws::IOStream> GetBodyStream(const Aws::AmazonWebServiceRequest& request)
51 {
52 if (request.GetBody() != nullptr)
53 {
54 return request.GetBody();
55 }
56 // Return an empty string stream for no body
57 return Aws::MakeShared<Aws::StringStream>(AWS_SMITHY_CLIENT_CHECKSUM, "");
58 }
59
61 {
62 const auto& httpRequest = context.GetTransmitRequest();
63 const auto& request = context.GetModeledRequest();
64 if (httpRequest == nullptr)
65 {
67 "ValidationErrorException",
68 "Checksum request validation missing request",
69 false};
70 }
71 Aws::String checksumAlgorithmName = Aws::Utils::StringUtils::ToLower(
72 request.GetChecksumAlgorithmName().c_str());
73 if (request.GetServiceSpecificParameters())
74 {
75 auto requestChecksumOverride = request.GetServiceSpecificParameters()->parameterMap.find(
76 "overrideChecksum");
77 if (requestChecksumOverride != request.GetServiceSpecificParameters()->parameterMap.end())
78 {
79 checksumAlgorithmName = requestChecksumOverride->second;
80 }
81 }
82
83 bool shouldSkipChecksum = request.GetServiceSpecificParameters() &&
84 request.GetServiceSpecificParameters()->parameterMap.find("overrideChecksumDisable") !=
85 request.GetServiceSpecificParameters()->parameterMap.end();
86
87 //Check if user has provided the checksum algorithm
88 if (!checksumAlgorithmName.empty() && !shouldSkipChecksum)
89 {
90 // Check if user has provided a checksum value for the specified algorithm
91 const Aws::String checksumType = "x-amz-checksum-" + checksumAlgorithmName;
92 const HeaderValueCollection& headers = request.GetHeaders();
93 const auto checksumHeader = headers.find(checksumType);
94 bool checksumValueAndAlgorithmProvided = checksumHeader != headers.end();
95
96 // For non-streaming payload, the resolved checksum location is always header.
97 // For streaming payload, the resolved checksum location depends on whether it is an unsigned payload, we let AwsAuthSigner decide it.
98 if (request.IsStreaming() && checksumValueAndAlgorithmProvided)
99 {
100 const auto hash = Aws::MakeShared<PrecalculatedHash>(
101 AWS_SMITHY_CLIENT_CHECKSUM, checksumHeader->second);
102 httpRequest->SetRequestHash(checksumAlgorithmName, hash);
103 }
104 else if (checksumValueAndAlgorithmProvided)
105 {
106 httpRequest->SetHeaderValue(checksumType, checksumHeader->second);
107 }
108 else if (checksumAlgorithmName == "crc32")
109 {
110 if (request.IsStreaming())
111 {
112 httpRequest->SetRequestHash(checksumAlgorithmName,
113 Aws::MakeShared<CRC32>(AWS_SMITHY_CLIENT_CHECKSUM));
114 }
115 else
116 {
117 httpRequest->SetHeaderValue(checksumType,
120 }
121 }
122 else if (checksumAlgorithmName == "crc32c")
123 {
124 if (request.IsStreaming())
125 {
126 httpRequest->SetRequestHash(checksumAlgorithmName,
127 Aws::MakeShared<CRC32C>(AWS_SMITHY_CLIENT_CHECKSUM));
128 }
129 else
130 {
131 httpRequest->SetHeaderValue(checksumType,
134 }
135 }
136 else if (checksumAlgorithmName == "sha256")
137 {
138 if (request.IsStreaming())
139 {
140 httpRequest->SetRequestHash(checksumAlgorithmName,
141 Aws::MakeShared<Sha256>(AWS_SMITHY_CLIENT_CHECKSUM));
142 }
143 else
144 {
145 httpRequest->SetHeaderValue(checksumType,
148 }
149 }
150 else if (checksumAlgorithmName == "sha1")
151 {
152 if (request.IsStreaming())
153 {
154 httpRequest->SetRequestHash(checksumAlgorithmName,
155 Aws::MakeShared<Sha1>(AWS_SMITHY_CLIENT_CHECKSUM));
156 }
157 else
158 {
159 httpRequest->SetHeaderValue(checksumType,
162 }
163 }
164 else if (checksumAlgorithmName == "md5" && headers.find(CHECKSUM_CONTENT_MD5_HEADER) == headers.end())
165 {
166 httpRequest->SetHeaderValue(CHECKSUM_CONTENT_MD5_HEADER,
169 }
170 else
171 {
172 AWS_LOGSTREAM_WARN(AWS_SMITHY_CLIENT_CHECKSUM,
173 "Checksum algorithm: " << checksumAlgorithmName <<
174 "is not supported by SDK.");
175 }
176 }
177
178 // Response checksums
179 if (request.ShouldValidateResponseChecksum())
180 {
181 for (const Aws::String& responseChecksumAlgorithmName : request.GetResponseChecksumAlgorithmNames())
182 {
183 checksumAlgorithmName = Aws::Utils::StringUtils::ToLower(responseChecksumAlgorithmName.c_str());
184
185 if (checksumAlgorithmName == "crc32c")
186 {
187 std::shared_ptr<CRC32C> crc32c = Aws::MakeShared<
189 httpRequest->AddResponseValidationHash("crc32c", crc32c);
190 }
191 else if (checksumAlgorithmName == "crc32")
192 {
193 std::shared_ptr<CRC32> crc32 = Aws::MakeShared<
195 httpRequest->AddResponseValidationHash("crc32", crc32);
196 }
197 else if (checksumAlgorithmName == "sha1")
198 {
199 std::shared_ptr<Sha1> sha1 = Aws::MakeShared<Sha1>(
201 httpRequest->AddResponseValidationHash("sha1", sha1);
202 }
203 else if (checksumAlgorithmName == "sha256")
204 {
205 std::shared_ptr<Sha256> sha256 = Aws::MakeShared<
207 httpRequest->AddResponseValidationHash("sha256", sha256);
208 }
209 else
210 {
211 AWS_LOGSTREAM_WARN(AWS_SMITHY_CLIENT_CHECKSUM,
212 "Checksum algorithm: " << checksumAlgorithmName <<
213 " is not supported in validating response body yet.");
214 }
215 }
216 }
217 return httpRequest;
218 }
219
221 {
222 const auto httpRequest = context.GetTransmitRequest();
223 const auto httpResponse = context.GetTransmitResponse();
224 if (httpRequest == nullptr || httpResponse == nullptr)
225 {
227 "ValidationErrorException",
228 "Checksum response validation missing request or response",
229 false};
230 }
231 for (const auto& hashIterator : httpRequest->GetResponseValidationHashes())
232 {
233 Aws::String checksumHeaderKey = Aws::String("x-amz-checksum-") + hashIterator.first;
234 // TODO: If checksum ends with -#, then skip
235 if (httpResponse->HasHeader(checksumHeaderKey.c_str()))
236 {
237 const Aws::String& checksumHeaderValue = httpResponse->GetHeader(checksumHeaderKey);
238 if (HashingUtils::Base64Encode(hashIterator.second->GetHash().GetResult()) !=
239 checksumHeaderValue)
240 {
243 "Response checksums mismatch",
244 false/*retryable*/};
245 error.SetResponseHeaders(httpResponse->GetHeaders());
246 error.SetResponseCode(httpResponse->GetResponseCode());
247 error.SetRemoteHostIpAddress(
248 httpResponse->GetOriginatingRequest().GetResolvedRemoteHost());
249
250 AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_CHECKSUM, error);
251 return {error};
252 }
253 // Validate only a single checksum returned in an HTTP response
254 break;
255 }
256 }
257 return httpResponse;
258 }
259 };
260 }
261}
virtual std::shared_ptr< Aws::IOStream > GetBody() const =0
void SetResponseHeaders(const Aws::Http::HeaderValueCollection &headers)
Definition AWSError.h:194
static ByteBuffer CalculateCRC32(const Aws::String &str)
static Aws::String Base64Encode(const ByteBuffer &byteBuffer)
static ByteBuffer CalculateMD5(const Aws::String &str)
static ByteBuffer CalculateSHA1(const Aws::String &str)
static ByteBuffer CalculateCRC32C(const Aws::String &str)
static ByteBuffer CalculateSHA256(const Aws::String &str)
static Aws::String ToLower(const char *source)
Aws::Http::HeaderValueCollection HeaderValueCollection
ChecksumInterceptor(const ChecksumInterceptor &other)=delete
ChecksumInterceptor & operator=(ChecksumInterceptor &&other) noexcept=default
ModifyRequestOutcome ModifyBeforeSigning(interceptor::InterceptorContext &context) override
ChecksumInterceptor & operator=(const ChecksumInterceptor &other)=delete
ModifyResponseOutcome ModifyBeforeDeserialization(interceptor::InterceptorContext &context) override
~ChecksumInterceptor() override=default
static std::shared_ptr< Aws::IOStream > GetBodyStream(const Aws::AmazonWebServiceRequest &request)
ChecksumInterceptor(ChecksumInterceptor &&other) noexcept=default
const Aws::AmazonWebServiceRequest & GetModeledRequest() const
std::shared_ptr< Aws::Http::HttpRequest > GetTransmitRequest() const
std::shared_ptr< Aws::Http::HttpResponse > GetTransmitResponse() const
Aws::Map< Aws::String, Aws::String > HeaderValueCollection
Definition HttpTypes.h:62
std::shared_ptr< T > MakeShared(const char *allocationTag, ArgTypes &&... args)
std::basic_string< char, std::char_traits< char >, Aws::Allocator< char > > String
Definition AWSString.h:97
static const char AWS_SMITHY_CLIENT_CHECKSUM[]
static const char CHECKSUM_CONTENT_MD5_HEADER[]