Skip to content

Commit

Permalink
Fix smithy client copy assignment (#3327)
Browse files Browse the repository at this point in the history
  • Loading branch information
SergeyRyabinin authored Mar 5, 2025
1 parent 3d0cdc2 commit bd1a11b
Show file tree
Hide file tree
Showing 10 changed files with 504 additions and 77 deletions.
2 changes: 1 addition & 1 deletion cmake/sdksCommon.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ list(APPEND SDK_TEST_PROJECT_LIST "elasticfilesystem:tests/aws-cpp-sdk-elasticfi
list(APPEND SDK_TEST_PROJECT_LIST "identity-management:tests/aws-cpp-sdk-identity-management-tests")
list(APPEND SDK_TEST_PROJECT_LIST "kinesis:tests/aws-cpp-sdk-kinesis-integration-tests")
list(APPEND SDK_TEST_PROJECT_LIST "lambda:tests/aws-cpp-sdk-lambda-integration-tests")
list(APPEND SDK_TEST_PROJECT_LIST "logs:tests/aws-cpp-sdk-logs-integration-tests")
list(APPEND SDK_TEST_PROJECT_LIST "logs:tests/aws-cpp-sdk-logs-integration-tests,tests/aws-cpp-sdk-logs-unit-tests")
list(APPEND SDK_TEST_PROJECT_LIST "mediastore-data:tests/aws-cpp-sdk-mediastore-data-integration-tests")
list(APPEND SDK_TEST_PROJECT_LIST "monitoring:tests/aws-cpp-sdk-monitoring-integration-tests")
list(APPEND SDK_TEST_PROJECT_LIST "rds:tests/aws-cpp-sdk-rds-integration-tests")
Expand Down
79 changes: 53 additions & 26 deletions src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,55 +60,78 @@ namespace client
m_authSchemes(authSchemes),
m_serializer(Aws::MakeShared<SerializerT>(ServiceNameT, m_clientConfiguration.telemetryProvider))
{
m_serviceName = ServiceNameT;
initClient();
}

AwsSmithyClientT(const AwsSmithyClientT& other):
AwsSmithyClientBase(other,
Aws::MakeUnique<ServiceClientConfigurationT>(ServiceNameT, other.m_clientConfiguration),
ServiceNameT,
other.m_serviceUserAgentName,
Aws::Http::CreateHttpClient(other.m_clientConfiguration),
Aws::MakeShared<ErrorMarshallerT>(ServiceNameT)),
m_clientConfiguration{*static_cast<ServiceClientConfigurationT*>(m_clientConfig.get())},
m_endpointProvider{other.m_endpointProvider},
m_authSchemeResolver{Aws::MakeShared<ServiceAuthSchemeResolverT>(ServiceNameT)},
m_authSchemes{other.m_authSchemes},
m_serializer{Aws::MakeShared<SerializerT>(ServiceNameT, m_clientConfiguration.telemetryProvider)}
AwsSmithyClientT(const AwsSmithyClientT& other)
: AwsSmithyClientBase(other,
Aws::MakeUnique<ServiceClientConfigurationT>(ServiceNameT, other.m_clientConfiguration),
Aws::Http::CreateHttpClient(other.m_clientConfiguration),
Aws::MakeShared<ErrorMarshallerT>(ServiceNameT)),
m_clientConfiguration(*static_cast<ServiceClientConfigurationT*>(m_clientConfig.get()))
{
initClient();
m_endpointProvider = other.m_endpointProvider; /* shallow copy */
m_authSchemeResolver = other.m_authSchemeResolver; /* shallow copy */
m_authSchemes = other.m_authSchemes;
m_serializer = Aws::MakeShared<SerializerT>(ServiceNameT, m_clientConfiguration.telemetryProvider);
initClient();
}

AwsSmithyClientT& operator=(const AwsSmithyClientT& other)
{
if(this != &other)
{
AwsSmithyClientBase::deepCopy(Aws::MakeUnique<ServiceClientConfigurationT>(ServiceNameT, other.m_clientConfiguration),
ServiceNameT,
Aws::Http::CreateHttpClient(other.m_clientConfiguration),
m_clientConfiguration = other.m_clientConfiguration;
AwsSmithyClientBase::baseCopyAssign(other,
Aws::Http::CreateHttpClient(m_clientConfiguration),
Aws::MakeShared<ErrorMarshallerT>(ServiceNameT));
m_clientConfiguration = *static_cast<ServiceClientConfigurationT*>(m_clientConfig.get());
m_endpointProvider = other.m_endpointProvider;
m_authSchemeResolver = Aws::MakeShared<ServiceAuthSchemeResolverT>(ServiceNameT);

m_endpointProvider = other.m_endpointProvider; /* shallow copy */
m_authSchemeResolver = other.m_authSchemeResolver; /* shallow copy */
m_authSchemes = other.m_authSchemes;
m_serializer = Aws::MakeShared<SerializerT>(ServiceNameT, m_clientConfiguration.telemetryProvider);
m_errorMarshaller = Aws::MakeShared<ErrorMarshallerT>(ServiceNameT);
initClient();
}
return *this;
}

AwsSmithyClientT (AwsSmithyClientT&&) = default;
AwsSmithyClientT(AwsSmithyClientT&& other) :
AwsSmithyClientBase(std::move(static_cast<AwsSmithyClientBase&&>(other)),
Aws::MakeUnique<ServiceClientConfigurationT>(ServiceNameT, std::move(other.m_clientConfiguration))),
m_clientConfiguration{*static_cast<ServiceClientConfigurationT*>(m_clientConfig.get())},
m_endpointProvider(std::move(other.m_endpointProvider)),
m_authSchemeResolver(std::move(other.m_authSchemeResolver)),
m_authSchemes(std::move(other.m_authSchemes)),
m_serializer(std::move(other.m_serializer))
{
}

AwsSmithyClientT& operator=(AwsSmithyClientT&&) = default;
AwsSmithyClientT& operator=(AwsSmithyClientT&& other)
{
if(this != &other)
{
m_clientConfiguration = std::move(other.m_clientConfiguration);
AwsSmithyClientBase::baseMoveAssign(std::move(static_cast<AwsSmithyClientBase&&>(other)));

m_endpointProvider = std::move(other.m_endpointProvider);
m_authSchemeResolver = std::move(other.m_authSchemeResolver);
m_authSchemes = std::move(other.m_authSchemes);
m_serializer = std::move(other.m_serializer);
}
return *this;
}

virtual ~AwsSmithyClientT() = default;

protected:
void initClient() {
m_endpointProvider->InitBuiltInParameters(m_clientConfiguration);
m_authSchemeResolver->Init(m_clientConfiguration);
if (m_endpointProvider && m_authSchemeResolver) {
m_endpointProvider->InitBuiltInParameters(m_clientConfiguration);
m_authSchemeResolver->Init(m_clientConfiguration);
} else {
AWS_LOGSTREAM_FATAL(ServiceNameT, "Unable to init client: endpoint provider=" << m_endpointProvider
<< " or " << "authSchemeResolver=" << m_authSchemeResolver << " are null!");
}
}

inline const char* GetServiceClientName() const override { return m_serviceName.c_str(); }
Expand Down Expand Up @@ -226,7 +249,7 @@ namespace client
const Aws::Http::URI& uri = endpoint.GetURI();
auto signerRegionOverride = region;
auto signerServiceNameOverride = serviceName;
//signer name is needed for some identity resolvers
// signer name is needed for some identity resolvers
if (endpoint.GetAttributes()) {
if (endpoint.GetAttributes()->authScheme.GetSigningRegion()) {
signerRegionOverride = endpoint.GetAttributes()->authScheme.GetSigningRegion()->c_str();
Expand All @@ -242,6 +265,10 @@ namespace client
}

protected:
/* Service client specific config, the actual object is stored in AwsSmithyClientBase by pointer
* In order to avoid config object duplication, smithy template client access it by a reference.
* So that base client has it by base config pointer, child smithy client has it by child config reference.
*/
ServiceClientConfigurationT& m_clientConfiguration;
std::shared_ptr<EndpointProviderT> m_endpointProvider{};
std::shared_ptr<ServiceAuthSchemeResolverT> m_authSchemeResolver{};
Expand Down
59 changes: 33 additions & 26 deletions src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ namespace client
using ResolveEndpointOutcome = Aws::Utils::Outcome<Aws::Endpoint::AWSEndpoint, AWSError>;
using StreamOutcome = Aws::Utils::Outcome<Aws::AmazonWebServiceResult<Aws::Utils::Stream::ResponseStream>, AWSError >;

/* primary constructor */
AwsSmithyClientBase(Aws::UniquePtr<Aws::Client::ClientConfiguration>&& clientConfig,
Aws::String serviceName,
Aws::String serviceUserAgentName,
Expand All @@ -100,27 +101,31 @@ namespace client
baseInit();
}

/* copy constructor substitute */
AwsSmithyClientBase(const AwsSmithyClientBase& other,
Aws::UniquePtr<Aws::Client::ClientConfiguration>&& clientConfig,
Aws::String serviceName,
Aws::String serviceUserAgentName,
std::shared_ptr<Aws::Http::HttpClient> httpClient,
std::shared_ptr<Aws::Client::AWSErrorMarshaller> errorMarshaller) :
m_clientConfig(std::move(clientConfig)),
m_serviceName(std::move(serviceName)),
m_serviceUserAgentName(std::move(serviceUserAgentName)),
m_httpClient(std::move(httpClient)),
m_errorMarshaller(std::move(errorMarshaller)),
m_interceptors{Aws::MakeShared<ChecksumInterceptor>("AwsSmithyClientBase", *m_clientConfig)}
m_clientConfig(std::move(clientConfig))
{
// this c-tor needs httpClient and errorMarshaller passed explicitly because this base class stores them
// by their parent pointer classes
// and base client class has no idea how to re-create or copy them, and "lombok toBuilder" is not a thing in cpp.
baseCopyAssign(other, std::move(httpClient), std::move(errorMarshaller));
}

/* move constructor substitute */
AwsSmithyClientBase(AwsSmithyClientBase&& other,
Aws::UniquePtr<Aws::Client::ClientConfiguration>&& clientConfig) :
m_clientConfig(std::move(clientConfig))
{
AWS_UNREFERENCED_PARAM(other);
baseCopyInit();
baseMoveAssign(std::move(other));
}

AwsSmithyClientBase(AwsSmithyClientBase& target) = delete;
AwsSmithyClientBase& operator=(AwsSmithyClientBase& target) = delete;
AwsSmithyClientBase(AwsSmithyClientBase&& target) = default;
AwsSmithyClientBase& operator=(AwsSmithyClientBase&& target) = default;
AwsSmithyClientBase(AwsSmithyClientBase&& target) = delete;
AwsSmithyClientBase& operator=(AwsSmithyClientBase&& target) = delete;

virtual ~AwsSmithyClientBase() = default;

Expand All @@ -144,19 +149,6 @@ namespace client
void AppendToUserAgent(const Aws::String& valueToAppend);

protected:
void deepCopy(Aws::UniquePtr<Aws::Client::ClientConfiguration>&& clientConfig,
const Aws::String& serviceName,
std::shared_ptr<Aws::Http::HttpClient> httpClient,
std::shared_ptr<Aws::Client::AWSErrorMarshaller> errorMarshaller)
{
m_clientConfig = std::move(clientConfig);
m_serviceName = serviceName;
m_httpClient = std::move(httpClient);
m_errorMarshaller = std::move(errorMarshaller);
m_interceptors = Aws::Vector<std::shared_ptr<interceptor::Interceptor>>{Aws::MakeShared<ChecksumInterceptor>("AwsSmithyClientBase")};
baseCopyInit();
}

/**
* Initialize client configuration with their factory method, unless the user has explicitly set the
* configuration, and it is to be shallow copied between different clients, in which case, delete the
Expand All @@ -170,6 +162,18 @@ namespace client
*/
void baseCopyInit();

/**
* A helper utility to re-initialize on copy assignment
*/
void baseCopyAssign(const AwsSmithyClientBase& other,
std::shared_ptr<Aws::Http::HttpClient> httpClient,
std::shared_ptr<Aws::Client::AWSErrorMarshaller> errorMarshaller);

/**
* A helper utility to move assign base client
*/
void baseMoveAssign(AwsSmithyClientBase&& other);

/**
* Transforms the AmazonWebServicesResult object into an HttpRequest.
*/
Expand All @@ -191,7 +195,10 @@ namespace client
virtual SigningOutcome SignHttpRequest(std::shared_ptr<HttpRequest> httpRequest, const AuthSchemeOption& targetAuthSchemeOption) const = 0;
virtual bool AdjustClockSkew(HttpResponseOutcome& outcome, const AuthSchemeOption& authSchemeOption) const = 0;

std::shared_ptr<Aws::Client::ClientConfiguration> m_clientConfig;
/* AwsSmithyClientT class binds its config reference to this pointer, so don't remove const and don't re-allocate it.
* This is done to avoid duplication of config object between this base and actual service template classes.
*/
const Aws::UniquePtr<Aws::Client::ClientConfiguration> m_clientConfig;
Aws::String m_serviceName;
Aws::String m_serviceUserAgentName;

Expand Down
47 changes: 36 additions & 11 deletions src/aws-cpp-sdk-core/source/smithy/client/AwsSmithyClientBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,22 @@
#include <smithy/client/AwsSmithyClientAsyncRequestContext.h>
#include <smithy/client/features/RecursionDetection.h>
#include <smithy/client/features/RequestPayloadCompression.h>
#include <smithy/identity/signer/built-in/SignerProperties.h>
#include <smithy/tracing/TracingUtils.h>

#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"
#include <aws/core/client/AWSErrorMarshaller.h>
#include <aws/core/client/CoreErrors.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/logging/ErrorMacros.h>
#include <aws/core/utils/stream/ResponseStream.h>
#include <aws/core/utils/threading/Executor.h>
#include <aws/core/utils/threading/SameThreadExecutor.h>
#include <aws/crt/Variant.h>
#include <aws/core/client/CoreErrors.h>
#include <smithy/identity/signer/built-in/SignerProperties.h>


using namespace smithy::client;
using namespace smithy::interceptor;
Expand Down Expand Up @@ -62,6 +64,7 @@ void createFromFactoriesIfPresent(T& entity, std::function<T()>& factory) {
} // namespace

void AwsSmithyClientBase::baseInit() {
AWS_CHECK_PTR(AWS_SMITHY_CLIENT_LOG, m_clientConfig);
createFromFactories(m_clientConfig->retryStrategy, m_clientConfig->configFactories.retryStrategyCreateFn);
createFromFactories(m_clientConfig->executor, m_clientConfig->configFactories.executorCreateFn);
createFromFactories(m_clientConfig->writeRateLimiter, m_clientConfig->configFactories.writeRateLimiterCreateFn);
Expand All @@ -77,6 +80,7 @@ void AwsSmithyClientBase::baseInit() {
}

void AwsSmithyClientBase::baseCopyInit() {
AWS_CHECK_PTR(AWS_SMITHY_CLIENT_LOG, m_clientConfig);
createFromFactoriesIfPresent(m_clientConfig->retryStrategy, m_clientConfig->configFactories.retryStrategyCreateFn);
createFromFactoriesIfPresent(m_clientConfig->executor, m_clientConfig->configFactories.executorCreateFn);
createFromFactoriesIfPresent(m_clientConfig->writeRateLimiter, m_clientConfig->configFactories.writeRateLimiterCreateFn);
Expand All @@ -91,6 +95,27 @@ void AwsSmithyClientBase::baseCopyInit() {
}
}

void AwsSmithyClientBase::baseCopyAssign(const AwsSmithyClientBase& other,
std::shared_ptr<Aws::Http::HttpClient> httpClient,
std::shared_ptr<Aws::Client::AWSErrorMarshaller> errorMarshaller) {
m_serviceName = other.m_serviceName;
m_serviceUserAgentName = other.m_serviceUserAgentName;
m_httpClient = std::move(httpClient);
m_errorMarshaller = std::move(errorMarshaller);
m_interceptors = Aws::Vector<std::shared_ptr<interceptor::Interceptor>>{Aws::MakeShared<ChecksumInterceptor>("AwsSmithyClientBase")};

baseCopyInit();
}

void AwsSmithyClientBase::baseMoveAssign(AwsSmithyClientBase&& other) {
m_serviceName = std::move(other.m_serviceName);
m_serviceUserAgentName = std::move(other.m_serviceUserAgentName);
m_httpClient = std::move(other.m_httpClient);
m_errorMarshaller = std::move(other.m_errorMarshaller);
m_interceptors = std::move(other.m_interceptors);
m_userAgentInterceptor = std::move(other.m_userAgentInterceptor);
}

std::shared_ptr<Aws::Http::HttpRequest>
AwsSmithyClientBase::BuildHttpRequest(const std::shared_ptr<AwsSmithyClientAsyncRequestContext>& pRequestCtx,
const Aws::Http::URI& uri,
Expand Down
Loading

0 comments on commit bd1a11b

Please sign in to comment.