Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ namespace smithy
std::shared_ptr<Aws::Utils::Threading::Executor> m_pExecutor;
std::shared_ptr<interceptor::InterceptorContext> m_interceptorContext;
std::shared_ptr<smithy::AwsIdentity> m_awsIdentity;

AwsSmithyClientAsyncRequestContext() = default;

AwsSmithyClientAsyncRequestContext(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This defaults initialization of few members which was previously being done in request processing

Aws::AmazonWebServiceRequest const * const request,
const char* requestName,
std::shared_ptr<Aws::Utils::Threading::Executor> pExecutor):
m_invocationId{Aws::Utils::UUID::PseudoRandomUUID()},
m_pRequest{request},
m_requestName{requestName ? requestName : m_pRequest ? m_pRequest->GetServiceRequestName() : ""},
m_retryCount{0},
m_pExecutor{pExecutor}
{

}
};
} // namespace client
} // namespace smithy
11 changes: 11 additions & 0 deletions src/aws-cpp-sdk-core/include/smithy/client/AwsSmithyClientBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,10 @@ namespace client
virtual bool AdjustClockSkew(HttpResponseOutcome& outcome, const AuthSchemeOption& authSchemeOption) const = 0;
virtual IdentityOutcome ResolveIdentity(const AwsSmithyClientAsyncRequestContext& ctx) const = 0;
virtual GetContextEndpointParametersOutcome GetContextEndpointParameters(const AwsSmithyClientAsyncRequestContext& ctx) const = 0;
AwsSmithyClientBase::ResolveEndpointOutcome ResolveEndpointFromRequest(
Aws::AmazonWebServiceRequest const * const request,
const char* requestName,
EndpointUpdateCallback&& endpointCallback) const;

