Skip to content

Commit

Permalink
move checksumming operations to a interceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
sbiscigl committed Oct 11, 2024
1 parent 9c328a6 commit ac9296d
Show file tree
Hide file tree
Showing 12 changed files with 381 additions and 276 deletions.
4 changes: 4 additions & 0 deletions src/aws-cpp-sdk-core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ file(GLOB SMITHY_IDENTITY_RESOLVER_IMPL_HEADERS "include/smithy/identity/resolve
file(GLOB SMITHY_IDENTITY_SIGNER_HEADERS "include/smithy/identity/signer/*.h")
file(GLOB SMITHY_IDENTITY_SIGNER_BUILTIN_HEADERS "include/smithy/identity/signer/built-in/*.h")
file(GLOB SMITHY_INTERCEPTOR_HEADERS "include/smithy/interceptor/*.h")
file(GLOB SMITHY_INTERCEPTOR_IMPL_HEADERS "include/smithy/interceptor/impl/*.h")

file(GLOB AWS_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
file(GLOB AWS_TINYXML2_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/external/tinyxml2/*.cpp")
Expand Down Expand Up @@ -363,6 +364,7 @@ file(GLOB AWS_NATIVE_SDK_COMMON_HEADERS
${SMITHY_IDENTITY_RESOLVER_BUILTIN_HEADERS}
${OPTEL_HEADERS}
${SMITHY_INTERCEPTOR_HEADERS}
${SMITHY_INTERCEPTOR_IMPL_HEADERS}
)

# misc platform-specific, not related to features (encryption/http clients)
Expand Down Expand Up @@ -495,6 +497,7 @@ if(MSVC)
source_group("Header Files\\smithy\\identity\\signer" FILES ${SMITHY_IDENTITY_SIGNER_HEADERS})
source_group("Header Files\\smithy\\identity\\signer\\built-in" FILES ${SMITHY_IDENTITY_SIGNER_BUILTIN_HEADERS})
source_group("Header Files\\smithy\\interceptor" FILES ${SMITHY_INTERCEPTOR_HEADERS})
source_group("Header Files\\smithy\\interceptor" FILES ${SMITHY_INTERCEPTOR_IMPL_HEADERS})

# http client conditional headers
if(ENABLE_CURL_CLIENT)
Expand Down Expand Up @@ -774,6 +777,7 @@ install (FILES ${SMITHY_IDENTITY_RESOLVER_IMPL_HEADERS} DESTINATION ${INCLUDE_DI
install (FILES ${SMITHY_IDENTITY_SIGNER_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/signer)
install (FILES ${SMITHY_IDENTITY_SIGNER_BUILTIN_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/signer/built-in)
install (FILES ${SMITHY_INTERCEPTOR_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/interceptor)
install (FILES ${SMITHY_INTERCEPTOR_IMPL_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/interceptor)

# android logcat headers
if(PLATFORM_ANDROID)
Expand Down
8 changes: 3 additions & 5 deletions src/aws-cpp-sdk-core/include/aws/core/client/AWSClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <aws/core/utils/crypto/Hash.h>
#include <aws/core/auth/AWSAuthSignerProvider.h>
#include <aws/core/endpoint/AWSEndpoint.h>
#include <smithy/interceptor/Interceptor.h>
#include <memory>
#include <atomic>

Expand Down Expand Up @@ -338,11 +339,10 @@ namespace Aws
*/
bool AdjustClockSkew(HttpResponseOutcome& outcome, const char* signerName) const;
void AddHeadersToRequest(const std::shared_ptr<Aws::Http::HttpRequest>& httpRequest, const Http::HeaderValueCollection& headerValues) const;
void AddChecksumToRequest(const std::shared_ptr<Aws::Http::HttpRequest>& HttpRequest, const Aws::AmazonWebServiceRequest& request) const;
void AddContentBodyToRequest(const std::shared_ptr<Aws::Http::HttpRequest>& httpRequest, const std::shared_ptr<Aws::IOStream>& body,
bool needsContentMd5 = false, bool isChunked = false) const;
void AddCommonHeaders(Aws::Http::HttpRequest& httpRequest) const;
std::shared_ptr<Aws::IOStream> GetBodyStream(const Aws::AmazonWebServiceRequest& request) const;
void AppendHeaderValueToRequest(const std::shared_ptr<Http::HttpRequest> &request, String header, String value) const;

std::shared_ptr<Aws::Http::HttpClient> m_httpClient;
std::shared_ptr<AWSErrorMarshaller> m_errorMarshaller;
Expand All @@ -355,9 +355,7 @@ namespace Aws
bool m_enableClockSkewAdjustment;
Aws::String m_serviceName = "AWSBaseClient";
Aws::Client::RequestCompressionConfig m_requestCompressionConfig;
void AppendHeaderValueToRequest(
const std::shared_ptr<Http::HttpRequest> &request, String header,
String value) const;
Aws::Vector<std::shared_ptr<smithy::interceptor::Interceptor>> m_interceptors;
};

AWS_CORE_API Aws::String GetAuthorizationHeader(const Aws::Http::HttpRequest& httpRequest);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include <smithy/Smithy_EXPORTS.h>
#include <smithy/interceptor/InterceptorContext.h>

#include <aws/core/endpoint/AWSEndpoint.h>
#include <aws/core/http/HttpRequest.h>
Expand Down Expand Up @@ -70,6 +71,7 @@ namespace smithy

ResponseHandlerFunc m_responseHandler;
std::shared_ptr<Aws::Utils::Threading::Executor> m_pExecutor;
std::shared_ptr<interceptor::InterceptorContext> m_interceptorContext;
};
} // namespace client
} // namespace smithy
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <smithy/identity/auth/AuthSchemeOption.h>
#include <smithy/identity/identity/AwsIdentity.h>
#include <smithy/tracing/TelemetryProvider.h>
#include <smithy/interceptor/Interceptor.h>
#include <smithy/client/features/ChecksumInterceptor.h>

