diff --git a/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3ExpressIdentity.h b/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3ExpressIdentity.h index 78bcf03e205..8fe41970d71 100644 --- a/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3ExpressIdentity.h +++ b/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3ExpressIdentity.h @@ -40,22 +40,22 @@ namespace Aws { return m_expiration; } - String accessKeyId() override + String accessKeyId() const override { return getAccessKeyId(); } - String secretAccessKey() override + String secretAccessKey() const override { return getSecretKeyId(); } - Crt::Optional sessionToken() override + Crt::Optional sessionToken() const override { return getSessionToken(); } - Crt::Optional expiration() override + Crt::Optional expiration() const override { return getExpiration(); } diff --git a/generated/src/aws-cpp-sdk-s3-crt/source/S3ExpressIdentityProvider.cpp b/generated/src/aws-cpp-sdk-s3-crt/source/S3ExpressIdentityProvider.cpp index 2a2adfc842e..1134d546e0a 100644 --- a/generated/src/aws-cpp-sdk-s3-crt/source/S3ExpressIdentityProvider.cpp +++ b/generated/src/aws-cpp-sdk-s3-crt/source/S3ExpressIdentityProvider.cpp @@ -43,8 +43,8 @@ S3ExpressIdentityProvider::getIdentity( return {}; }); } - const auto identity = GetS3ExpressIdentity(params); - return identity; + auto identity = GetS3ExpressIdentity(params); + return Aws::MakeUnique(S3_EXPRESS_IDENTITY_PROVIDER, std::move(identity)); } S3ExpressIdentity S3ExpressIdentityProvider::getIdentity(const Aws::String &bucketName) { diff --git a/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3ExpressIdentity.h b/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3ExpressIdentity.h index aa31e8c16c4..d52a1cf0a3c 100644 --- a/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3ExpressIdentity.h +++ b/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3ExpressIdentity.h @@ -40,22 +40,22 @@ namespace Aws { return m_expiration; } - String accessKeyId() override + String accessKeyId() const override { return getAccessKeyId(); } - String secretAccessKey() override + String secretAccessKey() const override { return getSecretKeyId(); } - Crt::Optional sessionToken() override + Crt::Optional sessionToken() const override { return getSessionToken(); } - Crt::Optional expiration() override + Crt::Optional expiration() const override { return getExpiration(); } diff --git a/generated/src/aws-cpp-sdk-s3/source/S3ExpressIdentityProvider.cpp b/generated/src/aws-cpp-sdk-s3/source/S3ExpressIdentityProvider.cpp index 77025038245..cc32f9010e2 100644 --- a/generated/src/aws-cpp-sdk-s3/source/S3ExpressIdentityProvider.cpp +++ b/generated/src/aws-cpp-sdk-s3/source/S3ExpressIdentityProvider.cpp @@ -43,8 +43,8 @@ S3ExpressIdentityProvider::getIdentity( return {}; }); } - const auto identity = GetS3ExpressIdentity(params); - return identity; + auto identity = GetS3ExpressIdentity(params); + return Aws::MakeUnique(S3_EXPRESS_IDENTITY_PROVIDER, std::move(identity)); } S3ExpressIdentity S3ExpressIdentityProvider::getIdentity(const Aws::String &bucketName) { diff --git a/src/aws-cpp-sdk-core/CMakeLists.txt b/src/aws-cpp-sdk-core/CMakeLists.txt index c23343f467d..9fcf6b62487 100644 --- a/src/aws-cpp-sdk-core/CMakeLists.txt +++ b/src/aws-cpp-sdk-core/CMakeLists.txt @@ -73,12 +73,22 @@ file(GLOB UTILS_STREAM_HEADERS "include/aws/core/utils/stream/*.h") file(GLOB CJSON_HEADERS "include/aws/core/external/cjson/*.h") file(GLOB TINYXML2_HEADERS "include/aws/core/external/tinyxml2/tinyxml2.h") file(GLOB SMITHY_HEADERS "include/smithy/*.h") -file(GLOB SMITHY_CLIENT_HEADERS "include/smithy/client/*.h" "include/smithy/client/impl/*.h") +file(GLOB SMITHY_CLIENT_HEADERS "include/smithy/client/*.h") +file(GLOB SMITHY_CLIENT_IMPL_HEADERS "include/smithy/client/impl/*.h") +file(GLOB SMITHY_CLIENT_COMMON_HEADERS "include/smithy/client/common/*.h") +file(GLOB SMITHY_CLIENT_FEATURES_HEADERS "include/smithy/client/features/*.h") file(GLOB SMITHY_TRACING_HEADERS "include/smithy/tracing/*.h") file(GLOB SMITHY_IDENTITY_HEADERS "include/smithy/identity/*.h") -file(GLOB SMITHY_IDENTITY_AUTH_HEADERS "include/smithy/identity/auth/*.h" "include/smithy/identity/auth/impl/*.h") -file(GLOB SMITHY_IDENTITY_IDENTITY_HEADERS "include/smithy/identity/identity/*.h" "include/smithy/identity/identity/impl/*.h") -file(GLOB SMITHY_IDENTITY_RESOLVER_HEADERS "include/smithy/identity/resolver/*.h" "include/smithy/identity/resolver/impl/*.h") +file(GLOB SMITHY_IDENTITY_AUTH_HEADERS "include/smithy/identity/auth/*.h") +file(GLOB SMITHY_IDENTITY_AUTH_IMPL_HEADERS "include/smithy/identity/auth/impl/*.h") +file(GLOB SMITHY_IDENTITY_AUTH_BUILTIN_HEADERS "include/smithy/identity/auth/built-in/*.h") +file(GLOB SMITHY_IDENTITY_IDENTITY_HEADERS "include/smithy/identity/identity/*.h") +file(GLOB SMITHY_IDENTITY_RESOLVER_HEADERS "include/smithy/identity/resolver/*.h") +file(GLOB SMITHY_IDENTITY_RESOLVER_BUILTIN_HEADERS "include/smithy/identity/resolver/built-in/*.h") +file(GLOB SMITHY_IDENTITY_IDENTITY_IMPL_HEADERS "include/smithy/identity/identity/impl/*.h") +file(GLOB SMITHY_IDENTITY_RESOLVER_IMPL_HEADERS "include/smithy/identity/resolver/impl/*.h") +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 AWS_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") file(GLOB AWS_TINYXML2_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/source/external/tinyxml2/*.cpp") @@ -333,11 +343,21 @@ file(GLOB AWS_NATIVE_SDK_COMMON_HEADERS ${HTTP_WINDOWS_CLIENT_HEADERS} ${SMITHY_HEADERS} ${SMITHY_CLIENT_HEADERS} + ${SMITHY_CLIENT_IMPL_HEADERS} + ${SMITHY_CLIENT_COMMON_HEADERS} + ${SMITHY_CLIENT_FEATURES_HEADERS} ${SMITHY_TRACING_HEADERS} ${SMITHY_IDENTITY_HEADERS} ${SMITHY_IDENTITY_AUTH_HEADERS} + ${SMITHY_IDENTITY_AUTH_IMPL_HEADERS} + ${SMITHY_IDENTITY_AUTH_BUILTIN_HEADERS} ${SMITHY_IDENTITY_IDENTITY_HEADERS} + ${SMITHY_IDENTITY_SIGNER_HEADERS} + ${SMITHY_IDENTITY_IDENTITY_IMPL_HEADERS} + ${SMITHY_IDENTITY_SIGNER_IMPL_HEADERS} + ${SMITHY_IDENTITY_SIGNER_BUILTIN_HEADERS} ${SMITHY_IDENTITY_RESOLVER_HEADERS} + ${SMITHY_IDENTITY_RESOLVER_BUILTIN_HEADERS} ${OPTEL_HEADERS} ) @@ -455,10 +475,20 @@ if(MSVC) source_group("Header Files\\aws\\core\\external\\tinyxml2" FILES ${TINYXML2_HEADERS}) source_group("Header Files\\smithy" FILES ${SMITHY_HEADERS}) source_group("Header Files\\smithy\\client" FILES ${SMITHY_CLIENT_HEADERS}) + source_group("Header Files\\smithy\\client" FILES ${SMITHY_CLIENT_IMPL_HEADERS}) + source_group("Header Files\\smithy\\client" FILES ${SMITHY_CLIENT_COMMON_HEADERS}) + source_group("Header Files\\smithy\\client" FILES ${SMITHY_CLIENT_FEATURES_HEADERS}) source_group("Header Files\\smithy\\tracing" FILES ${SMITHY_TRACING_HEADERS}) source_group("Header Files\\smithy\\identity\\auth" FILES ${SMITHY_IDENTITY_AUTH_HEADERS}) + source_group("Header Files\\smithy\\identity\\auth\\impl" FILES ${SMITHY_IDENTITY_AUTH_IMPL_HEADERS}) + source_group("Header Files\\smithy\\identity\\auth\\built-in" FILES ${SMITHY_IDENTITY_AUTH_BUILTIN_HEADERS}) source_group("Header Files\\smithy\\identity\\identity" FILES ${SMITHY_IDENTITY_IDENTITY_HEADERS}) source_group("Header Files\\smithy\\identity\\resolver" FILES ${SMITHY_IDENTITY_RESOLVER_HEADERS}) + source_group("Header Files\\smithy\\identity\\resolver\\built-in" FILES ${SMITHY_IDENTITY_RESOLVER_BUILTIN_HEADERS}) + source_group("Header Files\\smithy\\identity\\identity\\impl" FILES ${SMITHY_IDENTITY_IDENTITY_IMPL_HEADERS}) + source_group("Header Files\\smithy\\identity\\resolver\\impl" FILES ${SMITHY_IDENTITY_RESOLVER_IMPL_HEADERS}) + 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}) # http client conditional headers if(ENABLE_CURL_CLIENT) @@ -724,8 +754,14 @@ install (FILES ${SMITHY_CLIENT_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/ install (FILES ${SMITHY_TRACING_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/tracing) install (FILES ${SMITHY_IDENTITY_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity) install (FILES ${SMITHY_IDENTITY_AUTH_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/auth) +install (FILES ${SMITHY_IDENTITY_AUTH_IMPL_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/auth/impl) +install (FILES ${SMITHY_IDENTITY_AUTH_BUILTIN_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/auth/built-in) install (FILES ${SMITHY_IDENTITY_IDENTITY_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/identity) install (FILES ${SMITHY_IDENTITY_RESOLVER_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/resolver) +install (FILES ${SMITHY_IDENTITY_IDENTITY_IMPL_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/identity/impl) +install (FILES ${SMITHY_IDENTITY_RESOLVER_IMPL_HEADERS} DESTINATION ${INCLUDE_DIRECTORY}/smithy/identity/resolver/impl) +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) # android logcat headers if(PLATFORM_ANDROID) diff --git a/src/aws-cpp-sdk-core/include/aws/core/auth/signer/AWSAuthV4Signer.h b/src/aws-cpp-sdk-core/include/aws/core/auth/signer/AWSAuthV4Signer.h index 6c1490b452b..cc0cce9487e 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/auth/signer/AWSAuthV4Signer.h +++ b/src/aws-cpp-sdk-core/include/aws/core/auth/signer/AWSAuthV4Signer.h @@ -20,6 +20,11 @@ #include +namespace smithy +{ + class AwsSigV4Signer; +} + namespace Aws { namespace Http @@ -178,7 +183,6 @@ namespace Aws bool m_includeSha256HashHeader; private: - Aws::String GenerateSignature(const Aws::Auth::AWSCredentials& credentials, const Aws::String& stringToSign, const Aws::String& simpleDate, const Aws::String& region, const Aws::String& serviceName) const; @@ -193,6 +197,14 @@ namespace Aws bool SignRequestWithSigV4a(Aws::Http::HttpRequest& request, const char* region, const char* serviceName, bool signBody, long long expirationTimeInSeconds, Aws::Crt::Auth::SignatureType signatureType) const; + friend class smithy::AwsSigV4Signer; + /** + * Temporary method added for migration to the smithy architecture. Please do not use. + */ + bool SignRequestWithCreds(Aws::Http::HttpRequest& request, const Auth::AWSCredentials& credentials, + const char* region, const char* serviceName, bool signBody) const; + + Aws::Auth::AWSSigningAlgorithm m_signingAlgorithm; std::shared_ptr m_credentialsProvider; const Aws::String m_serviceName; diff --git a/src/aws-cpp-sdk-core/include/aws/core/client/AWSError.h b/src/aws-cpp-sdk-core/include/aws/core/client/AWSError.h index 47f803128a6..52ebe3ca278 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/client/AWSError.h +++ b/src/aws-cpp-sdk-core/include/aws/core/client/AWSError.h @@ -208,6 +208,10 @@ namespace Aws * Return whether or not the error should throttle retry strategies. */ inline bool ShouldThrottle() const { return m_retryableType == RetryableType::RETRYABLE_THROTTLING; } + /** + * Sets the response code from the http response + */ + inline void SetRetryableType(const RetryableType retryableType) { m_retryableType = retryableType; } protected: inline ErrorPayloadType GetErrorPayloadType() { return m_errorPayloadType; } diff --git a/src/aws-cpp-sdk-core/include/aws/core/client/AWSErrorMarshaller.h b/src/aws-cpp-sdk-core/include/aws/core/client/AWSErrorMarshaller.h index 628b9565fdc..d53c5748516 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/client/AWSErrorMarshaller.h +++ b/src/aws-cpp-sdk-core/include/aws/core/client/AWSErrorMarshaller.h @@ -6,6 +6,8 @@ #pragma once #include +#include +#include #include namespace Aws @@ -53,6 +55,15 @@ namespace Aws */ virtual AWSError FindErrorByName(const char* exceptionName) const; virtual AWSError FindErrorByHttpResponseCode(Aws::Http::HttpResponseCode code) const; + + /** + * Deserialize http error payload to an error object. + */ + virtual AWSError BuildAWSError(const std::shared_ptr&) const + { + return AWSError(CoreErrors::UNKNOWN, false); + } + /** * Attempts to extract region from error. */ @@ -75,6 +86,8 @@ namespace Aws */ AWSError Marshall(const Aws::Http::HttpResponse& response) const override; + AWSError BuildAWSError(const std::shared_ptr& httpResponse) const override; + protected: const Aws::Utils::Json::JsonValue& GetJsonPayloadFromError(const AWSError&) const; }; @@ -89,6 +102,8 @@ namespace Aws */ AWSError Marshall(const Aws::Http::HttpResponse& response) const override; + AWSError BuildAWSError(const std::shared_ptr& httpResponse) const override; + protected: const Aws::Utils::Xml::XmlDocument& GetXmlPayloadFromError(const AWSError&) const; }; diff --git a/src/aws-cpp-sdk-core/include/aws/core/client/CoreErrors.h b/src/aws-cpp-sdk-core/include/aws/core/client/CoreErrors.h index 8e5e6b9de77..6507bbc5d56 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/client/CoreErrors.h +++ b/src/aws-cpp-sdk-core/include/aws/core/client/CoreErrors.h @@ -46,6 +46,7 @@ namespace Aws INVALID_ACCESS_KEY_ID = 23, REQUEST_TIMEOUT = 24, NOT_INITIALIZED = 25, + MEMORY_ALLOCATION = 26, NETWORK_CONNECTION = 99, // General failure to send message to service diff --git a/src/aws-cpp-sdk-core/include/aws/core/utils/threading/SameThreadExecutor.h b/src/aws-cpp-sdk-core/include/aws/core/utils/threading/SameThreadExecutor.h new file mode 100644 index 00000000000..b5b57e5d55c --- /dev/null +++ b/src/aws-cpp-sdk-core/include/aws/core/utils/threading/SameThreadExecutor.h @@ -0,0 +1,36 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once + +#include + +#include +#include + +namespace Aws +{ + namespace Utils + { + namespace Threading + { + /** + * An executor that does not spawn any thread, instead, tasks are executed in the current thread + * TODO: add await functionality to avoid deadlocking if thread waits for another async task. + */ + class AWS_CORE_API SameThreadExecutor : public Executor + { + public: + virtual ~SameThreadExecutor(); + void WaitUntilStopped() override; + protected: + bool SubmitToThread(std::function&& task) override; + + using TaskFunc = std::function; + Aws::List m_tasks; + }; + } // namespace Threading + } // namespace Utils +} // namespace Aws diff --git a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h new file mode 100644 index 00000000000..20d640189d0 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h @@ -0,0 +1,128 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace smithy { +namespace client +{ + template + class AwsSmithyClientT : public AwsSmithyClientBase + { + public: + AwsSmithyClientT(Aws::Client::ClientConfiguration& clientConfig, const Aws::String& serviceName, + const std::shared_ptr& httpClient, + const std::shared_ptr& errorMarshaller, + const ServiceClientConfigurationT& m_client_config, + const std::shared_ptr endpointProvider, + const std::shared_ptr& m_auth_scheme_resolver, + const Aws::UnorderedMap& m_auth_schemes) + : AwsSmithyClientBase(clientConfig, serviceName, httpClient, errorMarshaller), + m_clientConfig(m_client_config), + m_endpointProvider(endpointProvider), + m_authSchemeResolver(m_auth_scheme_resolver), + m_authSchemes(m_auth_schemes) + { + if (ServiceNameT) { + m_serviceName = ServiceNameT; + } + } + + virtual ~AwsSmithyClientT() = default; + + protected: + inline const char* GetServiceClientName() const override { return m_serviceName.c_str(); } + + ResolveEndpointOutcome ResolveEndpoint(const Aws::Endpoint::EndpointParameters& endpointParameters, EndpointUpdateCallback&& epCallback) const override + { + assert(m_endpointProvider); + ResolveEndpointOutcome resolveEndpointOutcome = m_endpointProvider->ResolveEndpoint(endpointParameters); + if (resolveEndpointOutcome.IsSuccess()) + { + epCallback(resolveEndpointOutcome.GetResult()); + } + return resolveEndpointOutcome; + } + + SelectAuthSchemeOptionOutcome SelectAuthSchemeOption(const AwsSmithyClientAsyncRequestContext& ctx) const override + { + assert(m_authSchemeResolver); + typename ServiceAuthSchemeResolverT::ServiceAuthSchemeParameters identityParams; + + if (ctx.m_pRequest) { + // refactor once auth scheme resolver will use it's own rule set + const auto& epParams = ctx.m_pRequest->GetEndpointContextParams(); + for (const auto& epParam : epParams) { + using ParameterType = Aws::Endpoint::EndpointParameter::ParameterType; + if(epParam.GetStoredType() == ParameterType::STRING) + identityParams.insert({epParam.GetName(), epParam.GetStrValueNoCheck()}); + else if (epParam.GetStoredType() == ParameterType::BOOLEAN) + identityParams.insert({epParam.GetName(), epParam.GetBoolValueNoCheck()}); + else + assert(!"Unknown endpoint parameter!"); + } + const auto& serviceParams = ctx.m_pRequest->GetServiceSpecificParameters(); + if (serviceParams) { + for (const auto& serviceParam : serviceParams->parameterMap) { + identityParams.insert({serviceParam.first, serviceParam.second}); + } + } + } + Aws::Vector authSchemeOptions = m_authSchemeResolver->resolveAuthScheme(identityParams); + + auto authSchemeOptionIt = std::find_first_of(authSchemeOptions.begin(), authSchemeOptions.end(), + [this](const AuthSchemeOption& opt) + { + return m_authSchemes.find(opt.schemeId) != m_authSchemes.end(); + }); + assert(authSchemeOptionIt != authSchemeOptions.end()); + + if (authSchemeOptionIt != authSchemeOptions.end()) { + return SelectAuthSchemeOptionOutcome(*authSchemeOptionIt); + } + return AWSError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, + "", + "Failed to select an auth scheme", + false/*retryable*/); + } + + SigningOutcome SignRequest(std::shared_ptr httpRequest, const AuthSchemeOption& targetAuthSchemeOption) const override + { + return AwsClientRequestSigning::SignRequest(httpRequest, targetAuthSchemeOption, m_authSchemes); + } + + bool AdjustClockSkew(HttpResponseOutcome& outcome, const AuthSchemeOption& authSchemeOption) const override + { + return AwsClientRequestSigning::AdjustClockSkew(outcome, authSchemeOption, m_authSchemes); + } + + protected: + ServiceClientConfigurationT m_clientConfig{}; + std::shared_ptr m_endpointProvider{}; + std::shared_ptr m_authSchemeResolver{}; + Aws::UnorderedMap m_authSchemes{}; + }; + +} // namespace client +} // namespace smithy diff --git a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientAsyncRequestContext.h b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientAsyncRequestContext.h index 361189b0081..e47779ac0f2 100644 --- a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientAsyncRequestContext.h +++ b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientAsyncRequestContext.h @@ -16,50 +16,60 @@ namespace smithy { - class AwsClientAsyncRequestCtx + namespace client { - public: - using AwsCoreError = Aws::Client::AWSError; - using HttpResponseOutcome = Aws::Utils::Outcome, AwsCoreError>; - - struct RequestInfo + class AwsSmithyClientAsyncRequestContext { - Aws::Utils::DateTime ttl; - long attempt; - long maxAttempts; + public: + using AwsCoreError = Aws::Client::AWSError; + using HttpResponseOutcome = Aws::Utils::Outcome, AwsCoreError>; + using ResponseHandlerFunc = std::function; - explicit operator Aws::String() const + struct RequestInfo { - Aws::StringStream ss; - if (ttl.WasParseSuccessful() && ttl != Aws::Utils::DateTime()) + Aws::Utils::DateTime ttl; + long attempt; + long maxAttempts; + + Aws::String ToString() const { - assert(attempt > 1); - ss << "ttl=" << ttl.ToGmtString(Aws::Utils::DateFormat::ISO_8601_BASIC) << "; "; + Aws::StringStream ss; + if (ttl.WasParseSuccessful() && ttl != Aws::Utils::DateTime()) + { + assert(attempt > 1); + ss << "ttl=" << ttl.ToGmtString(Aws::Utils::DateFormat::ISO_8601_BASIC) << "; "; + } + ss << "attempt=" << attempt; + if (maxAttempts > 0) + { + ss << "; max=" << maxAttempts; + } + return ss.str(); } - ss << "attempt=" << attempt; - if (maxAttempts > 0) + + explicit operator Aws::String() const { - ss << "; max=" << maxAttempts; + return ToString(); } - return ss.str(); - } - }; + }; - Aws::String m_invocationId; - Aws::Http::HttpMethod m_method; - const Aws::AmazonWebServiceRequest* m_pRequest; // optional + Aws::String m_invocationId; + Aws::Http::HttpMethod m_method; + const Aws::AmazonWebServiceRequest* m_pRequest; // optional - RequestInfo m_requestInfo; - Aws::String m_requestName; - std::shared_ptr m_httpRequest; - Aws::Endpoint::AWSEndpoint m_endpoint; + RequestInfo m_requestInfo; + Aws::String m_requestName; + std::shared_ptr m_httpRequest; + AuthSchemeOption m_authSchemeOption; + Aws::Endpoint::AWSEndpoint m_endpoint; - Aws::Crt::Optional m_lastError; + Aws::Crt::Optional m_lastError; - size_t m_retryCount; - Aws::Vector m_monitoringContexts; + size_t m_retryCount; + Aws::Vector m_monitoringContexts; - std::function m_responseHandler; - std::shared_ptr m_pExecutor; - }; + ResponseHandlerFunc m_responseHandler; + std::shared_ptr m_pExecutor; + }; + } // namespace client } // namespace smithy diff --git a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientBase.h b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientBase.h new file mode 100644 index 00000000000..962328e8209 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientBase.h @@ -0,0 +1,138 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace Aws +{ + namespace Utils + { + namespace RateLimits + { + class RateLimiterInterface; + } + } + + namespace Http + { + class HttpClient; + } + + namespace Client + { + class AWSErrorMarshaller; + class RetryStrategy; + } + + namespace Utils + { + namespace Threading + { + class Executor; + } + } + + class AmazonWebServiceRequest; +} + +namespace Aws +{ + namespace Endpoint + { + class AWSEndpoint; + } +} + +namespace smithy { +namespace client +{ + class AwsSmithyClientAsyncRequestContext; + /* Non-template base client class that contains main Aws Client Request pipeline logic */ + class AwsSmithyClientBase + { + public: + using HttpRequest = Aws::Http::HttpRequest; + using HttpResponse = Aws::Http::HttpResponse; + using CoreErrors = Aws::Client::CoreErrors; + using AWSError = Aws::Client::AWSError; + using ClientError = AWSError; + using SigningError = AWSError; + using SigningOutcome = Aws::Utils::FutureOutcome, SigningError>; + using EndpointUpdateCallback = std::function; + using HttpResponseOutcome = Aws::Utils::Outcome, AWSError>; + using ResponseHandlerFunc = std::function; + using SelectAuthSchemeOptionOutcome = Aws::Utils::Outcome; + using ResolveEndpointOutcome = Aws::Utils::Outcome; + + AwsSmithyClientBase(Aws::Client::ClientConfiguration& clientConfig, + Aws::String serviceName, + std::shared_ptr httpClient, + std::shared_ptr errorMarshaller) : + m_clientConfig(clientConfig), + m_serviceName(std::move(serviceName)), + m_userAgent(Aws::Client::ComputeUserAgentString(&clientConfig)), + m_httpClient(std::move(httpClient)), + m_errorMarshaller(std::move(errorMarshaller)) + {} + + AwsSmithyClientBase(const AwsSmithyClientBase&) = delete; + AwsSmithyClientBase(AwsSmithyClientBase&&) = delete; + AwsSmithyClientBase& operator=(const AwsSmithyClientBase&) = delete; + AwsSmithyClientBase& operator=(AwsSmithyClientBase&&) = delete; + + virtual ~AwsSmithyClientBase() = default; + + void MakeRequestAsync(Aws::AmazonWebServiceRequest const * const request, + const char* requestName, + Aws::Http::HttpMethod method, + EndpointUpdateCallback&& endpointCallback, + ResponseHandlerFunc&& responseHandler, + std::shared_ptr pExecutor) const; + + HttpResponseOutcome MakeRequestSync(Aws::AmazonWebServiceRequest const * const request, + const char* requestName, + Aws::Http::HttpMethod method, + EndpointUpdateCallback&& endpointCallback) const; + + protected: + /** + * Transforms the AmazonWebServicesResult object into an HttpRequest. + */ + std::shared_ptr BuildHttpRequest(const std::shared_ptr& pRequestCtx, const Aws::Http::URI& uri, Aws::Http::HttpMethod method) const; + + + virtual void AttemptOneRequestAsync(std::shared_ptr pRequestCtx) const; + + virtual void HandleAsyncReply(std::shared_ptr pRequestCtx, + std::shared_ptr httpResponse) const; + + inline virtual const char* GetServiceClientName() const { return m_serviceName.c_str(); } + + virtual ResolveEndpointOutcome ResolveEndpoint(const Aws::Endpoint::EndpointParameters& endpointParameters, EndpointUpdateCallback&& epCallback) const = 0; + virtual SelectAuthSchemeOptionOutcome SelectAuthSchemeOption(const AwsSmithyClientAsyncRequestContext& ctx) const = 0; + virtual SigningOutcome SignRequest(std::shared_ptr httpRequest, const AuthSchemeOption& targetAuthSchemeOption) const = 0; + virtual bool AdjustClockSkew(HttpResponseOutcome& outcome, const AuthSchemeOption& authSchemeOption) const = 0; + + protected: + Aws::Client::ClientConfiguration& m_clientConfig; + Aws::String m_serviceName; + Aws::String m_userAgent; + + std::shared_ptr m_httpClient; + std::shared_ptr m_errorMarshaller; + }; +} // namespace client +} // namespace smithy diff --git a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyRequestSigning.h b/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyRequestSigning.h deleted file mode 100644 index 11cc0d47a6e..00000000000 --- a/src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyRequestSigning.h +++ /dev/null @@ -1,126 +0,0 @@ -/** -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ -#pragma once - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include - - -namespace smithy -{ - template - class AwsClientRequestSigning - { - public: - using HttpRequest = Aws::Http::HttpRequest; - using SigningError = Aws::Client::AWSError; - using SigningOutcome = Aws::Utils::FutureOutcome; - - static SigningOutcome SignRequest(const HttpRequest& HTTPRequest, const AuthOption& authOption, - const Aws::UnorderedMap& authSchemes) - { - auto authSchemeIt = authSchemes.find(authOption.schemeId); - if (authSchemeIt == authSchemes.end()) - { - assert(!"Auth scheme has not been found for a given auth option!"); - return (SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, - "", - "Requested AuthOption was not found within client Auth Schemes", - false/*retryable*/)); - } - - AuthSchemesVariantT authScheme = *authSchemeIt; - - return SignWithAuthScheme(HTTPRequest, authScheme, authOption); - } - - protected: - struct SignerVisitor - { - SignerVisitor(const HttpRequest& httpRequest, const AuthOption& targetAuthOption) - : m_httpRequest(httpRequest), m_targetAuthOption(targetAuthOption) - { - } - - const HttpRequest& m_httpRequest; - const AuthOption& m_targetAuthOption; - - Aws::Crt::Optional result; - - template - void operator()(AuthSchemeAlternativeT& authScheme) - { - // Auth Scheme Variant alternative contains the requested auth option - assert(strcmp(authScheme.schemeId, m_targetAuthOption.schemeId) == 0); - - using IdentityT = typename decltype(authScheme)::IdentityT; - using IdentityResolver = IdentityResolverBase; - using Signer = AwsSignerBase; - - std::shared_ptr identityResolver = authScheme.identityResolver(); - if (!identityResolver) - { - result.emplace(SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, - "", - "Auth scheme provided a nullptr identityResolver", - false/*retryable*/)); - return; - } - - static_assert( - std::is_same, typename decltype(identityResolver - )::IdentityT>::value); - static_assert(std::is_base_of, decltype(identityResolver)>::value); - - IdentityT identity = identityResolver->getIdentity(m_targetAuthOption.identityProperties); - - std::shared_ptr signer = authScheme.signer(); - if (!signer) - { - result.emplace(SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, - "", - "Auth scheme provided a nullptr signer", - false/*retryable*/)); - return; - } - - - static_assert(std::is_same, typename decltype(signer)::IdentityT>::value); - static_assert(std::is_base_of, decltype(signer)>::value); - - result.emplace(signer->sign(m_httpRequest, identity, m_targetAuthOption.signerProperties)); - } - }; - - static - SigningOutcome SignWithAuthScheme(const HttpRequest& HTTPRequest, const AuthSchemesVariantT& authSchemesVariant, - const AuthOption& targetAuthOption) - { - SignerVisitor visitor(HTTPRequest, targetAuthOption); - visitor.Visit(authSchemesVariant); - - if (!visitor.result) - { - return (SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, - "", - "Failed to sign with an unknown error", - false/*retryable*/)); - } - return std::move(*visitor.result); - } - }; -} diff --git a/src/aws-cpp-sdk-core/include/smithy/client/common/AwsSmithyClientUtils.h b/src/aws-cpp-sdk-core/include/smithy/client/common/AwsSmithyClientUtils.h new file mode 100644 index 00000000000..5cffdd75eef --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/client/common/AwsSmithyClientUtils.h @@ -0,0 +1,160 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once + +#include +#include + +#include + +#include +#include +#include +#include + + +namespace smithy +{ + namespace client + { + static const char AWS_SMITHY_CLIENT_UTILS_TAG[] = "AwsSmithyClientUtils"; + + class Utils + { + public: + using HttpMethod = Aws::Http::HttpMethod; + using HeaderValueCollection = Aws::Http::HeaderValueCollection; + using DateTime = Aws::Utils::DateTime; + using DateFormat = Aws::Utils::DateFormat; + using ClientError = Aws::Client::AWSError; + + static void AppendHeaderValueToRequest(const std::shared_ptr& httpRequest, + const Aws::String& header, + const Aws::String& value) + { + assert(httpRequest); + assert(!header.empty()); + + if (!httpRequest->HasHeader(header.c_str())) + { + httpRequest->SetHeaderValue(header, value); + } + else + { + Aws::String contentEncoding = httpRequest->GetHeaderValue(header.c_str()); + contentEncoding.append(",").append(value); + httpRequest->SetHeaderValue(header, contentEncoding); + } + } + + static void AddContentBodyToRequest(const std::shared_ptr& httpRequest, + const std::shared_ptr& body, + const std::shared_ptr& httpClient, + bool needsContentMd5, + bool isChunked) + { + assert(httpRequest); + + httpRequest->AddContentBody(body); + + //If there is no body, we have a content length of 0 + //note: we also used to remove content-type, but S3 actually needs content-type on InitiateMultipartUpload and it isn't + //forbidden by the spec. If we start getting weird errors related to this, make sure it isn't caused by this removal. + if (!body) + { + AWS_LOGSTREAM_TRACE(AWS_SMITHY_CLIENT_UTILS_TAG, "No content body, content-length headers"); + + if (httpRequest->GetMethod() == HttpMethod::HTTP_POST || httpRequest->GetMethod() == + HttpMethod::HTTP_PUT) + { + httpRequest->SetHeaderValue(Aws::Http::CONTENT_LENGTH_HEADER, "0"); + } + else + { + httpRequest->DeleteHeader(Aws::Http::CONTENT_LENGTH_HEADER); + } + } + + //Add transfer-encoding:chunked to header + if (body && isChunked && !httpRequest->HasHeader(Aws::Http::CONTENT_LENGTH_HEADER)) + { + httpRequest->SetTransferEncoding(Aws::Http::CHUNKED_VALUE); + } + //in the scenario where we are adding a content body as a stream, the request object likely already + //has a content-length header set and we don't want to seek the stream just to find this information. + else if (body && !httpRequest->HasHeader(Aws::Http::CONTENT_LENGTH_HEADER)) + { + assert(httpClient); + if (!httpClient->SupportsChunkedTransferEncoding()) + { + AWS_LOGSTREAM_WARN(AWS_SMITHY_CLIENT_UTILS_TAG, + "This http client doesn't support transfer-encoding:chunked. " << + "The request may fail if it's not a seekable stream."); + } + AWS_LOGSTREAM_TRACE(AWS_SMITHY_CLIENT_UTILS_TAG, + "Found body, but content-length has not been set, attempting to compute content-length"); + body->seekg(0, body->end); + auto streamSize = body->tellg(); + body->seekg(0, body->beg); + Aws::StringStream ss; + ss << streamSize; + httpRequest->SetContentLength(ss.str()); + } + + if (needsContentMd5 && body && !httpRequest->HasHeader(Aws::Http::CONTENT_MD5_HEADER)) + { + AWS_LOGSTREAM_TRACE(AWS_SMITHY_CLIENT_UTILS_TAG, "Found body, and content-md5 needs to be set" << + ", attempting to compute content-md5"); + + //changing the internal state of the hash computation is not a logical state + //change as far as constness goes for this class. Due to the platform specificness + //of hash computations, we can't control the fact that computing a hash mutates + //state on some platforms such as windows (but that isn't a concern of this class. + + // TODO: check refactoring from: + // auto md5HashResult = const_cast(this)->m_hash->Calculate(*body); + Aws::Utils::Crypto::MD5 hash; + auto md5HashResult = hash.Calculate(*body); + body->clear(); + if (md5HashResult.IsSuccess()) + { + httpRequest->SetHeaderValue(Aws::Http::CONTENT_MD5_HEADER, + Aws::Utils::HashingUtils::Base64Encode(md5HashResult.GetResult())); + } + } + } + + static bool DoesResponseGenerateError(const std::shared_ptr& response) + { + assert(response); + if (response->HasClientError()) + return true; + + static const int SUCCESS_RESPONSE_MIN = 200; + static const int SUCCESS_RESPONSE_MAX = 299; + + const int responseCode = static_cast(response->GetResponseCode()); + return responseCode < SUCCESS_RESPONSE_MIN || responseCode > SUCCESS_RESPONSE_MAX; + } + + static Aws::Utils::DateTime GetServerTimeFromError(const ClientError& error) + { + const HeaderValueCollection& headers = error.GetResponseHeaders(); + auto awsDateHeaderIter = headers.find(Aws::Utils::StringUtils::ToLower(Aws::Http::AWS_DATE_HEADER)); + auto dateHeaderIter = headers.find(Aws::Utils::StringUtils::ToLower(Aws::Http::DATE_HEADER)); + if (awsDateHeaderIter != headers.end()) + { + return DateTime(awsDateHeaderIter->second.c_str(), DateFormat::AutoDetect); + } + else if (dateHeaderIter != headers.end()) + { + return DateTime(dateHeaderIter->second.c_str(), DateFormat::AutoDetect); + } + return DateTime(); + } + }; + } +} diff --git a/src/aws-cpp-sdk-core/include/smithy/client/common/AwsSmithyRequestSigning.h b/src/aws-cpp-sdk-core/include/smithy/client/common/AwsSmithyRequestSigning.h new file mode 100644 index 00000000000..0554d7197e7 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/client/common/AwsSmithyRequestSigning.h @@ -0,0 +1,215 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + + +namespace smithy +{ + static const char AWS_SMITHY_CLIENT_SIGNING_TAG[] = "AwsClientRequestSigning"; + //4 Minutes + static const std::chrono::milliseconds TIME_DIFF_MAX = std::chrono::minutes(4); + //-4 Minutes + static const std::chrono::milliseconds TIME_DIFF_MIN = std::chrono::minutes(-4); + + template + class AwsClientRequestSigning + { + public: + using HttpRequest = Aws::Http::HttpRequest; + using SigningError = Aws::Client::AWSError; + using SigningOutcome = Aws::Utils::FutureOutcome, SigningError>; + using HttpResponseOutcome = Aws::Utils::Outcome, Aws::Client::AWSError>; + + static SigningOutcome SignRequest(std::shared_ptr HTTPRequest, const AuthSchemeOption& authSchemeOption, + const Aws::UnorderedMap& authSchemes) + { + auto authSchemeIt = authSchemes.find(authSchemeOption.schemeId); + if (authSchemeIt == authSchemes.end()) + { + assert(!"Auth scheme has not been found for a given auth option!"); + return (SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, + "", + "Requested AuthSchemeOption was not found within client Auth Schemes", + false/*retryable*/)); + } + + AuthSchemesVariantT authScheme = *authSchemeIt; + + return SignWithAuthScheme(std::move(HTTPRequest), authScheme, authSchemeOption); + } + + static bool AdjustClockSkew(HttpResponseOutcome& outcome, const AuthSchemeOption& authSchemeOption, + const Aws::UnorderedMap& authSchemes) + { + assert(!outcome.IsSuccess()); + AWS_LOGSTREAM_WARN(AWS_SMITHY_CLIENT_SIGNING_TAG, "If the signature check failed. This could be because of a time skew. Attempting to adjust the signer."); + + using DateTime = Aws::Utils::DateTime; + DateTime serverTime = smithy::client::Utils::GetServerTimeFromError(outcome.GetError()); + + auto authSchemeIt = authSchemes.find(authSchemeOption.schemeId); + if (authSchemeIt == authSchemes.end()) + { + assert(!"Auth scheme has not been found for a given auth option!"); + return false; + } + AuthSchemesVariantT authScheme = *authSchemeIt; + + ClockSkewVisitor visitor(outcome, serverTime, authSchemeOption); + visitor.Visit(authScheme); + + return visitor.m_resultShouldWait; + } + + + protected: + struct SignerVisitor + { + SignerVisitor(std::shared_ptr httpRequest, const AuthSchemeOption& targetAuthSchemeOption) + : m_httpRequest(std::move(httpRequest)), m_targetAuthSchemeOption(targetAuthSchemeOption) + { + } + + const std::shared_ptr m_httpRequest; + const AuthSchemeOption& m_targetAuthSchemeOption; + + Aws::Crt::Optional result; + + template + void operator()(AuthSchemeAlternativeT& authScheme) + { + // Auth Scheme Variant alternative contains the requested auth option + assert(strcmp(authScheme.schemeId, m_targetAuthSchemeOption.schemeId) == 0); + + using IdentityT = typename decltype(authScheme)::IdentityT; + using IdentityResolver = IdentityResolverBase; + using Signer = AwsSignerBase; + + std::shared_ptr identityResolver = authScheme.identityResolver(); + if (!identityResolver) + { + result.emplace(SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, + "", + "Auth scheme provided a nullptr identityResolver", + false/*retryable*/)); + return; + } + + static_assert( + std::is_same, typename decltype(identityResolver + )::IdentityT>::value, "Must be the same type"); + static_assert(std::is_base_of, decltype(identityResolver)>::value, "Must be the same type"); + + IdentityT identity = identityResolver->getIdentity(m_targetAuthSchemeOption.identityProperties); + + std::shared_ptr signer = authScheme.signer(); + if (!signer) + { + result.emplace(SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, + "", + "Auth scheme provided a nullptr signer", + false/*retryable*/)); + return; + } + + + static_assert(std::is_same, typename decltype(signer)::IdentityT>::value, "Must be the same type"); + static_assert(std::is_base_of, decltype(signer)>::value, "Must be the same type"); + + result.emplace(signer->sign(m_httpRequest, identity, m_targetAuthSchemeOption.signerProperties)); + } + }; + + static + SigningOutcome SignWithAuthScheme(std::shared_ptr httpRequest, const AuthSchemesVariantT& authSchemesVariant, + const AuthSchemeOption& targetAuthSchemeOption) + { + SignerVisitor visitor(httpRequest, targetAuthSchemeOption); + visitor.Visit(authSchemesVariant); + + if (!visitor.result) + { + return (SigningError(Aws::Client::CoreErrors::CLIENT_SIGNING_FAILURE, + "", + "Failed to sign with an unknown error", + false/*retryable*/)); + } + return std::move(*visitor.result); + } + + struct ClockSkewVisitor + { + using DateTime = Aws::Utils::DateTime; + using DateFormat = Aws::Utils::DateFormat; + using ClientError = Aws::Client::AWSError; + + ClockSkewVisitor(HttpResponseOutcome& outcome, const DateTime& serverTime, const AuthSchemeOption& targetAuthSchemeOption) + : m_outcome(outcome), m_serverTime(serverTime), m_targetAuthSchemeOption(targetAuthSchemeOption) + { + } + + bool m_resultShouldWait = false; + HttpResponseOutcome& m_outcome; + const Aws::Utils::DateTime& m_serverTime; + const AuthSchemeOption& m_targetAuthSchemeOption; + + template + void operator()(AuthSchemeAlternativeT& authScheme) + { + // Auth Scheme Variant alternative contains the requested auth option + assert(strcmp(authScheme.schemeId, m_targetAuthSchemeOption.schemeId) == 0); + + using IdentityT = typename decltype(authScheme)::IdentityT; + using Signer = AwsSignerBase; + + std::shared_ptr signer = authScheme.signer(); + if (!signer) + { + AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_SIGNING_TAG, "Failed to adjust signing clock skew. Signer is null."); + return; + } + + const auto signingTimestamp = signer->GetSigningTimestamp(); + if (!m_serverTime.WasParseSuccessful() || m_serverTime == DateTime()) + { + AWS_LOGSTREAM_DEBUG(AWS_SMITHY_CLIENT_SIGNING_TAG, "Date header was not found in the response, can't attempt to detect clock skew"); + return; + } + + AWS_LOGSTREAM_DEBUG(AWS_SMITHY_CLIENT_SIGNING_TAG, "Server time is " << m_serverTime.ToGmtString(DateFormat::RFC822) << ", while client time is " << DateTime::Now().ToGmtString(DateFormat::RFC822)); + auto diff = DateTime::Diff(m_serverTime, signingTimestamp); + //only try again if clock skew was the cause of the error. + if (diff >= TIME_DIFF_MAX || diff <= TIME_DIFF_MIN) + { + diff = DateTime::Diff(m_serverTime, DateTime::Now()); + AWS_LOGSTREAM_INFO(AWS_SMITHY_CLIENT_SIGNING_TAG, "Computed time difference as " << diff.count() << " milliseconds. Adjusting signer with the skew."); + signer->SetClockSkew(diff); + ClientError newError(m_outcome.GetError()); + newError.SetRetryableType(Aws::Client::RetryableType::RETRYABLE); + + m_outcome = std::move(newError); + m_resultShouldWait = true; + } + } + }; + + }; +} diff --git a/src/aws-cpp-sdk-core/include/smithy/client/features/Checksums.h b/src/aws-cpp-sdk-core/include/smithy/client/features/Checksums.h new file mode 100644 index 00000000000..729ace43391 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/client/features/Checksums.h @@ -0,0 +1,242 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace smithy +{ + namespace client + { + static const char AWS_SMITHY_CLIENT_CHECKSUM[] = "AwsSmithyClientChecksums"; + + static const char CHECKSUM_CONTENT_MD5_HEADER[] = "content-md5"; + + class Checksums + { + public: + using HeaderValueCollection = Aws::Http::HeaderValueCollection; + using HashingUtils = Aws::Utils::HashingUtils; + using MD5 = Aws::Utils::Crypto::MD5; + using CRC32 = Aws::Utils::Crypto::CRC32; + using CRC32C = Aws::Utils::Crypto::CRC32C; + using Sha256 = Aws::Utils::Crypto::Sha256; + using Sha1 = Aws::Utils::Crypto::Sha1; + using PrecalculatedHash = Aws::Utils::Crypto::PrecalculatedHash; + + + static std::shared_ptr GetBodyStream(const Aws::AmazonWebServiceRequest& request) + { + if (request.GetBody() != nullptr) + { + return request.GetBody(); + } + // Return an empty string stream for no body + return Aws::MakeShared(AWS_SMITHY_CLIENT_CHECKSUM, ""); + } + + static void AddChecksumToRequest(const std::shared_ptr& httpRequest, + const Aws::AmazonWebServiceRequest& request) + { + Aws::String checksumAlgorithmName = Aws::Utils::StringUtils::ToLower( + request.GetChecksumAlgorithmName().c_str()); + if (request.GetServiceSpecificParameters()) + { + auto requestChecksumOverride = request.GetServiceSpecificParameters()->parameterMap.find( + "overrideChecksum"); + if (requestChecksumOverride != request.GetServiceSpecificParameters()->parameterMap.end()) + { + checksumAlgorithmName = requestChecksumOverride->second; + } + } + + bool shouldSkipChecksum = request.GetServiceSpecificParameters() && + request.GetServiceSpecificParameters()->parameterMap.find("overrideChecksumDisable") != + request.GetServiceSpecificParameters()->parameterMap.end(); + + //Check if user has provided the checksum algorithm + if (!checksumAlgorithmName.empty() && !shouldSkipChecksum) + { + // Check if user has provided a checksum value for the specified algorithm + const Aws::String checksumType = "x-amz-checksum-" + checksumAlgorithmName; + const HeaderValueCollection& headers = request.GetHeaders(); + const auto checksumHeader = headers.find(checksumType); + bool checksumValueAndAlgorithmProvided = checksumHeader != headers.end(); + + // For non-streaming payload, the resolved checksum location is always header. + // For streaming payload, the resolved checksum location depends on whether it is an unsigned payload, we let AwsAuthSigner decide it. + if (request.IsStreaming() && checksumValueAndAlgorithmProvided) + { + const auto hash = Aws::MakeShared( + AWS_SMITHY_CLIENT_CHECKSUM, checksumHeader->second); + httpRequest->SetRequestHash(checksumAlgorithmName, hash); + } + else if (checksumValueAndAlgorithmProvided) + { + httpRequest->SetHeaderValue(checksumType, checksumHeader->second); + } + else if (checksumAlgorithmName == "crc32") + { + if (request.IsStreaming()) + { + httpRequest->SetRequestHash(checksumAlgorithmName, + Aws::MakeShared(AWS_SMITHY_CLIENT_CHECKSUM)); + } + else + { + httpRequest->SetHeaderValue(checksumType, + HashingUtils::Base64Encode( + HashingUtils::CalculateCRC32(*(GetBodyStream(request))))); + } + } + else if (checksumAlgorithmName == "crc32c") + { + if (request.IsStreaming()) + { + httpRequest->SetRequestHash(checksumAlgorithmName, + Aws::MakeShared(AWS_SMITHY_CLIENT_CHECKSUM)); + } + else + { + httpRequest->SetHeaderValue(checksumType, + HashingUtils::Base64Encode( + HashingUtils::CalculateCRC32C(*(GetBodyStream(request))))); + } + } + else if (checksumAlgorithmName == "sha256") + { + if (request.IsStreaming()) + { + httpRequest->SetRequestHash(checksumAlgorithmName, + Aws::MakeShared(AWS_SMITHY_CLIENT_CHECKSUM)); + } + else + { + httpRequest->SetHeaderValue(checksumType, + HashingUtils::Base64Encode( + HashingUtils::CalculateSHA256(*(GetBodyStream(request))))); + } + } + else if (checksumAlgorithmName == "sha1") + { + if (request.IsStreaming()) + { + httpRequest->SetRequestHash(checksumAlgorithmName, + Aws::MakeShared(AWS_SMITHY_CLIENT_CHECKSUM)); + } + else + { + httpRequest->SetHeaderValue(checksumType, + HashingUtils::Base64Encode( + HashingUtils::CalculateSHA1(*(GetBodyStream(request))))); + } + } + else if (checksumAlgorithmName == "md5" && headers.find(CHECKSUM_CONTENT_MD5_HEADER) == headers.end()) + { + httpRequest->SetHeaderValue(CHECKSUM_CONTENT_MD5_HEADER, + HashingUtils::Base64Encode( + HashingUtils::CalculateMD5(*(GetBodyStream(request))))); + } + else + { + AWS_LOGSTREAM_WARN(AWS_SMITHY_CLIENT_CHECKSUM, + "Checksum algorithm: " << checksumAlgorithmName << + "is not supported by SDK."); + } + } + + // Response checksums + if (request.ShouldValidateResponseChecksum()) + { + for (const Aws::String& responseChecksumAlgorithmName : request.GetResponseChecksumAlgorithmNames()) + { + checksumAlgorithmName = Aws::Utils::StringUtils::ToLower(responseChecksumAlgorithmName.c_str()); + + if (checksumAlgorithmName == "crc32c") + { + std::shared_ptr crc32c = Aws::MakeShared< + CRC32C>(AWS_SMITHY_CLIENT_CHECKSUM); + httpRequest->AddResponseValidationHash("crc32c", crc32c); + } + else if (checksumAlgorithmName == "crc32") + { + std::shared_ptr crc32 = Aws::MakeShared< + CRC32>(AWS_SMITHY_CLIENT_CHECKSUM); + httpRequest->AddResponseValidationHash("crc", crc32); + } + else if (checksumAlgorithmName == "sha1") + { + std::shared_ptr sha1 = Aws::MakeShared( + AWS_SMITHY_CLIENT_CHECKSUM); + httpRequest->AddResponseValidationHash("sha1", sha1); + } + else if (checksumAlgorithmName == "sha256") + { + std::shared_ptr sha256 = Aws::MakeShared< + Sha256>(AWS_SMITHY_CLIENT_CHECKSUM); + httpRequest->AddResponseValidationHash("sha256", sha256); + } + else + { + AWS_LOGSTREAM_WARN(AWS_SMITHY_CLIENT_CHECKSUM, + "Checksum algorithm: " << checksumAlgorithmName << + " is not supported in validating response body yet."); + } + } + } + } + + using OptionalError = Aws::Crt::Optional>; + + static OptionalError ValidateResponseChecksum(AwsSmithyClientAsyncRequestContext const* const pRequestCtx, + Aws::Http::HttpResponse const* const httpResponse) + { + assert(pRequestCtx); + assert(httpResponse); + assert(pRequestCtx->m_httpRequest); + for (const auto& hashIterator : pRequestCtx->m_httpRequest->GetResponseValidationHashes()) + { + Aws::String checksumHeaderKey = Aws::String("x-amz-checksum-") + hashIterator.first; + // TODO: If checksum ends with -#, then skip + if (httpResponse->HasHeader(checksumHeaderKey.c_str())) + { + const Aws::String& checksumHeaderValue = httpResponse->GetHeader(checksumHeaderKey); + if (HashingUtils::Base64Encode(hashIterator.second->GetHash().GetResult()) != + checksumHeaderValue) + { + auto error = OptionalError( + Aws::Client::AWSError( + 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; + } + // Validate only a single checksum returned in an HTTP response + break; + } + } + return {}; + } + }; + } +} diff --git a/src/aws-cpp-sdk-core/include/smithy/client/features/RecursionDetection.h b/src/aws-cpp-sdk-core/include/smithy/client/features/RecursionDetection.h new file mode 100644 index 00000000000..0837721c732 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/client/features/RecursionDetection.h @@ -0,0 +1,63 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once + +#include +#include +#include + +namespace smithy +{ + namespace client + { + static const char SMITHY_AWS_LAMBDA_FUNCTION_NAME[] = "AWS_LAMBDA_FUNCTION_NAME"; + static const char SMITHY_X_AMZN_TRACE_ID[] = "_X_AMZN_TRACE_ID"; + + class RecursionDetection + { + public: + static void AppendRecursionDetectionHeader(const std::shared_ptr& ioRequest) + { + if (!ioRequest || ioRequest->HasHeader(Aws::Http::X_AMZN_TRACE_ID_HEADER)) + { + return; + } + const Aws::String& awsLambdaFunctionName = Aws::Environment::GetEnv(SMITHY_AWS_LAMBDA_FUNCTION_NAME); + if (awsLambdaFunctionName.empty()) + { + return; + } + Aws::String xAmznTraceIdVal = Aws::Environment::GetEnv(SMITHY_X_AMZN_TRACE_ID); + if (xAmznTraceIdVal.empty()) + { + return; + } + + // Escape all non-printable ASCII characters by percent encoding + Aws::OStringStream xAmznTraceIdValEncodedStr; + for (const char ch : xAmznTraceIdVal) + { + if (ch >= 0x20 && ch <= 0x7e) // ascii chars [32-126] or [' ' to '~'] are not escaped + { + xAmznTraceIdValEncodedStr << ch; + } + else + { + // A percent-encoded octet is encoded as a character triplet + xAmznTraceIdValEncodedStr << '%' // consisting of the percent character "%" + << std::hex << std::setfill('0') << std::setw(2) << std::uppercase + << (size_t)ch + //followed by the two hexadecimal digits representing that octet's numeric value + << std::dec << std::setfill(' ') << std::setw(0) << std::nouppercase; + } + } + xAmznTraceIdVal = xAmznTraceIdValEncodedStr.str(); + + ioRequest->SetHeaderValue(Aws::Http::X_AMZN_TRACE_ID_HEADER, xAmznTraceIdVal); + } + }; + } +} diff --git a/src/aws-cpp-sdk-core/include/smithy/client/features/RequestPayloadCompression.h b/src/aws-cpp-sdk-core/include/smithy/client/features/RequestPayloadCompression.h new file mode 100644 index 00000000000..c978e3d9ea1 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/client/features/RequestPayloadCompression.h @@ -0,0 +1,63 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace smithy +{ + namespace client + { + static const char AWS_CLIENT_REQUEST_COMPRESSION_LOG_TAG[] = "RequestPayloadCompression"; + + class RequestPayloadCompression + { + public: + static void AddCompressedContentBodyToRequest(const Aws::AmazonWebServiceRequest* pRequest, + const std::shared_ptr& httpRequest, + const Aws::Client::CompressionAlgorithm& compressionAlgorithm, + const std::shared_ptr& httpClient) + { + if (Aws::Client::CompressionAlgorithm::NONE != compressionAlgorithm) + { + Aws::Client::RequestCompression rc; + auto compressOutcome = rc.compress(pRequest->GetBody(), compressionAlgorithm); + + if (compressOutcome.IsSuccess()) + { + const Aws::String compressionAlgorithmId = Aws::Client::GetCompressionAlgorithmId( + compressionAlgorithm); + Utils::AppendHeaderValueToRequest(httpRequest, Aws::Http::CONTENT_ENCODING_HEADER, + compressionAlgorithmId); + Utils::AddContentBodyToRequest(httpRequest, + compressOutcome.GetResult(), + httpClient, + pRequest->ShouldComputeContentMd5(), + pRequest->IsStreaming() && pRequest->IsChunked() && httpClient-> + SupportsChunkedTransferEncoding()); + } + else + { + AWS_LOGSTREAM_ERROR(AWS_CLIENT_REQUEST_COMPRESSION_LOG_TAG, + "Failed to compress request, submitting uncompressed"); + Utils::AddContentBodyToRequest(httpRequest, + pRequest->GetBody(), + httpClient, + pRequest->ShouldComputeContentMd5(), + pRequest->IsStreaming() && pRequest->IsChunked() && httpClient-> + SupportsChunkedTransferEncoding()); + } + } + } + }; + } +} diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthOption.h b/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthOption.h deleted file mode 100644 index 1403ed61eae..00000000000 --- a/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthOption.h +++ /dev/null @@ -1,24 +0,0 @@ -/** -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0. - */ -#pragma once - -#include - -#include -#include - -namespace smithy { - /* AuthOption and AuthOptionResolver */ - class AuthOption - { - using PropertyBag = Aws::UnorderedMap>; - /* note: AuthOption is not connected with AuthScheme by type system, only by the String of schemeId, this is in accordance with SRA */ - public: - static const char* schemeId; - - PropertyBag identityProperties{}; - PropertyBag signerProperties{}; - }; -} \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthScheme.h b/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthScheme.h index 3f22ad2cb54..f6355d86f35 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthScheme.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthScheme.h @@ -8,13 +8,19 @@ #include namespace smithy { - template + template class AuthScheme { public: using IdentityT = IDENTITY_T; - static const uint64_t schemeId; + template + AuthScheme(char const (&iSchemeId)[N]) + { + memcpy(schemeId, iSchemeId, N); + } + + char schemeId[32]; virtual ~AuthScheme() = default; diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthSchemeOption.h b/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthSchemeOption.h new file mode 100644 index 00000000000..5c2bb4b8767 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthSchemeOption.h @@ -0,0 +1,32 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include + +#include +#include +#include + +namespace smithy { + /* AuthSchemeOption and AuthSchemeOptionResolver */ + class AuthSchemeOption + { + using PropertyBag = Aws::UnorderedMap>; + using EndpointParameters = Aws::Vector; + /* note: AuthSchemeOption is not connected with AuthScheme by type system, only by the String of schemeId, this is in accordance with SRA */ + public: + AuthSchemeOption(const char* id = nullptr, PropertyBag identityProps = {}, PropertyBag signerProps = {}, EndpointParameters epParams = {}) + : schemeId(id), identityProperties(identityProps), signerProperties(signerProps), endpointParameters(epParams) + { + } + + const char* schemeId = nullptr; + + PropertyBag identityProperties{}; + PropertyBag signerProperties{}; + EndpointParameters endpointParameters{}; + }; +} \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthSchemeResolverBase.h b/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthSchemeResolverBase.h index d1021979b2b..5b6f8258939 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthSchemeResolverBase.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/auth/AuthSchemeResolverBase.h @@ -4,21 +4,35 @@ */ #pragma once -#include +#include #include #include #include namespace smithy { - template>> + /** + * A base interface for code-generated interfaces for passing in the data required for determining the + * authentication scheme. By default, this only includes the operation name. + */ + class DefaultAuthSchemeResolverParameters + { + public: + Aws::String serviceName; + Aws::String operation; + Aws::Crt::Optional region; + + Aws::UnorderedMap> additionalProperties; + }; + + template class AuthSchemeResolverBase { public: using ServiceAuthSchemeParameters = ServiceAuthSchemeParametersT; virtual ~AuthSchemeResolverBase() = default; - // AuthScheme Resolver returns a list of AuthOptions for some reason, according to the SRA... - virtual std::vector resolveAuthScheme(const ServiceAuthSchemeParameters& identityProperties) = 0; + // AuthScheme Resolver returns a list of AuthSchemeOptions for some reason, according to the SRA... + virtual Aws::Vector resolveAuthScheme(const ServiceAuthSchemeParameters& identityProperties) = 0; }; } \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4AuthScheme.h b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4AuthScheme.h new file mode 100644 index 00000000000..1dd31306a26 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4AuthScheme.h @@ -0,0 +1,52 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include +#include + +#include + +#include +#include + + + +namespace smithy { + constexpr char SIGV4[] = "aws.auth#sigv4"; + + class SigV4AuthScheme : public AuthScheme + { + public: + using AwsCredentialIdentityResolverT = IdentityResolverBase; + using AwsCredentialSignerT = AwsSignerBase; + using SigV4AuthSchemeParameters = DefaultAuthSchemeResolverParameters; + + explicit SigV4AuthScheme(const SigV4AuthSchemeParameters& parameters) + : AuthScheme(SIGV4) + { + m_identityResolver = Aws::MakeShared("SigV4AuthScheme"); + assert(m_identityResolver); + + m_signer = Aws::MakeShared("SigV4AuthScheme", parameters); + assert(m_signer); + } + + virtual ~SigV4AuthScheme() = default; + + std::shared_ptr identityResolver() override + { + return m_identityResolver; + } + + std::shared_ptr signer() override + { + return m_signer; + } + protected: + std::shared_ptr m_identityResolver; + std::shared_ptr m_signer; + }; +} diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4AuthSchemeOption.h b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4AuthSchemeOption.h new file mode 100644 index 00000000000..b85c7e489bf --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4AuthSchemeOption.h @@ -0,0 +1,16 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include + +namespace smithy { + struct SigV4AuthSchemeOption + { + static AuthSchemeOption sigV4AuthSchemeOption; + }; + + AuthSchemeOption SigV4AuthSchemeOption::sigV4AuthSchemeOption = AuthSchemeOption("aws.auth#sigv4"); +} \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4AuthSchemeResolver.h b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4AuthSchemeResolver.h new file mode 100644 index 00000000000..60c825fb7e8 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/identity/auth/built-in/SigV4AuthSchemeResolver.h @@ -0,0 +1,25 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include +#include + + +namespace smithy { + template + class SigV4AuthSchemeResolver : public AuthSchemeResolverBase + { + public: + using ServiceAuthSchemeParameters = ServiceAuthSchemeParametersT; + virtual ~SigV4AuthSchemeResolver() = default; + + Aws::Vector resolveAuthScheme(const ServiceAuthSchemeParameters& identityProperties) override + { + AWS_UNREFERENCED_PARAM(identityProperties); + return {SigV4AuthSchemeOption::sigV4AuthSchemeOption}; + } + }; +} \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsCredentialIdentity.h b/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsCredentialIdentity.h index 6ea04a3b30c..9767ec8d461 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsCredentialIdentity.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsCredentialIdentity.h @@ -9,10 +9,17 @@ namespace smithy { class AwsCredentialIdentity : public AwsCredentialIdentityBase { public: - virtual Aws::String accessKeyId() override; - virtual Aws::String secretAccessKey() override; - virtual Aws::Crt::Optional sessionToken() override; - virtual Aws::Crt::Optional expiration() override; + AwsCredentialIdentity(const Aws::String& accessKeyId, + const Aws::String& secretAccessKey, + const Aws::Crt::Optional& sessionToken, + const Aws::Crt::Optional& expiration) + : m_accessKeyId(accessKeyId), m_secretAccessKey(secretAccessKey), + m_sessionToken(sessionToken), m_expiration(expiration) {} + + Aws::String accessKeyId() const override; + Aws::String secretAccessKey() const override; + Aws::Crt::Optional sessionToken() const override; + Aws::Crt::Optional expiration() const override; protected: Aws::String m_accessKeyId; diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsCredentialIdentityBase.h b/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsCredentialIdentityBase.h index 8b0c8fc0a86..98d0697b107 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsCredentialIdentityBase.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsCredentialIdentityBase.h @@ -9,9 +9,9 @@ namespace smithy { class AwsCredentialIdentityBase : public AwsIdentity { public: - virtual Aws::String accessKeyId() = 0; - virtual Aws::String secretAccessKey() = 0; - virtual Aws::Crt::Optional sessionToken() = 0; - virtual Aws::Crt::Optional expiration() override = 0 ; + virtual Aws::String accessKeyId() const = 0; + virtual Aws::String secretAccessKey() const = 0; + virtual Aws::Crt::Optional sessionToken() const = 0; + virtual Aws::Crt::Optional expiration() const override = 0 ; }; } \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsIdentity.h b/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsIdentity.h index 42438500240..661290ebf94 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsIdentity.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/identity/AwsIdentity.h @@ -14,7 +14,7 @@ namespace smithy { using DateTime = Aws::Utils::DateTime; virtual ~AwsIdentity(){}; - virtual Aws::Crt::Optional expiration() { + virtual Aws::Crt::Optional expiration() const { return Aws::Crt::Optional(); }; }; diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/identity/impl/AwsCredentialIdentityImpl.h b/src/aws-cpp-sdk-core/include/smithy/identity/identity/impl/AwsCredentialIdentityImpl.h index 6ebeb88b883..63ca9aedc82 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/identity/impl/AwsCredentialIdentityImpl.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/identity/impl/AwsCredentialIdentityImpl.h @@ -8,19 +8,19 @@ #include namespace smithy { - inline Aws::String AwsCredentialIdentity::accessKeyId() { + inline Aws::String AwsCredentialIdentity::accessKeyId() const { return m_accessKeyId; } - inline Aws::String AwsCredentialIdentity::secretAccessKey() { + inline Aws::String AwsCredentialIdentity::secretAccessKey() const { return m_secretAccessKey; } - inline Aws::Crt::Optional AwsCredentialIdentity::sessionToken() { + inline Aws::Crt::Optional AwsCredentialIdentity::sessionToken() const { return m_sessionToken; } - inline Aws::Crt::Optional AwsCredentialIdentity::expiration() { + inline Aws::Crt::Optional AwsCredentialIdentity::expiration() const { return m_expiration; } } \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsBearerTokenIdentityResolver.h b/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsBearerTokenIdentityResolver.h index 33aa23f651d..5c387797e15 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsBearerTokenIdentityResolver.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsBearerTokenIdentityResolver.h @@ -14,6 +14,6 @@ namespace smithy { using IdentityT = AwsBearerTokenIdentity; virtual ~AwsBearerTokenIdentityResolver() = default; - virtual ResolveIdentityFutureOutcome getIdentity(const IdentityProperties& identityProperties, const AdditionalParameters& additionalParameters) = 0; + ResolveIdentityFutureOutcome getIdentity(const IdentityProperties& identityProperties, const AdditionalParameters& additionalParameters) override = 0; }; } \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsCredentialIdentityResolver.h b/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsCredentialIdentityResolver.h index b0295cf4fb5..df0e3f6dc81 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsCredentialIdentityResolver.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsCredentialIdentityResolver.h @@ -9,11 +9,11 @@ #include namespace smithy { - class AwsCredentialIdentityResolver : public IdentityResolverBase { + class AwsCredentialIdentityResolver : public IdentityResolverBase { public: using IdentityT = AwsCredentialIdentity; virtual ~AwsCredentialIdentityResolver() = default; - virtual ResolveIdentityFutureOutcome getIdentity(const IdentityProperties& identityProperties, const AdditionalParameters& additionalParameters) = 0; + ResolveIdentityFutureOutcome getIdentity(const IdentityProperties& identityProperties, const AdditionalParameters& additionalParameters) override = 0; }; } \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsIdentityResolverBase.h b/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsIdentityResolverBase.h index 50d80eff6b9..5f89e775411 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsIdentityResolverBase.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/resolver/AwsIdentityResolverBase.h @@ -25,7 +25,7 @@ namespace smithy { using IdentityProperties = Aws::UnorderedMap>; // IdentityResolvers are asynchronous. - using ResolveIdentityFutureOutcome = Aws::Utils::FutureOutcome>; + using ResolveIdentityFutureOutcome = Aws::Utils::FutureOutcome, Aws::Client::AWSError>; using AdditionalParameters = Aws::UnorderedMap>; // Each Identity has one or more identity resolvers that are able to load the customer’s diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/resolver/built-in/DefaultAwsCredentialIdentityResolver.h b/src/aws-cpp-sdk-core/include/smithy/identity/resolver/built-in/DefaultAwsCredentialIdentityResolver.h new file mode 100644 index 00000000000..448f985dc37 --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/identity/resolver/built-in/DefaultAwsCredentialIdentityResolver.h @@ -0,0 +1,41 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include + +#include +#include + +namespace smithy { + /** + * A smithy SigV4 AWS Credentials resolver wrapper on top of legacy SDK Credentials provider + * TODO: refactor into own signer using smithy design + */ + class DefaultAwsCredentialIdentityResolver : public AwsCredentialIdentityResolver { + public: + using SigV4AuthSchemeParameters = DefaultAuthSchemeResolverParameters; + + DefaultAwsCredentialIdentityResolver() = default; + + ResolveIdentityFutureOutcome getIdentity(const IdentityProperties& identityProperties, const AdditionalParameters& additionalParameters) override + { + AWS_UNREFERENCED_PARAM(identityProperties); + AWS_UNREFERENCED_PARAM(additionalParameters); + + auto legacyCreds = legacyCredentialsProvider.GetAWSCredentials(); + + auto smithyCreds = Aws::MakeUnique("DefaultAwsCredentialIdentityResolver", + legacyCreds.GetAWSAccessKeyId(), legacyCreds.GetAWSSecretKey(), + legacyCreds.GetSessionToken(), legacyCreds.GetExpiration()); + + return ResolveIdentityFutureOutcome(std::move(smithyCreds)); + } + + virtual ~DefaultAwsCredentialIdentityResolver() {}; + protected: + mutable Aws::Auth::DefaultAWSCredentialsProviderChain legacyCredentialsProvider{}; + }; +} diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/signer/AwsSignerBase.h b/src/aws-cpp-sdk-core/include/smithy/identity/signer/AwsSignerBase.h index 7c2ef003139..2e4c3722e7b 100644 --- a/src/aws-cpp-sdk-core/include/smithy/identity/signer/AwsSignerBase.h +++ b/src/aws-cpp-sdk-core/include/smithy/identity/signer/AwsSignerBase.h @@ -4,6 +4,7 @@ */ #pragma once +#include #include #include @@ -14,19 +15,38 @@ namespace smithy { + class AwsSignerCommon { + public: + virtual ~AwsSignerCommon() = default; + /** + * This handles detection of clock skew between clients and the server and adjusts the clock so that the next request will not + * fail on the timestamp check. + */ + virtual void SetClockSkew(const std::chrono::milliseconds& clockSkew) { m_clockSkew = clockSkew; } + /** + * Gets the timestamp being used by the signer. This may include a clock skew if a clock skew has been detected. + */ + virtual Aws::Utils::DateTime GetSigningTimestamp() const { return Aws::Utils::DateTime::Now() + GetClockSkewOffset(); } + + protected: + virtual std::chrono::milliseconds GetClockSkewOffset() const { return m_clockSkew.load(); } + std::atomic m_clockSkew = {}; + }; + template - class AwsSignerBase { + class AwsSignerBase : public AwsSignerCommon { public: using IdentityT = IDENTITY_T; - static_assert(std::is_base_of::value_type, "Identity type should inherit AwsIdentity"); + static_assert(std::is_base_of::value, "Identity type should inherit AwsIdentity"); using SigningProperties = Aws::UnorderedMap; using AdditionalParameters = Aws::UnorderedMap>; using HttpRequest = Aws::Http::HttpRequest; - using SigningFutureOutcome = Aws::Utils::FutureOutcome>; - + using SigningError = Aws::Client::AWSError; + using SigningFutureOutcome = Aws::Utils::FutureOutcome, SigningError>; - virtual SigningFutureOutcome sign(const HttpRequest& httpRequest, const IdentityT& identity, SigningProperties properties, const AdditionalParameters& additionalParameters) = 0; + // signer may copy the original httpRequest or create a new one + virtual SigningFutureOutcome sign(std::shared_ptr httpRequest, const IdentityT& identity, SigningProperties properties, const AdditionalParameters& additionalParameters) = 0; - virtual ~AwsSignerBase(){}; + virtual ~AwsSignerBase() {}; }; } \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/include/smithy/identity/signer/built-in/SigV4Signer.h b/src/aws-cpp-sdk-core/include/smithy/identity/signer/built-in/SigV4Signer.h new file mode 100644 index 00000000000..eca8009b36b --- /dev/null +++ b/src/aws-cpp-sdk-core/include/smithy/identity/signer/built-in/SigV4Signer.h @@ -0,0 +1,51 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#pragma once + +#include +#include +#include + +#include + +#include + +namespace smithy { + /** + * A smithy SigV4 signer wrapper on top of legacy SDK SigV4 signer + * TODO: refactor into own signer using smithy design + */ + class AwsSigV4Signer : public AwsSignerBase { + public: + using SigV4AuthSchemeParameters = DefaultAuthSchemeResolverParameters; + explicit AwsSigV4Signer(const SigV4AuthSchemeParameters& parameters) + : legacySigner(nullptr, parameters.serviceName.c_str(), *parameters.region) + { + } + + SigningFutureOutcome sign(std::shared_ptr httpRequest, const AwsCredentialIdentityBase& identity, SigningProperties properties, const AdditionalParameters& additionalParameters) override + { + AWS_UNREFERENCED_PARAM(additionalParameters); + + Aws::Auth::AWSCredentials legacyCreds(identity.accessKeyId(), identity.secretAccessKey(), *identity.sessionToken(), *identity.expiration()); + + auto signPayloadIt = properties.find("SignPayload"); + bool signPayload = signPayloadIt != properties.end() ? signPayloadIt->second == "true" : false; + + assert(httpRequest); + bool success = legacySigner.SignRequestWithCreds(*httpRequest, legacyCreds, parameters.region->c_str(), parameters.serviceName.c_str(), signPayload); + if (success) + { + return SigningFutureOutcome(std::move(httpRequest)); + } + return SigningError(Aws::Client::CoreErrors::MEMORY_ALLOCATION, "", "Failed to sign the request with sigv4", false); + } + + virtual ~AwsSigV4Signer() {}; + protected: + SigV4AuthSchemeParameters parameters; + Aws::Client::AWSAuthV4Signer legacySigner; + }; +} diff --git a/src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp b/src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp index a14a7b06d05..5f55a842aca 100644 --- a/src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp +++ b/src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp @@ -66,7 +66,10 @@ AWSAuthV4Signer::AWSAuthV4Signer(const std::shared_ptrGetAWSCredentials().GetAWSSecretKey(), DateTime::CalculateGmtTimestampAsString(Aws::Auth::AWSAuthHelper::SIMPLE_DATE_FORMAT_STR), region, m_serviceName); + if (credentialsProvider) + { + ComputeHash(credentialsProvider->GetAWSCredentials().GetAWSSecretKey(), DateTime::CalculateGmtTimestampAsString(Aws::Auth::AWSAuthHelper::SIMPLE_DATE_FORMAT_STR), region, m_serviceName); + } } AWSAuthV4Signer::~AWSAuthV4Signer() @@ -185,11 +188,11 @@ bool AWSAuthV4Signer::ShouldSignHeader(const Aws::String& header) const return m_unsignedHeaders.find(Aws::Utils::StringUtils::ToLower(header.c_str())) == m_unsignedHeaders.cend(); } -bool AWSAuthV4Signer::SignRequest(Aws::Http::HttpRequest& request, const char* region, const char* serviceName, bool signBody) const +bool AWSAuthV4Signer::SignRequestWithCreds(Aws::Http::HttpRequest& request, const AWSCredentials& credentials, + const char* region, const char* serviceName, bool signBody) const { Aws::String signingRegion = region ? region : m_region; Aws::String signingServiceName = serviceName ? serviceName : m_serviceName; - AWSCredentials credentials = GetCredentials(request.GetServiceSpecificParameters()); //don't sign anonymous requests if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) @@ -347,6 +350,12 @@ bool AWSAuthV4Signer::SignRequest(Aws::Http::HttpRequest& request, const char* r return true; } +bool AWSAuthV4Signer::SignRequest(Aws::Http::HttpRequest& request, const char* region, const char* serviceName, bool signBody) const +{ + AWSCredentials credentials = GetCredentials(request.GetServiceSpecificParameters()); + return SignRequestWithCreds(request, credentials, region, serviceName, signBody); +} + bool AWSAuthV4Signer::PresignRequest(Aws::Http::HttpRequest& request, long long expirationTimeInSeconds) const { return PresignRequest(request, m_region.c_str(), expirationTimeInSeconds); diff --git a/src/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp b/src/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp index a8ec94f921f..242db2d7978 100644 --- a/src/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp +++ b/src/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp @@ -26,6 +26,20 @@ AWS_CORE_API extern const char REQUEST_ID_HEADER[] = "x-amzn-RequestId"; AWS_CORE_API extern const char QUERY_ERROR_HEADER[] = "x-amzn-query-error"; AWS_CORE_API extern const char TYPE[] = "__type"; +static CoreErrors GuessBodylessErrorType(const Aws::Http::HttpResponseCode responseCode) +{ + switch (responseCode) + { + case HttpResponseCode::FORBIDDEN: + case HttpResponseCode::UNAUTHORIZED: + return CoreErrors::ACCESS_DENIED; + case HttpResponseCode::NOT_FOUND: + return CoreErrors::RESOURCE_NOT_FOUND; + default: + return CoreErrors::UNKNOWN; + } +} + AWSError JsonErrorMarshaller::Marshall(const Aws::Http::HttpResponse& httpResponse) const { Aws::StringStream memoryStream; @@ -86,6 +100,37 @@ AWSError JsonErrorMarshaller::Marshall(const Aws::Http::HttpResponse return error; } +AWSError JsonErrorMarshaller::BuildAWSError(const std::shared_ptr& httpResponse) const +{ + AWSError error; + if (httpResponse->HasClientError()) + { + bool retryable = httpResponse->GetClientErrorType() == CoreErrors::NETWORK_CONNECTION ? true : false; + error = AWSError(httpResponse->GetClientErrorType(), "", httpResponse->GetClientErrorMessage(), retryable); + } + else if (!httpResponse->GetResponseBody() || httpResponse->GetResponseBody().tellp() < 1) + { + auto responseCode = httpResponse->GetResponseCode(); + auto errorCode = GuessBodylessErrorType(responseCode); + + Aws::StringStream ss; + ss << "No response body."; + error = AWSError(errorCode, "", ss.str(), + IsRetryableHttpResponseCode(responseCode)); + } + else + { + assert(httpResponse->GetResponseCode() != HttpResponseCode::OK); + error = Marshall(*httpResponse); + } + + error.SetResponseHeaders(httpResponse->GetHeaders()); + error.SetResponseCode(httpResponse->GetResponseCode()); + error.SetRemoteHostIpAddress(httpResponse->GetOriginatingRequest().GetResolvedRemoteHost()); + AWS_LOGSTREAM_ERROR(AWS_ERROR_MARSHALLER_LOG_TAG, error); + return error; +} + const JsonValue& JsonErrorMarshaller::GetJsonPayloadFromError(const AWSError& error) const { return error.GetJsonPayload(); @@ -149,6 +194,43 @@ AWSError XmlErrorMarshaller::Marshall(const Aws::Http::HttpResponse& return error; } +AWSError XmlErrorMarshaller::BuildAWSError(const std::shared_ptr& httpResponse) const +{ + AWSError error; + if (httpResponse->HasClientError()) + { + bool retryable = httpResponse->GetClientErrorType() == CoreErrors::NETWORK_CONNECTION ? true : false; + error = AWSError(httpResponse->GetClientErrorType(), "", httpResponse->GetClientErrorMessage(), retryable); + } + else if (!httpResponse->GetResponseBody() || httpResponse->GetResponseBody().tellp() < 1) + { + auto responseCode = httpResponse->GetResponseCode(); + auto errorCode = GuessBodylessErrorType(responseCode); + + Aws::StringStream ss; + ss << "No response body."; + error = AWSError(errorCode, "", ss.str(), IsRetryableHttpResponseCode(responseCode)); + } + else + { + // When trying to build an AWS Error from a response which is an FStream, we need to rewind the + // file pointer back to the beginning in order to correctly read the input using the XML string iterator + if ((httpResponse->GetResponseBody().tellp() > 0) + && (httpResponse->GetResponseBody().tellg() > 0)) + { + httpResponse->GetResponseBody().seekg(0); + } + + error = Marshall(*httpResponse); + } + + error.SetResponseHeaders(httpResponse->GetHeaders()); + error.SetResponseCode(httpResponse->GetResponseCode()); + error.SetRemoteHostIpAddress(httpResponse->GetOriginatingRequest().GetResolvedRemoteHost()); + AWS_LOGSTREAM_ERROR(AWS_ERROR_MARSHALLER_LOG_TAG, error); + return error; +} + const XmlDocument& XmlErrorMarshaller::GetXmlPayloadFromError(const AWSError& error) const { return error.GetXmlPayload(); diff --git a/src/aws-cpp-sdk-core/source/smithy/client/AwsSmithyClientBase.cpp b/src/aws-cpp-sdk-core/source/smithy/client/AwsSmithyClientBase.cpp new file mode 100644 index 00000000000..237f46b8c37 --- /dev/null +++ b/src/aws-cpp-sdk-core/source/smithy/client/AwsSmithyClientBase.cpp @@ -0,0 +1,481 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include + +#include "aws/core/client/AWSErrorMarshaller.h" +#include "aws/core/client/RetryStrategy.h" +#include "aws/core/http/HttpClientFactory.h" +#include "aws/core/monitoring/CoreMetrics.h" +#include "aws/core/monitoring/MonitoringManager.h" +#include "aws/core/utils/DNS.h" +#include "aws/core/utils/threading/Executor.h" +#include "aws/core/utils/threading/SameThreadExecutor.h" +#include "smithy/tracing/TracingUtils.h" + +using namespace smithy::client; +using namespace smithy::components::tracing; + +static const char AWS_SMITHY_CLIENT_LOG[] = "AwsSmithyClient"; + + +void AddHeadersToRequest(const std::shared_ptr& httpRequest, + const Aws::Http::HeaderValueCollection& headerValues) +{ + for (auto const& headerValue : headerValues) + { + httpRequest->SetHeaderValue(headerValue.first, headerValue.second); + } +} + +std::shared_ptr +AwsSmithyClientBase::BuildHttpRequest(const std::shared_ptr& pRequestCtx, + const Aws::Http::URI& uri, + Aws::Http::HttpMethod method + ) const +{ + assert(pRequestCtx); + std::shared_ptr httpRequest; + const auto* pRequest = pRequestCtx->m_pRequest; + if(pRequest) { + httpRequest = CreateHttpRequest(uri, method, pRequest->GetResponseStreamFactory()); + } else { + httpRequest = CreateHttpRequest(uri, method, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod); + } + if (!httpRequest) + { + AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_LOG, "Failed to allocate an HttpRequest under a shared ptr"); + return httpRequest; + } + + httpRequest->SetUserAgent(m_userAgent); + httpRequest->SetHeaderValue(Aws::Http::SDK_INVOCATION_ID_HEADER, pRequestCtx->m_invocationId); + httpRequest->SetHeaderValue(Aws::Http::SDK_REQUEST_HEADER, pRequestCtx->m_requestInfo.ToString()); + RecursionDetection::AppendRecursionDetectionHeader(pRequestCtx->m_httpRequest); + + if (pRequest) + { + AddHeadersToRequest(httpRequest, pRequest->GetHeaders()); + AddHeadersToRequest(httpRequest, pRequest->GetAdditionalCustomHeaders()); + + if (pRequest->IsEventStreamRequest()) + { + httpRequest->SetEventStreamRequest(true); + httpRequest->AddContentBody(pRequest->GetBody()); + } + else + { + //Check if compression is required + Aws::Client::CompressionAlgorithm selectedCompressionAlgorithm = pRequest->GetSelectedCompressionAlgorithm(m_clientConfig.requestCompressionConfig); + if (Aws::Client::CompressionAlgorithm::NONE != selectedCompressionAlgorithm) { + RequestPayloadCompression::AddCompressedContentBodyToRequest(pRequest, httpRequest, selectedCompressionAlgorithm, m_httpClient); + } else { + Utils::AddContentBodyToRequest(httpRequest, pRequest->GetBody(), m_httpClient, + pRequest->ShouldComputeContentMd5(), pRequest->IsStreaming() && pRequest->IsChunked()); + } + } + + Checksums::AddChecksumToRequest(httpRequest, *pRequest); + // Pass along handlers for processing data sent/received in bytes + httpRequest->SetHeadersReceivedEventHandler(pRequest->GetHeadersReceivedEventHandler()); + httpRequest->SetDataReceivedEventHandler(pRequest->GetDataReceivedEventHandler()); + httpRequest->SetDataSentEventHandler(pRequest->GetDataSentEventHandler()); + httpRequest->SetContinueRequestHandle(pRequest->GetContinueRequestHandler()); + httpRequest->SetServiceSpecificParameters(pRequest->GetServiceSpecificParameters()); + + pRequest->AddQueryStringParameters(httpRequest->GetUri()); + } + + return httpRequest; +} + +void AwsSmithyClientBase::MakeRequestAsync(Aws::AmazonWebServiceRequest const* const request, + const char* requestName, + Aws::Http::HttpMethod method, + EndpointUpdateCallback&& endpointCallback, + ResponseHandlerFunc&& responseHandler, + std::shared_ptr pExecutor) const +{ + if(!responseHandler) + { + assert(!"Missing a mandatory response handler!"); + AWS_LOGSTREAM_FATAL(AWS_SMITHY_CLIENT_LOG, "Unable to continue AWSClient request: response handler is missing!"); + return; + } + + std::shared_ptr pRequestCtx = + Aws::MakeShared(AWS_SMITHY_CLIENT_LOG); + if (!pRequestCtx) + { + AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_LOG, "Failed to allocate an AwsSmithyClientAsyncRequestContext under a shared ptr"); + auto outcome = HttpResponseOutcome(ClientError(CoreErrors::MEMORY_ALLOCATION, "", "Failed to allocate async request context", false/*retryable*/)); + pExecutor->Submit([outcome, responseHandler]() mutable + { + responseHandler(std::move(outcome)); + } ); + return; + } + pRequestCtx->m_responseHandler = std::move(responseHandler); + pRequestCtx->m_pExecutor = pExecutor; + pRequestCtx->m_pRequest = request; + if (requestName) + pRequestCtx->m_requestName = requestName; + else if (pRequestCtx->m_pRequest) + pRequestCtx->m_requestName = pRequestCtx->m_pRequest->GetServiceRequestName(); + pRequestCtx->m_method = method; + pRequestCtx->m_retryCount = 0; + pRequestCtx->m_invocationId = Aws::Utils::UUID::PseudoRandomUUID(); + auto authSchemeOptionOutcome = this->SelectAuthSchemeOption(*pRequestCtx); + if (!authSchemeOptionOutcome.IsSuccess()) + { + pExecutor->Submit([authSchemeOptionOutcome, responseHandler]() mutable + { + responseHandler(std::move(authSchemeOptionOutcome)); + } ); + return; + } + pRequestCtx->m_authSchemeOption = std::move(authSchemeOptionOutcome.GetResultWithOwnership()); + assert(pRequestCtx->m_authSchemeOption.schemeId); + Aws::Endpoint::EndpointParameters epParams = request ? request->GetEndpointContextParams() : Aws::Endpoint::EndpointParameters(); + epParams.insert(epParams.end(), pRequestCtx->m_authSchemeOption.endpointParameters.begin(), pRequestCtx->m_authSchemeOption.endpointParameters.end()); + auto epResolutionOutcome = this->ResolveEndpoint(std::move(epParams), std::move(endpointCallback)); + if (!epResolutionOutcome.IsSuccess()) + { + pExecutor->Submit([epResolutionOutcome, responseHandler]() mutable + { + responseHandler(std::move(epResolutionOutcome)); + } ); + return; + } + pRequestCtx->m_endpoint = std::move(epResolutionOutcome.GetResultWithOwnership()); + if (!Aws::Utils::IsValidHost(pRequestCtx->m_endpoint.GetURI().GetAuthority())) + { + AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_LOG, "Invalid DNS Label found in URI host"); + auto outcome = HttpResponseOutcome(ClientError(CoreErrors::VALIDATION, "", "Invalid DNS Label found in URI host", false/*retryable*/)); + pExecutor->Submit([outcome, responseHandler]() mutable + { + responseHandler(std::move(outcome)); + } ); + return; + } + pRequestCtx->m_requestInfo.attempt = 1; + pRequestCtx->m_requestInfo.maxAttempts = 0; + + AttemptOneRequestAsync(std::move(pRequestCtx)); +} + +/*HttpResponseOutcome*/ +void AwsSmithyClientBase::AttemptOneRequestAsync(std::shared_ptr pRequestCtx) const +{ + if(!pRequestCtx) + { + assert(!"Missing pRequestCtx"); + AWS_LOGSTREAM_FATAL(AWS_SMITHY_CLIENT_LOG, "Missing request context!"); + } + auto& responseHandler = pRequestCtx->m_responseHandler; + auto pExecutor = pRequestCtx->m_pExecutor; + + TracingUtils::MakeCallWithTiming( + [&]() -> void { + pRequestCtx->m_httpRequest = BuildHttpRequest(pRequestCtx, pRequestCtx->m_endpoint.GetURI(), pRequestCtx->m_method); + }, + TracingUtils::SMITHY_CLIENT_SERIALIZATION_METRIC, + *m_clientConfig.telemetryProvider->getMeter(this->GetServiceClientName(), {}), + {{TracingUtils::SMITHY_METHOD_DIMENSION, pRequestCtx->m_requestName}, + {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}}); + + if (!pRequestCtx->m_httpRequest) + { + AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_LOG, "Failed to BuildHttpRequest"); + auto outcome = HttpResponseOutcome(ClientError(CoreErrors::VALIDATION, "", "Unable to create HttpRequest object", false/*retryable*/)); + pExecutor->Submit([outcome, responseHandler]() mutable + { + responseHandler(std::move(outcome)); + } ); + return; + } + + Aws::Monitoring::CoreMetricsCollection coreMetrics; + pRequestCtx->m_monitoringContexts = Aws::Monitoring::OnRequestStarted(this->GetServiceClientName(), + pRequestCtx->m_requestName, + pRequestCtx->m_httpRequest); + + if(m_clientConfig.retryStrategy && !m_clientConfig.retryStrategy->HasSendToken()) + { + auto errOutcome = HttpResponseOutcome(ClientError(CoreErrors::SLOW_DOWN, + "", + "Unable to acquire enough send tokens to execute request.", + false/*retryable*/)); + pExecutor->Submit([errOutcome, responseHandler]() mutable + { + responseHandler(std::move(errOutcome)); + } ); + return; + }; + + SigningOutcome signingOutcome = TracingUtils::MakeCallWithTiming([&]() -> SigningOutcome { + return this->SignRequest(pRequestCtx->m_httpRequest, pRequestCtx->m_authSchemeOption); + }, + TracingUtils::SMITHY_CLIENT_SIGNING_METRIC, + *m_clientConfig.telemetryProvider->getMeter(this->GetServiceClientName(), {}), + {{TracingUtils::SMITHY_METHOD_DIMENSION, pRequestCtx->m_requestName}, + {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}}); + + if (!signingOutcome.IsSuccess()) + { + AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_LOG, "Request signing failed. Returning error."); + auto errOutcome = HttpResponseOutcome(ClientError(CoreErrors::CLIENT_SIGNING_FAILURE, "", "SDK failed to sign the request", false/*retryable*/)); + pRequestCtx->m_pExecutor->Submit([errOutcome, pRequestCtx]() mutable + { + pRequestCtx->m_responseHandler(std::move(errOutcome)); + } ); + return; + } + + std::shared_ptr signedHttpRequest = signingOutcome.GetResultWithOwnership(); + assert(signedHttpRequest); + + if (pRequestCtx->m_pRequest && pRequestCtx->m_pRequest->GetRequestSignedHandler()) + { + pRequestCtx->m_pRequest->GetRequestSignedHandler()(*signedHttpRequest); + } + + // handler for a single http reply (vs final AWS response handler) + auto httpResponseHandler = [this, pRequestCtx](std::shared_ptr pResponse) mutable + { + HandleAsyncReply(std::move(pRequestCtx), std::move(pResponse)); + }; + + // TODO: async http client +#if 0 + AWS_LOGSTREAM_DEBUG(AWS_SMITHY_CLIENT_LOG, "Request Successfully signed"); + TracingUtils::MakeCallWithTiming( + [&]() -> void { + m_httpClient->MakeAsyncRequest(pRequestCtx->m_httpRequest, + pRequestCtx->m_pExecutor, + responseHandler, + m_clientConfig.readRateLimiter.get(), + m_clientConfig.writeRateLimiter.get()); + }, + TracingUtils::SMITHY_CLIENT_SERVICE_CALL_METRIC, + *m_clientConfig.telemetryProvider->getMeter(this->GetServiceClientName(), {}), + {{TracingUtils::SMITHY_METHOD_DIMENSION, pRequestCtx->m_requestName}, + {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}}); +#else + auto httpResponse = TracingUtils::MakeCallWithTiming>( + [&]() -> std::shared_ptr { + return m_httpClient->MakeRequest(signedHttpRequest, m_clientConfig.readRateLimiter.get(), m_clientConfig.writeRateLimiter.get()); + }, + TracingUtils::SMITHY_CLIENT_SERVICE_CALL_METRIC, + *m_clientConfig.telemetryProvider->getMeter(this->GetServiceClientName(), {}), + {{TracingUtils::SMITHY_METHOD_DIMENSION, pRequestCtx->m_requestName},{TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}}); + + pRequestCtx->m_pExecutor->Submit([httpResponse, httpResponseHandler]() mutable + { + httpResponseHandler(std::move(httpResponse)); + } ); +#endif + return; +} + +void AwsSmithyClientBase::HandleAsyncReply(std::shared_ptr pRequestCtx, + std::shared_ptr httpResponse) const +{ + assert(pRequestCtx && httpResponse); + + if (pRequestCtx->m_pRequest && pRequestCtx->m_pRequest->ShouldValidateResponseChecksum()) + { + auto checksumError = Checksums::ValidateResponseChecksum(pRequestCtx.get(), httpResponse.get()); + if (checksumError) + { + return pRequestCtx->m_responseHandler(HttpResponseOutcome(std::move(*checksumError))); + } + } + + bool hasEmbeddedError = pRequestCtx->m_pRequest && + pRequestCtx->m_pRequest->HasEmbeddedError(httpResponse->GetResponseBody(), httpResponse->GetHeaders()); + + if (Utils::DoesResponseGenerateError(httpResponse) || hasEmbeddedError) + { + AWS_LOGSTREAM_DEBUG(AWS_SMITHY_CLIENT_LOG, "Request returned error. Attempting to generate appropriate error codes from response"); + assert(m_errorMarshaller); + auto error = m_errorMarshaller->BuildAWSError(httpResponse); + return pRequestCtx->m_responseHandler(HttpResponseOutcome(std::move(error))); + } + + AWS_LOGSTREAM_DEBUG(AWS_SMITHY_CLIENT_LOG, "Request returned successful response."); + + Aws::Client::HttpResponseOutcome outcome = HttpResponseOutcome(std::move(httpResponse)); + + Aws::Monitoring::CoreMetricsCollection coreMetrics; + + do // goto in a form of "do { break; } while(0);" // TODO: refactor + { + if (pRequestCtx->m_retryCount == 0) + { + m_clientConfig.retryStrategy->RequestBookkeeping(outcome); + } + else + { + assert(pRequestCtx->m_lastError); + m_clientConfig.retryStrategy->RequestBookkeeping(outcome, pRequestCtx->m_lastError.value()); + } + coreMetrics.httpClientMetrics = pRequestCtx->m_httpRequest->GetRequestMetrics(); + TracingUtils::EmitCoreHttpMetrics(pRequestCtx->m_httpRequest->GetRequestMetrics(), + *m_clientConfig.telemetryProvider->getMeter(this->GetServiceClientName(), {}), + {{TracingUtils::SMITHY_METHOD_DIMENSION, pRequestCtx->m_requestName}, + {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}}); + if (outcome.IsSuccess()) + { + Aws::Monitoring::OnRequestSucceeded(this->GetServiceClientName(), + pRequestCtx->m_requestName, + pRequestCtx->m_httpRequest, + outcome, + coreMetrics, + pRequestCtx->m_monitoringContexts); + AWS_LOGSTREAM_TRACE(AWS_SMITHY_CLIENT_LOG, "Request successful returning."); + break; + } + pRequestCtx->m_lastError = outcome.GetError(); + + Utils::DateTime serverTime = Utils::GetServerTimeFromError(outcome.GetError()); + auto clockSkew = Utils::DateTime::Diff(serverTime, Utils::DateTime::Now()); + + Aws::Monitoring::OnRequestFailed(this->GetServiceClientName(), + pRequestCtx->m_requestName, + pRequestCtx->m_httpRequest, + outcome, + coreMetrics, + pRequestCtx->m_monitoringContexts); + + if (!m_httpClient->IsRequestProcessingEnabled()) + { + AWS_LOGSTREAM_TRACE(AWS_SMITHY_CLIENT_LOG, "Request was cancelled externally."); + break; + } + + // Adjust region + // TODO: extract into common func + bool retryWithCorrectRegion = [&]() { + using HttpResponseCode = Aws::Http::HttpResponseCode; + const HttpResponseCode httpResponseCode = outcome.GetError().GetResponseCode(); + if (httpResponseCode == HttpResponseCode::MOVED_PERMANENTLY || // 301 + httpResponseCode == HttpResponseCode::TEMPORARY_REDIRECT || // 307 + httpResponseCode == HttpResponseCode::BAD_REQUEST || // 400 + httpResponseCode == HttpResponseCode::FORBIDDEN) // 403 + { + assert(m_errorMarshaller); + const Aws::String regionFromResponse = m_errorMarshaller->ExtractRegion(outcome.GetError()); + const Aws::String& signerRegion = pRequestCtx->m_endpoint.GetAttributes()->authScheme.GetSigningRegion() ? + pRequestCtx->m_endpoint.GetAttributes()->authScheme.GetSigningRegion().value() : ""; + if (m_clientConfig.region == Aws::Region::AWS_GLOBAL && !regionFromResponse.empty() && + regionFromResponse != signerRegion) { + pRequestCtx->m_endpoint.AccessAttributes()->authScheme.SetSigningRegion(regionFromResponse); + AWS_LOGSTREAM_DEBUG(AWS_SMITHY_CLIENT_LOG, "Need to retry with a correct region"); + return true; + } + } + return false; + } (); // <- immediately invoked lambda + + long sleepMillis = TracingUtils::MakeCallWithTiming( + [&]() -> long { + return m_clientConfig.retryStrategy->CalculateDelayBeforeNextRetry(outcome.GetError(), pRequestCtx->m_retryCount); + }, + TracingUtils::SMITHY_CLIENT_SERVICE_BACKOFF_DELAY_METRIC, + *m_clientConfig.telemetryProvider->getMeter(this->GetServiceClientName(), {}), + {{TracingUtils::SMITHY_METHOD_DIMENSION, pRequestCtx->m_requestName}, + {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}}); + bool shouldSleep = !retryWithCorrectRegion; + if (m_clientConfig.enableClockSkewAdjustment) + { + // AdjustClockSkew returns true means clock skew was the problem and skew was adjusted, false otherwise. + // sleep if clock skew and region was NOT the problem. AdjustClockSkew may update error inside outcome. + shouldSleep |= !this->AdjustClockSkew(outcome, pRequestCtx->m_authSchemeOption); + } + + if (!retryWithCorrectRegion && !m_clientConfig.retryStrategy->ShouldRetry(outcome.GetError(), pRequestCtx->m_retryCount)) + { + break; + } + + AWS_LOGSTREAM_WARN(AWS_SMITHY_CLIENT_LOG, "Request failed, now waiting " << sleepMillis << " ms before attempting again."); + + if(pRequestCtx->m_pRequest) { + if (pRequestCtx->m_pRequest->GetBody()) { + pRequestCtx->m_pRequest->GetBody()->clear(); + pRequestCtx->m_pRequest->GetBody()->seekg(0); + } + + if (pRequestCtx->m_pRequest->GetRequestRetryHandler()) { + pRequestCtx->m_pRequest->GetRequestRetryHandler()(*pRequestCtx->m_pRequest); + } + } + + if (shouldSleep) + { + m_httpClient->RetryRequestSleep(std::chrono::milliseconds(sleepMillis)); + } + + if (retryWithCorrectRegion) + { + Aws::String newEndpoint = m_errorMarshaller->ExtractEndpoint(outcome.GetError()); + if (newEndpoint.empty()) { + Aws::Http::URI newUri = pRequestCtx->m_endpoint.GetURI(); + newUri.SetAuthority(newEndpoint); + pRequestCtx->m_endpoint.SetURI(newUri); + AWS_LOGSTREAM_DEBUG(AWS_SMITHY_CLIENT_LOG, "Endpoint has been updated to " << newUri.GetURIString()); + } + } + if (serverTime.WasParseSuccessful() && serverTime != Utils::DateTime()) + { + pRequestCtx->m_requestInfo.ttl = Utils::DateTime::Now() + clockSkew + std::chrono::milliseconds(m_clientConfig.requestTimeoutMs); + } + pRequestCtx->m_requestInfo.attempt ++; + pRequestCtx->m_requestInfo.maxAttempts = m_clientConfig.retryStrategy->GetMaxAttempts(); + + Aws::Monitoring::OnRequestRetry(this->GetServiceClientName(), pRequestCtx->m_requestName, pRequestCtx->m_httpRequest, pRequestCtx->m_monitoringContexts); + + pRequestCtx->m_retryCount++; + AttemptOneRequestAsync(std::move(pRequestCtx)); + return; + } while(false); // end of goto in a form of "do { break; } while(false);" + + auto meter = m_clientConfig.telemetryProvider->getMeter(this->GetServiceClientName(), {}); + auto counter = meter->CreateCounter(TracingUtils::SMITHY_CLIENT_SERVICE_ATTEMPTS_METRIC, TracingUtils::COUNT_METRIC_TYPE, ""); + counter->add(pRequestCtx->m_requestInfo.attempt, {{TracingUtils::SMITHY_METHOD_DIMENSION, pRequestCtx->m_requestName}, + {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}}); + Aws::Monitoring::OnFinish(this->GetServiceClientName(), pRequestCtx->m_requestName, pRequestCtx->m_httpRequest, pRequestCtx->m_monitoringContexts); + + return pRequestCtx->m_responseHandler(std::move(outcome)); +} + +AwsSmithyClientBase::HttpResponseOutcome +AwsSmithyClientBase::MakeRequestSync(Aws::AmazonWebServiceRequest const * const request, + const char* requestName, + Aws::Http::HttpMethod method, + EndpointUpdateCallback&& endpointCallback) const +{ + std::shared_ptr pExecutor = Aws::MakeShared(AWS_SMITHY_CLIENT_LOG); + assert(pExecutor); + + HttpResponseOutcome outcome = ClientError(CoreErrors::INTERNAL_FAILURE, "", "Response handler was not called", false); + ResponseHandlerFunc responseHandler = [&outcome](HttpResponseOutcome&& asyncOutcome) + { + outcome = std::move(asyncOutcome); + }; + + pExecutor->Submit([&]() + { + this->MakeRequestAsync(request, requestName, method, std::move(endpointCallback), std::move(responseHandler), pExecutor); + }); + pExecutor->WaitUntilStopped(); + + return outcome; +} \ No newline at end of file diff --git a/src/aws-cpp-sdk-core/source/utils/threading/SameThreadExecutor.cpp b/src/aws-cpp-sdk-core/source/utils/threading/SameThreadExecutor.cpp new file mode 100644 index 00000000000..5a96fa45d64 --- /dev/null +++ b/src/aws-cpp-sdk-core/source/utils/threading/SameThreadExecutor.cpp @@ -0,0 +1,35 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include + +#include + +using namespace Aws::Utils::Threading; + +bool SameThreadExecutor::SubmitToThread(std::function&& task) +{ + m_tasks.push_back(std::move(task)); + return true; +} + +SameThreadExecutor::~SameThreadExecutor() +{ + SameThreadExecutor::WaitUntilStopped(); +} + +void SameThreadExecutor::WaitUntilStopped() +{ + while(!m_tasks.empty()) + { + auto task = std::move(m_tasks.front()); + m_tasks.pop_front(); + assert(task); + if(task) { + task(); + } + } +} \ No newline at end of file diff --git a/tests/aws-cpp-sdk-core-tests/CMakeLists.txt b/tests/aws-cpp-sdk-core-tests/CMakeLists.txt index 1918e2ee68e..9b04acb5487 100644 --- a/tests/aws-cpp-sdk-core-tests/CMakeLists.txt +++ b/tests/aws-cpp-sdk-core-tests/CMakeLists.txt @@ -24,6 +24,7 @@ file(GLOB UTILS_MEMORY_SRC "${CMAKE_CURRENT_SOURCE_DIR}/utils/memory/*.cpp") file(GLOB UTILS_COMPONENT_REGISTRY_SRC "${CMAKE_CURRENT_SOURCE_DIR}/utils/component-registry/*.cpp") file(GLOB MONITORING_SRC "${CMAKE_CURRENT_SOURCE_DIR}/monitoring/*.cpp") file(GLOB SMITHY_TRACING_SRC "${CMAKE_CURRENT_SOURCE_DIR}/smithy/tracing/*.cpp") +file(GLOB SMITHY_CLIENT_SRC "${CMAKE_CURRENT_SOURCE_DIR}/smithy/client/*.cpp") file(GLOB AWS_CPP_SDK_CORE_TESTS_SRC @@ -48,6 +49,7 @@ file(GLOB AWS_CPP_SDK_CORE_TESTS_SRC ${UTILS_LOGGING_SRC} ${UTILS_RATE_LIMITER_SRC} ${SMITHY_TRACING_SRC} + ${SMITHY_CLIENT_SRC} ) if(PLATFORM_WINDOWS) @@ -71,6 +73,7 @@ if(PLATFORM_WINDOWS) source_group("Source Files\\utils\\component-registry" FILES ${UTILS_COMPONENT_REGISTRY_SRC}) source_group("Source Files\\utils\\threading" FILES ${UTILS_THREADING_SRC}) source_group("Source Files\\smithy\\tracing" FILES ${SMITHY_TRACING_SRC}) + source_group("Source Files\\smithy\\client" FILES ${SMITHY_CLIENT_SRC}) endif() endif() diff --git a/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp b/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp new file mode 100644 index 00000000000..621d3960c4d --- /dev/null +++ b/tests/aws-cpp-sdk-core-tests/smithy/client/SmithyClientTest.cpp @@ -0,0 +1,34 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include +#include +#include +#include + + +#include + +#include "aws/core/endpoint/EndpointProviderBase.h" + + +class SmithyClientTest : public Aws::Testing::AwsCppSdkGTestSuite { +}; + +class MyTestEndpointProvider : public Aws::Endpoint::EndpointProviderBase<> +{ +}; + +static constexpr char MyServiceName[] = "MySuperService"; + +TEST_F(SmithyClientTest, TestCompiles) { + + using MySmithyClientConfig = Aws::Client::ClientConfiguration; + using MyServiceAuthSchemeResolver = smithy::SigV4AuthSchemeResolver<>; + + using MySmithyClient = smithy::client::AwsSmithyClientT; + + MySmithyClient* ptr = nullptr; + AWS_UNREFERENCED_PARAM(ptr); +} diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ExpressIdentityHeader.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ExpressIdentityHeader.vm index 07c9dc35673..039e4c6756c 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ExpressIdentityHeader.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ExpressIdentityHeader.vm @@ -40,22 +40,22 @@ namespace ${rootNamespace} { return m_expiration; } - String accessKeyId() override + String accessKeyId() const override { return getAccessKeyId(); } - String secretAccessKey() override + String secretAccessKey() const override { return getSecretKeyId(); } - Crt::Optional sessionToken() override + Crt::Optional sessionToken() const override { return getSessionToken(); } - Crt::Optional expiration() override + Crt::Optional expiration() const override { return getExpiration(); } diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ExpressIdentityProviderSource.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ExpressIdentityProviderSource.vm index 7c6957a483a..9cdec7bff68 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ExpressIdentityProviderSource.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ExpressIdentityProviderSource.vm @@ -43,8 +43,8 @@ S3ExpressIdentityProvider::getIdentity( return {}; }); } - const auto identity = GetS3ExpressIdentity(params); - return identity; + auto identity = GetS3ExpressIdentity(params); + return Aws::MakeUnique(S3_EXPRESS_IDENTITY_PROVIDER, std::move(identity)); } S3ExpressIdentity S3ExpressIdentityProvider::getIdentity(const Aws::String &bucketName) {