/* 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.
Expand All @@ -220,6 +224,13 @@ namespace client
std::shared_ptr<smithy::client::UserAgentInterceptor> m_userAgentInterceptor;
private:
void UpdateAuthSchemeFromEndpoint(const Aws::Endpoint::AWSEndpoint& endpoint, AuthSchemeOption& authscheme) const;

bool ResolveIdentityAuth(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. pExecutor and responseHandler assumes that the function is async, why does it also return a simple boolean? How does it going to fit into the existing async design?
  2. Why do we need 2 method of ResolveIdentityAuth?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This is a Private helper method , the reason it returns bool is simply for the caller to know if any error occurred. The actual type specific outcome is handled in responseHandler. The return type is bool because this function throws different types of outcome errors which will need a variant and complicate the code much more in the calling function. The equivalent is achieved by choosing a simple return type but still processing and returning correct outcome type through the responseHandler.
  2. ResolveIdentityAuth is a private method that just only houses the code for identity and auth resolution. It is simply reused in preexisting 'AwsSmithyClientBase::MakeRequestAsync'. The reason this part of code is moved to a helper function is because in S3-CRT operations like put/getObjectAsync, we only resolve the endpoint in sdk and for rest of the request handling happens in crt.

std::shared_ptr<AwsSmithyClientAsyncRequestContext>& pRequestCtx,
ResponseHandlerFunc&& responseHandler,
EndpointUpdateCallback&& endpointCallback
) const;

};
} // namespace client
} // namespace smithy
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ namespace smithy {

};

DefaultAwsCredentialIdentityResolver(const Aws::Auth::DefaultAWSCredentialsProviderChain& credChain): legacyChain_sp{Aws::MakeShared<Aws::Auth::DefaultAWSCredentialsProviderChain>(ALLOC_ID, credChain)}{

};

DefaultAwsCredentialIdentityResolver(const DefaultAwsCredentialIdentityResolver& other) = delete;
DefaultAwsCredentialIdentityResolver(DefaultAwsCredentialIdentityResolver&& other) noexcept = default;
DefaultAwsCredentialIdentityResolver& operator=(const DefaultAwsCredentialIdentityResolver& other) = delete;
Expand Down
152 changes: 96 additions & 56 deletions src/aws-cpp-sdk-core/source/smithy/client/AwsSmithyClientBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,49 +177,19 @@ AwsSmithyClientBase::BuildHttpRequest(const std::shared_ptr<AwsSmithyClientAsync
return httpRequest;
}

void AwsSmithyClientBase::MakeRequestAsync(Aws::AmazonWebServiceRequest const* const request,
const char* requestName,
Aws::Http::HttpMethod method,
EndpointUpdateCallback&& endpointCallback,
ResponseHandlerFunc&& responseHandler,
std::shared_ptr<Aws::Utils::Threading::Executor> pExecutor) const

bool AwsSmithyClientBase::ResolveIdentityAuth(
std::shared_ptr<AwsSmithyClientAsyncRequestContext>& pRequestCtx,
ResponseHandlerFunc&& responseHandler,
EndpointUpdateCallback&& endpointCallback
) 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<AwsSmithyClientAsyncRequestContext> pRequestCtx =
Aws::MakeShared<AwsSmithyClientAsyncRequestContext>(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_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;
responseHandler(std::move(authSchemeOptionOutcome));
return false;
}
pRequestCtx->m_authSchemeOption = std::move(authSchemeOptionOutcome.GetResultWithOwnership());
assert(pRequestCtx->m_authSchemeOption.schemeId);
Expand All @@ -228,26 +198,23 @@ void AwsSmithyClientBase::MakeRequestAsync(Aws::AmazonWebServiceRequest const* c
auto identityOutcome = this->ResolveIdentity(*pRequestCtx);
if (!identityOutcome.IsSuccess())
{
pExecutor->Submit([identityOutcome, responseHandler]() mutable
{
responseHandler(std::move(identityOutcome));
});
return;
responseHandler(std::move(identityOutcome));
return false;
}

pRequestCtx->m_awsIdentity = std::move(identityOutcome.GetResultWithOwnership());

// get endpoint params from operation context
const auto contextEndpointParameters = this->GetContextEndpointParameters(*pRequestCtx);

if (!contextEndpointParameters.IsSuccess())
{
pExecutor->Submit([contextEndpointParameters, responseHandler]() mutable
{
responseHandler(std::move(contextEndpointParameters.GetError()));
});
return;
responseHandler(std::move(contextEndpointParameters.GetError()));

return false;
}

Aws::Endpoint::EndpointParameters epParams = request ? request->GetEndpointContextParams() : Aws::Endpoint::EndpointParameters();
Aws::Endpoint::EndpointParameters epParams = pRequestCtx->m_pRequest ? pRequestCtx->m_pRequest->GetEndpointContextParams() : Aws::Endpoint::EndpointParameters();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why ResolveIdentity also resolves an endpoint?

Copy link
Contributor Author

@sbera87 sbera87 Mar 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The helper function just houses the code to select auth scheme, resolve identity and resolve endpoint.
In pass case, the crt methods need the endpointOutcome.
For example, from s3-crtclient.cpp, for one of the async apis, the crt initialization takes endpoint, which is simply obtained in the smithy auth call flow aforementioned.

auto endpointResolutionOutcome = TracingUtils::MakeCallWithTiming<ResolveEndpointOutcome>(
  [&]() -> ResolveEndpointOutcome {
    return AwsSmithyClientT::ResolveIdentityAuth(
    &request,
    request.GetServiceRequestName(),
    std::move(endpointCallback));
   },
  TracingUtils::SMITHY_CLIENT_ENDPOINT_RESOLUTION_METRIC,
  *meter,
  {{TracingUtils::SMITHY_METHOD_DIMENSION, request.GetServiceRequestName()}, {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});

  // make aws_s3_meta_request with callbacks
  CrtRequestCallbackUserData *userData = Aws::New<CrtRequestCallbackUserData>(ALLOCATION_TAG);
  aws_s3_meta_request_options options;
  AWS_ZERO_STRUCT(options);
  aws_uri endpoint;
  InitCrtEndpointFromUri(endpoint, endpointResolutionOutcome.GetResult().GetURI());

const auto authSchemeEpParams = pRequestCtx->m_authSchemeOption.endpointParameters();
epParams.insert(epParams.end(), authSchemeEpParams.begin(), authSchemeEpParams.end());
const auto contextParams = contextEndpointParameters.GetResult();
Expand All @@ -260,25 +227,63 @@ void AwsSmithyClientBase::MakeRequestAsync(Aws::AmazonWebServiceRequest const* c
epResolutionOutcome.GetError().GetExceptionName(),
epResolutionOutcome.GetError().GetMessage(),
false});

pExecutor->Submit([epOutcome, responseHandler]() mutable
{
responseHandler(std::move(epOutcome));
} );
return;
responseHandler(std::move(epOutcome));
return false;
}
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*/));
responseHandler(std::move(outcome));
return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why ResolveIdentity also validates DNS host name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the helper function just houses the code to select auth, resolve identity and then resolve endpoint, this validation code is the existing check we have to ensure endpoint resolved is valid or not. In case it is not valid, it reflects that in the outcome.

}
return true;
}