#include <aws/crt/Variant.h>
#include <aws/core/client/ClientConfiguration.h>
Expand Down Expand Up @@ -85,7 +87,8 @@ namespace client
m_serviceName(std::move(serviceName)),
m_userAgent(),
m_httpClient(std::move(httpClient)),
m_errorMarshaller(std::move(errorMarshaller))
m_errorMarshaller(std::move(errorMarshaller)),
m_interceptors{Aws::MakeShared<ChecksumInterceptor>("AwsSmithyClientBase")}
{
if (!m_clientConfig->retryStrategy)
{
Expand Down Expand Up @@ -163,6 +166,7 @@ namespace client

std::shared_ptr<Aws::Http::HttpClient> m_httpClient;
std::shared_ptr<Aws::Client::AWSErrorMarshaller> m_errorMarshaller;
Aws::Vector<std::shared_ptr<smithy::interceptor::Interceptor>> m_interceptors{};
};
} // namespace client
} // namespace smithy
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#pragma once

#include <smithy/interceptor/Interceptor.h>

#include <aws/core/AmazonWebServiceRequest.h>
#include <aws/core/http/HttpRequest.h>
#include <aws/core/http/HttpResponse.h>
Expand All @@ -26,7 +28,7 @@ namespace smithy

static const char CHECKSUM_CONTENT_MD5_HEADER[] = "content-md5";

class Checksums
class ChecksumInterceptor: public smithy::interceptor::Interceptor
{
public:
using HeaderValueCollection = Aws::Http::HeaderValueCollection;
Expand All @@ -38,6 +40,12 @@ namespace smithy
using Sha1 = Aws::Utils::Crypto::Sha1;
using PrecalculatedHash = Aws::Utils::Crypto::PrecalculatedHash;

~ChecksumInterceptor() override = default;
ChecksumInterceptor() = default;
ChecksumInterceptor(const ChecksumInterceptor& other) = delete;
ChecksumInterceptor(ChecksumInterceptor&& other) noexcept = default;
ChecksumInterceptor& operator=(const ChecksumInterceptor& other) = delete;
ChecksumInterceptor& operator=(ChecksumInterceptor&& other) noexcept = default;

static std::shared_ptr<Aws::IOStream> GetBodyStream(const Aws::AmazonWebServiceRequest& request)
{
Expand All @@ -49,9 +57,17 @@ namespace smithy
return Aws::MakeShared<Aws::StringStream>(AWS_SMITHY_CLIENT_CHECKSUM, "");
}

static void AddChecksumToRequest(const std::shared_ptr<Aws::Http::HttpRequest>& httpRequest,
const Aws::AmazonWebServiceRequest& request)
ModifyRequestOutcome ModifyBeforeSigning(interceptor::InterceptorContext& context) override
{
const auto& httpRequest = context.GetTransmitRequest();
const auto& request = context.GetModeledRequest();
if (httpRequest == nullptr)
{
return Aws::Client::AWSError<Aws::Client::CoreErrors>{Aws::Client::CoreErrors::VALIDATION,
"ValidationErrorException",
"Checksum request validation missing request",
false};
}
Aws::String checksumAlgorithmName = Aws::Utils::StringUtils::ToLower(
request.GetChecksumAlgorithmName().c_str());
if (request.GetServiceSpecificParameters())
Expand Down Expand Up @@ -176,7 +192,7 @@ namespace smithy
{
std::shared_ptr<CRC32> crc32 = Aws::MakeShared<
CRC32>(AWS_SMITHY_CLIENT_CHECKSUM);
httpRequest->AddResponseValidationHash("crc", crc32);
httpRequest->AddResponseValidationHash("crc32", crc32);
}
else if (checksumAlgorithmName == "sha1")
{
Expand All @@ -198,17 +214,21 @@ namespace smithy
}
}
}
return httpRequest;
}

