AWS SDK for C++

AWS SDK for C++ Version 1.11.606

Loading...
Searching...
No Matches
ChecksumInterceptor.h
1
6#pragma once
7
8#include <aws/core/AmazonWebServiceRequest.h>
9#include <aws/core/http/HttpRequest.h>
10#include <aws/core/http/HttpResponse.h>
11#include <aws/core/utils/HashingUtils.h>
12#include <aws/core/utils/crypto/CRC32.h>
13#include <aws/core/utils/crypto/CRC64.h>
14#include <aws/core/utils/crypto/MD5.h>
15#include <aws/core/utils/crypto/PrecalculatedHash.h>
16#include <aws/core/utils/crypto/Sha1.h>
17#include <aws/core/utils/crypto/Sha256.h>
18#include <smithy/interceptor/Interceptor.h>
19
20#include <iomanip>
21
22namespace smithy {
23namespace client {
24static const char AWS_SMITHY_CLIENT_CHECKSUM[] = "AwsSmithyClientChecksums";
25
26static const char CHECKSUM_CONTENT_MD5_HEADER[] = "content-md5";
27
29 public:
42
43 explicit ChecksumInterceptor(const ClientConfiguration& configuration)
44 : m_requestChecksumCalculation(configuration.checksumConfig.requestChecksumCalculation),
45 m_responseChecksumValidation(configuration.checksumConfig.responseChecksumValidation) {}
46 ~ChecksumInterceptor() override = default;
49 ChecksumInterceptor(ChecksumInterceptor&& other) noexcept = default;
52
53 static std::shared_ptr<Aws::IOStream> GetBodyStream(const Aws::AmazonWebServiceRequest& request) {
54 if (request.GetBody() != nullptr) {
55 return request.GetBody();
56 }
57 // Return an empty string stream for no body
58 return Aws::MakeShared<Aws::StringStream>(AWS_SMITHY_CLIENT_CHECKSUM, "");
59 }
60
62 const auto& httpRequest = context.GetTransmitRequest();
63 const auto& request = context.GetModeledRequest();
64 if (httpRequest == nullptr) {
66 "Checksum request validation missing request", false};
67 }
68
69 // Add RequestChecksum
70 addChecksumConfigurationFeatures(request);
71 if ((!request.GetChecksumAlgorithmName().empty() && m_requestChecksumCalculation == RequestChecksumCalculation::WHEN_SUPPORTED) ||
72 request.RequestChecksumRequired()) {
73 Aws::String checksumAlgorithmName = Aws::Utils::StringUtils::ToLower(request.GetChecksumAlgorithmName().c_str());
74 // Check if user has provided the checksum algorithm
75 if (!checksumAlgorithmName.empty()) {
76 // Check if user has provided a checksum value for the specified algorithm
77 const Aws::String checksumType = "x-amz-checksum-" + checksumAlgorithmName;
78 const HeaderValueCollection& headers = request.GetHeaders();
79 const auto checksumHeader = headers.find(checksumType);
80 bool checksumValueAndAlgorithmProvided = checksumHeader != headers.end();
81
82 // For non-streaming payload, the resolved checksum location is always header.
83 // For streaming payload, the resolved checksum location depends on whether it is an unsigned payload, we let
84 // AwsAuthSigner decide it.
85 if (request.IsStreaming() && checksumValueAndAlgorithmProvided) {
86 addChecksumFeatureForChecksumName(checksumAlgorithmName, request);
87 const auto hash = Aws::MakeShared<PrecalculatedHash>(AWS_SMITHY_CLIENT_CHECKSUM, checksumHeader->second);
88 httpRequest->SetRequestHash(checksumAlgorithmName, hash);
89 } else if (checksumValueAndAlgorithmProvided) {
90 httpRequest->SetHeaderValue(checksumType, checksumHeader->second);
91 } else if (checksumAlgorithmName == "crc64nvme") {
93 if (request.IsStreaming()) {
94 httpRequest->SetRequestHash(checksumAlgorithmName, Aws::MakeShared<CRC64>(AWS_SMITHY_CLIENT_CHECKSUM));
95 } else {
96 httpRequest->SetHeaderValue(checksumType, HashingUtils::Base64Encode(HashingUtils::CalculateCRC64(*(GetBodyStream(request)))));
97 }
98 } else if (checksumAlgorithmName == "crc32") {
100 if (request.IsStreaming()) {
101 httpRequest->SetRequestHash(checksumAlgorithmName, Aws::MakeShared<CRC32>(AWS_SMITHY_CLIENT_CHECKSUM));
102 } else {
103 httpRequest->SetHeaderValue(checksumType, HashingUtils::Base64Encode(HashingUtils::CalculateCRC32(*(GetBodyStream(request)))));
104 }
105 } else if (checksumAlgorithmName == "crc32c") {
107 if (request.IsStreaming()) {
108 httpRequest->SetRequestHash(checksumAlgorithmName, Aws::MakeShared<CRC32C>(AWS_SMITHY_CLIENT_CHECKSUM));
109 } else {
110 httpRequest->SetHeaderValue(checksumType, HashingUtils::Base64Encode(HashingUtils::CalculateCRC32C(*(GetBodyStream(request)))));
111 }
112 } else if (checksumAlgorithmName == "sha256") {
114 if (request.IsStreaming()) {
115 httpRequest->SetRequestHash(checksumAlgorithmName, Aws::MakeShared<Sha256>(AWS_SMITHY_CLIENT_CHECKSUM));
116 } else {
117 httpRequest->SetHeaderValue(checksumType, HashingUtils::Base64Encode(HashingUtils::CalculateSHA256(*(GetBodyStream(request)))));
118 }
119 } else if (checksumAlgorithmName == "sha1") {
121 if (request.IsStreaming()) {
122 httpRequest->SetRequestHash(checksumAlgorithmName, Aws::MakeShared<Sha1>(AWS_SMITHY_CLIENT_CHECKSUM));
123 } else {
124 httpRequest->SetHeaderValue(checksumType, HashingUtils::Base64Encode(HashingUtils::CalculateSHA1(*(GetBodyStream(request)))));
125 }
126 } else {
127 AWS_LOGSTREAM_WARN(AWS_SMITHY_CLIENT_CHECKSUM, "Checksum algorithm: " << checksumAlgorithmName << "is not supported by SDK.");
128 }
129 }
130 }
131
132 // Add response checksum
133 if ((!request.GetResponseChecksumAlgorithmNames().empty() &&
134 m_responseChecksumValidation == ResponseChecksumValidation::WHEN_SUPPORTED) ||
135 request.ShouldValidateResponseChecksum()) {
136 for (const Aws::String& responseChecksumAlgorithmName : request.GetResponseChecksumAlgorithmNames()) {
137 const auto responseChecksum = Aws::Utils::StringUtils::ToLower(responseChecksumAlgorithmName.c_str());
138 if (responseChecksum == "crc32c") {
139 std::shared_ptr<CRC32C> crc32c = Aws::MakeShared<CRC32C>(AWS_SMITHY_CLIENT_CHECKSUM);
140 httpRequest->AddResponseValidationHash("crc32c", crc32c);
141 } else if (responseChecksum == "crc32") {
142 std::shared_ptr<CRC32> crc32 = Aws::MakeShared<CRC32>(AWS_SMITHY_CLIENT_CHECKSUM);
143 httpRequest->AddResponseValidationHash("crc32", crc32);
144 } else if (responseChecksum == "sha1") {
145 std::shared_ptr<Sha1> sha1 = Aws::MakeShared<Sha1>(AWS_SMITHY_CLIENT_CHECKSUM);
146 httpRequest->AddResponseValidationHash("sha1", sha1);
147 } else if (responseChecksum == "sha256") {
148 std::shared_ptr<Sha256> sha256 = Aws::MakeShared<Sha256>(AWS_SMITHY_CLIENT_CHECKSUM);
149 httpRequest->AddResponseValidationHash("sha256", sha256);
150 } else if (responseChecksum == "crc64nvme") {
151 std::shared_ptr<CRC64> crc64 = Aws::MakeShared<CRC64>(AWS_SMITHY_CLIENT_CHECKSUM);
152 httpRequest->AddResponseValidationHash("crc64nvme", crc64);
153 } else {
154 AWS_LOGSTREAM_WARN(AWS_SMITHY_CLIENT_CHECKSUM,
155 "Checksum algorithm: " << responseChecksum << " is not supported in validating response body yet.");
156 }
157 }
158 // we have to set the checksum mode to enabled if it was not previously
159 httpRequest->SetHeaderValue("x-amz-checksum-mode", "enabled");
160 }
161
162 return httpRequest;
163 }
164
166 const auto httpRequest = context.GetTransmitRequest();
167 const auto httpResponse = context.GetTransmitResponse();
168 if (httpRequest == nullptr || httpResponse == nullptr) {
170 "Checksum response validation missing request or response", false};
171 }
172 for (const auto& hashIterator : httpRequest->GetResponseValidationHashes()) {
173 Aws::String checksumHeaderKey = Aws::String("x-amz-checksum-") + hashIterator.first;
174 // TODO: If checksum ends with -#, then skip
175 if (httpResponse->HasHeader(checksumHeaderKey.c_str())) {
176 const Aws::String& checksumHeaderValue = httpResponse->GetHeader(checksumHeaderKey);
177 if (HashingUtils::Base64Encode(hashIterator.second->GetHash().GetResult()) != checksumHeaderValue) {
179 "Response checksums mismatch", false /*retryable*/};
180 error.SetResponseHeaders(httpResponse->GetHeaders());
181 error.SetResponseCode(httpResponse->GetResponseCode());
182 error.SetRemoteHostIpAddress(httpResponse->GetOriginatingRequest().GetResolvedRemoteHost());
183
184 AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_CHECKSUM, error);
185 return {error};
186 }
187 // Validate only a single checksum returned in an HTTP response
188 AWS_LOGSTREAM_DEBUG(AWS_SMITHY_CLIENT_CHECKSUM, "Successfully calculated a checksum for "
189 << httpRequest->GetURIString()
190 << " using checksum algorithm: " << hashIterator.first);
191 break;
192 }
193 }
194 return httpResponse;
195 }
196
197 private:
198 void addChecksumFeatureForChecksumName(const Aws::String& checksumName, const Aws::AmazonWebServiceRequest& request) {
199 if (checksumName == "crc32") {
201 } else if (checksumName == "crc32c") {
203 } else if (checksumName == "crc64") {
205 } else if (checksumName == "sha1") {
207 } else if (checksumName == "sha256") {
209 } else {
210 AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_CHECKSUM, "could not add useragent feature for checksum " << checksumName);
211 }
212 }
213
214 void addChecksumConfigurationFeatures(const Aws::AmazonWebServiceRequest& request) {
215 switch (m_requestChecksumCalculation) {
216 case RequestChecksumCalculation::WHEN_SUPPORTED: request.AddUserAgentFeature(Aws::Client::UserAgentFeature::FLEXIBLE_CHECKSUMS_REQ_WHEN_SUPPORTED); break;
217 case RequestChecksumCalculation::WHEN_REQUIRED: request.AddUserAgentFeature(Aws::Client::UserAgentFeature::FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED);break;
218 default: AWS_LOG_ERROR(AWS_SMITHY_CLIENT_CHECKSUM, "could not add useragent feature for checksum request configuration"); break;
219 }
220
221 switch (m_responseChecksumValidation) {
222 case ResponseChecksumValidation::WHEN_SUPPORTED: request.AddUserAgentFeature(Aws::Client::UserAgentFeature::FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED); break;
223 case ResponseChecksumValidation::WHEN_REQUIRED: request.AddUserAgentFeature(Aws::Client::UserAgentFeature::FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED); break;
224 default: AWS_LOG_ERROR(AWS_SMITHY_CLIENT_CHECKSUM, "could not add useragent feature for checksum response configuration"); break;
225 }
226 }
227
228 RequestChecksumCalculation m_requestChecksumCalculation{RequestChecksumCalculation::WHEN_SUPPORTED};
229 ResponseChecksumValidation m_responseChecksumValidation{ResponseChecksumValidation::WHEN_SUPPORTED};
230};
231} // namespace client
232} // namespace smithy
virtual std::shared_ptr< Aws::IOStream > GetBody() const =0
void AddUserAgentFeature(Aws::Client::UserAgentFeature feature) const
void SetResponseHeaders(const Aws::Http::HeaderValueCollection &headers)
Definition AWSError.h:195
static ByteBuffer CalculateCRC32(const Aws::String &str)
static Aws::String Base64Encode(const ByteBuffer &byteBuffer)
static ByteBuffer CalculateCRC64(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
Aws::Client::RequestChecksumCalculation RequestChecksumCalculation
~ChecksumInterceptor() override=default
static std::shared_ptr< Aws::IOStream > GetBodyStream(const Aws::AmazonWebServiceRequest &request)
Aws::Client::ResponseChecksumValidation ResponseChecksumValidation
ChecksumInterceptor(ChecksumInterceptor &&other) noexcept=default
ChecksumInterceptor(const ClientConfiguration &configuration)
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:56
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[]