void AwsSmithyClientBase::MakeRequestAsync(Aws::AmazonWebServiceRequest const* const request,
const char* requestName,
Aws::Http::HttpMethod method,
EndpointUpdateCallback&& endpointCallback,
ResponseHandlerFunc&& responseHandler,
std::shared_ptr<Aws::Utils::Threading::Executor> 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<AwsSmithyClientAsyncRequestContext> pRequestCtx =
Aws::MakeShared<AwsSmithyClientAsyncRequestContext>(AWS_SMITHY_CLIENT_LOG, request, requestName, pExecutor );
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_method = method;
ResponseHandlerFunc modifiedResponseHandler = [&](HttpResponseOutcome&& outcome){
auto capturedOutcome = std::make_shared<HttpResponseOutcome>(std::move(outcome));
pExecutor->Submit([capturedOutcome, &responseHandler]()
{
responseHandler(std::move(*capturedOutcome));
});
};

if(!ResolveIdentityAuth(
pRequestCtx,
std::move(modifiedResponseHandler),
std::move(endpointCallback)))
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Async in MakeRequestAsync stands for async execution of this method, the handler is not being called, the caller will wait forever for a request.

Copy link
Contributor Author

@sbera87 sbera87 Mar 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure which handler is being referred to, but 'responseHandler' is being called. This is tested with S3-crt and S3-crt express integration and unit tests

return;
}
pRequestCtx->m_requestInfo.attempt = 1;
pRequestCtx->m_requestInfo.maxAttempts = 0;
pRequestCtx->m_interceptorContext = Aws::MakeShared<InterceptorContext>(AWS_SMITHY_CLIENT_LOG, *request);
Expand Down Expand Up @@ -682,4 +687,39 @@ void AwsSmithyClientBase::AppendToUserAgent(const Aws::String& valueToAppend)
{
assert(m_userAgentInterceptor);
m_userAgentInterceptor->AddLegacyFeaturesToUserAgent(valueToAppend);
}