using OptionalError = Aws::Crt::Optional<Aws::Client::AWSError<Aws::Client::CoreErrors>>;

static OptionalError ValidateResponseChecksum(AwsSmithyClientAsyncRequestContext const* const pRequestCtx,
Aws::Http::HttpResponse const* const httpResponse)
ModifyResponseOutcome ModifyBeforeDeserialization(interceptor::InterceptorContext& context) override
{
assert(pRequestCtx);
assert(httpResponse);
assert(pRequestCtx->m_httpRequest);
for (const auto& hashIterator : pRequestCtx->m_httpRequest->GetResponseValidationHashes())
const auto httpRequest = context.GetTransmitRequest();
const auto httpResponse = context.GetTransmitResponse();
if (httpRequest == nullptr || httpResponse == nullptr)
{
return Aws::Client::AWSError<Aws::Client::CoreErrors>{Aws::Client::CoreErrors::VALIDATION,
"ValidationErrorException",
"Checksum response validation missing request or response",
false};
}
for (const auto& hashIterator : httpRequest->GetResponseValidationHashes())
{
Aws::String checksumHeaderKey = Aws::String("x-amz-checksum-") + hashIterator.first;
// TODO: If checksum ends with -#, then skip
Expand All @@ -218,24 +238,23 @@ namespace smithy
if (HashingUtils::Base64Encode(hashIterator.second->GetHash().GetResult()) !=
checksumHeaderValue)
{
auto error = OptionalError(
Aws::Client::AWSError<Aws::Client::CoreErrors>(
auto error = Aws::Client::AWSError<Aws::Client::CoreErrors>{
Aws::Client::CoreErrors::VALIDATION, "",
"Response checksums mismatch",
false/*retryable*/));
error->SetResponseHeaders(httpResponse->GetHeaders());
error->SetResponseCode(httpResponse->GetResponseCode());
error->SetRemoteHostIpAddress(
httpResponse->GetOriginatingRequest().GetResolvedRemoteHost());

AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_CHECKSUM, *error);
return error;
false/*retryable*/};
error.SetResponseHeaders(httpResponse->GetHeaders());
error.SetResponseCode(httpResponse->GetResponseCode());
error.SetRemoteHostIpAddress(
httpResponse->GetOriginatingRequest().GetResolvedRemoteHost());

AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_CHECKSUM, error);
return {error};
}
// Validate only a single checksum returned in an HTTP response
break;
}
}
return {};
return httpResponse;
}
};
}
Expand Down
4 changes: 2 additions & 2 deletions src/aws-cpp-sdk-core/include/smithy/interceptor/Interceptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ namespace smithy
virtual ~Interceptor() = default;

using ModifyRequestOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpRequest>, Aws::Client::AWSError<Aws::Client::CoreErrors>>;
virtual ModifyRequestOutcome ModifyRequest(InterceptorContext& context) = 0;
virtual ModifyRequestOutcome ModifyBeforeSigning(InterceptorContext& context) = 0;

using ModifyResponseOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpResponse>, Aws::Client::AWSError<Aws::Client::CoreErrors>>;
virtual ModifyResponseOutcome ModifyResponse(InterceptorContext& context) = 0;
virtual ModifyResponseOutcome ModifyBeforeDeserialization(InterceptorContext& context) = 0;
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
*/
#pragma once
#include <aws/core/utils/memory/stl/AWSString.h>
#include <aws/core/utils/Outcome.h>
#include <aws/core/AmazonWebServiceRequest.h>
#include <aws/core/AmazonWebServiceResult.h>
#include <aws/core/http/HttpRequest.h>
#include <aws/core/http/HttpResponse.h>
#include <aws/core/client/AWSError.h>
#include <aws/core/client/CoreErrors.h>