/*
blocking API to resolve endpoint from request
*/
AwsSmithyClientBase::ResolveEndpointOutcome AwsSmithyClientBase::ResolveEndpointFromRequest(
Aws::AmazonWebServiceRequest const * const request,
const char* requestName,
EndpointUpdateCallback&& endpointCallback) const
{
ResolveEndpointOutcome outcome = ClientError(CoreErrors::INTERNAL_FAILURE, "", "Response handler was not called", false);
ResponseHandlerFunc responseHandler = [&outcome](HttpResponseOutcome&& asyncOutcome)
{
outcome = std::move(asyncOutcome);
};

std::shared_ptr<AwsSmithyClientAsyncRequestContext> pRequestCtx = Aws::MakeShared<AwsSmithyClientAsyncRequestContext>(AWS_SMITHY_CLIENT_LOG, request, requestName, nullptr);
if (!pRequestCtx)
{
AWS_LOGSTREAM_ERROR(AWS_SMITHY_CLIENT_LOG, "Failed to allocate an AwsSmithyClientAsyncRequestContext under a shared ptr");
auto result = HttpResponseOutcome(ClientError(CoreErrors::MEMORY_ALLOCATION, "", "Failed to allocate async request context", false/*retryable*/));
responseHandler(std::move(result));
}
else
{
if(this->ResolveIdentityAuth(
pRequestCtx,
std::move(responseHandler),
std::move(endpointCallback)
))
{
outcome = std::move(pRequestCtx->m_endpoint);
}
}
return outcome;
}
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ protected SdkFileEntry GenerateLegacyClientSourceFile(final ServiceModel service
"aws.auth#sigv4a", "smithy::SigV4aAuthScheme",
"bearer", "smithy::BearerTokenAuthScheme",
"v4","smithy::SigV4AuthScheme",
"sigv4-s3express","S3::S3ExpressSigV4AuthScheme",
"sigv4-s3express","S3ExpressSigV4AuthScheme",
"v2","smithy::SigV4AuthScheme"
);