namespace smithy
Expand All @@ -17,78 +17,57 @@ namespace smithy
class InterceptorContext
{
public:
InterceptorContext() = default;
explicit InterceptorContext(const Aws::AmazonWebServiceRequest& m_modeled_request)
: m_modeledRequest(m_modeled_request)
{
}

virtual ~InterceptorContext() = default;
InterceptorContext(const InterceptorContext& other) = delete;
InterceptorContext(InterceptorContext&& other) noexcept = default;
InterceptorContext(InterceptorContext&& other) noexcept = delete;
InterceptorContext& operator=(const InterceptorContext& other) = delete;
InterceptorContext& operator=(InterceptorContext&& other) noexcept = default;
InterceptorContext& operator=(InterceptorContext&& other) noexcept = delete;

const Aws::AmazonWebServiceRequest& GetModeledRequest() const
{
return m_modeledRequest;
}

using GetRequestOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpRequest>, Aws::Client::AWSError<Aws::Client::CoreErrors>>;
GetRequestOutcome GetRequest() const
std::shared_ptr<Aws::Http::HttpRequest> GetTransmitRequest() const
{
if (!m_request)
{
return Aws::Client::AWSError<Aws::Client::CoreErrors>{
Aws::Client::CoreErrors::RESOURCE_NOT_FOUND,
"ResourceNotFoundException",
"Request is NULL",
false
};
}
return m_request;
return m_transmitRequest;
}

void SetRequest(const std::shared_ptr<Aws::Http::HttpRequest>& request)
void SetTransmitRequest(const std::shared_ptr<Aws::Http::HttpRequest>& transmitRequest)
{
this->m_request = request;
m_transmitRequest = transmitRequest;
}

using GetResponseOutcome = Aws::Utils::Outcome<std::shared_ptr<Aws::Http::HttpResponse>, Aws::Client::AWSError<Aws::Client::CoreErrors>>;
GetResponseOutcome GetResponse() const
std::shared_ptr<Aws::Http::HttpResponse> GetTransmitResponse() const
{
if (!m_response)
{
return Aws::Client::AWSError<Aws::Client::CoreErrors>{
Aws::Client::CoreErrors::RESOURCE_NOT_FOUND,
"ResourceNotFoundException",
"Response is NULL",
false
};
}
return m_response;
return m_transmitResponse;
}

void SetResponse(const std::shared_ptr<Aws::Http::HttpResponse>& response)
void SetTransmitResponse(const std::shared_ptr<Aws::Http::HttpResponse>& transmitResponse)
{
this->m_response = response;
m_transmitResponse = transmitResponse;
}

using GetAttributeOutcome = Aws::Utils::Outcome<Aws::String, Aws::Client::AWSError<Aws::Client::CoreErrors>>;
GetAttributeOutcome GetAttribute(const Aws::String& attribute)
Aws::String GetAttribute(const Aws::String& key) const
{
const auto attribute_iter = m_attributes.find(attribute);
if (attribute_iter == m_attributes.end())
{
return Aws::Client::AWSError<Aws::Client::CoreErrors>{
Aws::Client::CoreErrors::RESOURCE_NOT_FOUND,
"ResourceNotFoundException",
"Attribute not found",
false
};
}
return attribute_iter->second;
return m_attributes.at(key);
}

void SetAttribute(const Aws::String& attribute, const Aws::String& value)
void SetAttribute(const Aws::String& key, const Aws::String& value)
{
m_attributes.emplace(attribute, value);
m_attributes.insert({key, value});
}

private:
Aws::Map<Aws::String, Aws::String> m_attributes{};
std::shared_ptr<Aws::Http::HttpRequest> m_request{nullptr};
std::shared_ptr<Aws::Http::HttpResponse> m_response{nullptr};
const Aws::AmazonWebServiceRequest& m_modeledRequest;
std::shared_ptr<Aws::Http::HttpRequest> m_transmitRequest{nullptr};
std::shared_ptr<Aws::Http::HttpResponse> m_transmitResponse{nullptr};
};
}
}
Loading

0 comments on commit ac9296d

Please sign in to comment.