Expand All @@ -808,7 +808,7 @@ protected String mapAuthSchemes(final String authSchemeName) {
"aws.auth#sigv4a", "smithy::SigV4aAuthSchemeOption::sigV4aAuthSchemeOption",
"bearer", "smithy::BearerTokenAuthSchemeOption::bearerTokenAuthSchemeOption",
"v4", "smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption",
"sigv4-s3express", "S3::S3ExpressSigV4AuthSchemeOption::s3ExpressSigV4AuthSchemeOption",
"sigv4-s3express", "S3ExpressSigV4AuthSchemeOption::s3ExpressSigV4AuthSchemeOption",
"v2", "smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption"
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -480,8 +480,16 @@ protected List<SdkFileEntry> generateClientSourceFile(final List<ServiceModel> s
}
Map<String, String> templateOverride = new HashMap<>();
if ("S3-CRT".equalsIgnoreCase(serviceModels.get(i).getMetadata().getProjectName())) {
templateOverride.put("ServiceClientSourceInit_template",
"/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtServiceClientSourceInit.vm");
if (serviceModels.get(i).isUseSmithyClient())
{
templateOverride.put("ServiceClientSourceInit_template",
"/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/SmithyS3CrtServiceClientSourceInit.vm");
}
else
{
templateOverride.put("ServiceClientSourceInit_template",
"/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtServiceClientSourceInit.vm");
}
}
VelocityContext context = createContext(serviceModels.get(i));
context.put("CppViewHelper", CppViewHelper.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@
#end
#if($serviceModel.metadata.namespace == "S3Crt")
\#include <aws/s3-crt/S3CrtIdentityProviderAdapter.h>
#if($serviceModel.isUseSmithyClient())
\#include <smithy/client/AwsSmithyClientAsyncRequestContext.h>
#end
#end

\#include <smithy/tracing/TracingUtils.h>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,11 +280,7 @@ namespace ${rootNamespace}
bool m_enableHostPrefixInjection = false;
Aws::String m_configScheme;
#end##-#if(!$serviceModel.endpointRules)
#if($serviceModel.endpointRules)
#if($serviceNamespace == "S3Crt")
${metadata.classNamePrefix}::ClientConfiguration m_clientConfiguration;
#end
#end

#if($serviceNamespace == "S3Crt")
struct aws_s3_client* m_s3CrtClient = {};
struct aws_signing_config_aws m_s3CrtSigningConfig = {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ namespace ${serviceNamespace} {
{
{Aws::Auth::ASYMMETRIC_SIGV4_SIGNER, smithy::SigV4aAuthSchemeOption::sigV4aAuthSchemeOption},
{Aws::Auth::SIGV4_SIGNER, smithy::SigV4AuthSchemeOption::sigV4AuthSchemeOption},
{S3::S3_EXPRESS_SIGNER_NAME, S3ExpressSigV4AuthSchemeOption::s3ExpressSigV4AuthSchemeOption}
{${serviceNamespace}::S3_EXPRESS_SIGNER_NAME, S3ExpressSigV4AuthSchemeOption::s3ExpressSigV4AuthSchemeOption}
};
authSchemes.reserve(authSchemeMap.size());
auto authschemeMapper = [&](const Aws::String& schemeId){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
\#include <aws/${metadata.projectName}/S3ExpressIdentity.h>
\#include <thread>
\#include <condition_variable>
\#include <aws/s3/S3_EXPORTS.h>
\#include <aws/${metadata.projectName}/${metadata.classNamePrefix}_EXPORTS.h>

namespace ${rootNamespace} {
namespace Http {
Expand All @@ -24,7 +24,7 @@ namespace ${rootNamespace} {
class ${serviceNamespace}Client;
class ${CppViewHelper.computeExportValue($serviceNamespace)} S3ExpressIdentityProvider : public smithy::IdentityResolverBase<S3ExpressIdentity> {
public:
explicit S3ExpressIdentityProvider(const S3Client& s3Client);
explicit S3ExpressIdentityProvider(const ${serviceNamespace}Client& s3Client);
ResolveIdentityFutureOutcome getIdentity(
const IdentityProperties& identityProperties,
const AdditionalParameters& additionalParameters) override;
Expand All @@ -37,7 +37,7 @@ namespace ${rootNamespace} {
S3ExpressIdentity GetCredentialsFromBucket(const Aws::String& bucketName) const;

private:
const Aws::S3::S3Client& m_s3Client;
const ${serviceNamespace}Client& m_s3Client;
mutable std::mutex m_bucketNameMapMutex;
Aws::Map<Aws::String, std::shared_ptr<std::mutex>> m_bucketNameMutex;

Expand All @@ -48,9 +48,9 @@ namespace ${rootNamespace} {

class ${CppViewHelper.computeExportValue($serviceNamespace)} DefaultS3ExpressIdentityProvider : public S3ExpressIdentityProvider {
public:
explicit DefaultS3ExpressIdentityProvider(const S3Client& s3Client);
explicit DefaultS3ExpressIdentityProvider(const ${serviceNamespace}Client& s3Client);
explicit DefaultS3ExpressIdentityProvider(
const S3Client& s3Client,
const ${serviceNamespace}Client& s3Client,
std::shared_ptr<Utils::ConcurrentCache<Aws::String, S3ExpressIdentity>> credentialsCache);
DefaultS3ExpressIdentityProvider(const DefaultS3ExpressIdentityProvider& other) = delete;
DefaultS3ExpressIdentityProvider(DefaultS3ExpressIdentityProvider&& other) noexcept = delete;
Expand All @@ -66,11 +66,11 @@ namespace ${rootNamespace} {
class ${CppViewHelper.computeExportValue($serviceNamespace)} DefaultAsyncS3ExpressIdentityProvider : public S3ExpressIdentityProvider {
public:
explicit DefaultAsyncS3ExpressIdentityProvider(
const S3Client& s3Client,
const ${serviceNamespace}Client& s3Client,
std::chrono::minutes refreshPeriod = std::chrono::minutes(1));

explicit DefaultAsyncS3ExpressIdentityProvider(
const S3Client& s3Client,
const ${serviceNamespace}Client& s3Client,
std::shared_ptr<Utils::ConcurrentCache<Aws::String, S3ExpressIdentity>> credentialsCache,
std::chrono::minutes refreshPeriod = std::chrono::minutes(1));

Expand Down
Loading
Loading