From 37e702d705dcff89e65f3dcbcc2fdd47096b4c9b Mon Sep 17 00:00:00 2001 From: sbiscigl Date: Wed, 1 Jul 2026 17:03:55 -0400 Subject: [PATCH 1/2] move Head Object in crt client to aws-c-s3 implementation --- .../include/aws/s3-crt/S3CrtClient.h | 16 +- .../aws/s3-crt/S3CrtServiceClientModel.h | 1 - .../aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp | 180 ++++++++++++++++-- .../aws-cpp-sdk-s3/include/aws/s3/S3Client.h | 12 +- .../src/aws-cpp-sdk-s3/source/S3Client.cpp | 13 ++ .../cpp/s3/S3RestXmlCppClientGenerator.java | 1 + .../velocity/cpp/s3/S3ClientHeader.vm | 1 + .../velocity/cpp/s3/SmithyS3ClientHeader.vm | 1 + .../cpp/s3/s3-crt/S3CrtSpecificOperations.vm | 6 + .../s3-crt/SmithyS3CrtSpecificOperations.vm | 6 + 10 files changed, 193 insertions(+), 44 deletions(-) diff --git a/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3CrtClient.h b/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3CrtClient.h index 045803f4ea4..9edab72c9a4 100644 --- a/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3CrtClient.h +++ b/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3CrtClient.h @@ -4675,23 +4675,12 @@ class AWS_S3CRT_API S3CrtClient : public Aws::Client::AWSXMLClient, */ virtual Model::HeadObjectOutcome HeadObject(const Model::HeadObjectRequest& request) const; - /** - * A Callable wrapper for HeadObject that returns a future to the operation so that it can be executed in parallel to other requests. - */ - template - Model::HeadObjectOutcomeCallable HeadObjectCallable(const HeadObjectRequestT& request) const { - return SubmitCallable(&S3CrtClient::HeadObject, request); - } - /** * An Async wrapper for HeadObject that queues the request into a thread executor and triggers associated callback when operation has * finished. */ - template - void HeadObjectAsync(const HeadObjectRequestT& request, const HeadObjectResponseReceivedHandler& handler, - const std::shared_ptr& context = nullptr) const { - return SubmitAsync(&S3CrtClient::HeadObject, request, handler, context); - } + virtual void HeadObjectAsync(const Model::HeadObjectRequest& request, const HeadObjectResponseReceivedHandler& handler, + const std::shared_ptr& context = nullptr) const; /** *

This operation is not supported for directory buckets.

@@ -8720,6 +8709,7 @@ class AWS_S3CRT_API S3CrtClient : public Aws::Client::AWSXMLClient, GetObjectResponseReceivedHandler getResponseHandler; PutObjectResponseReceivedHandler putResponseHandler; CopyObjectResponseReceivedHandler copyResponseHandler; + HeadObjectResponseReceivedHandler headObjectResponseHandler; std::shared_ptr asyncCallerContext; const Aws::AmazonWebServiceRequest* originalRequest; std::shared_ptr request; diff --git a/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3CrtServiceClientModel.h b/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3CrtServiceClientModel.h index 288cc5bdef7..b65e7776e78 100644 --- a/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3CrtServiceClientModel.h +++ b/generated/src/aws-cpp-sdk-s3-crt/include/aws/s3-crt/S3CrtServiceClientModel.h @@ -414,7 +414,6 @@ typedef std::future GetObjectTaggingOutcomeCallable; typedef std::future GetObjectTorrentOutcomeCallable; typedef std::future GetPublicAccessBlockOutcomeCallable; typedef std::future HeadBucketOutcomeCallable; -typedef std::future HeadObjectOutcomeCallable; typedef std::future ListBucketAnalyticsConfigurationsOutcomeCallable; typedef std::future ListBucketIntelligentTieringConfigurationsOutcomeCallable; typedef std::future ListBucketInventoryConfigurationsOutcomeCallable; diff --git a/generated/src/aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp b/generated/src/aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp index b2e2d9f4888..0094d827847 100644 --- a/generated/src/aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp +++ b/generated/src/aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp @@ -1394,6 +1394,165 @@ GetObjectOutcome S3CrtClient::GetObject(const GetObjectRequest& request) const { {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}}); } +static void HeadObjectRequestShutdownCallback(void* user_data) { + if (!user_data) { + AWS_LOGSTREAM_ERROR("HeadObject", "user data passed is NULL "); + return; + } + auto* userData = static_cast(user_data); + + // call user callback and release user_data + S3Crt::Model::HeadObjectOutcome outcome(userData->s3CrtClient->GenerateXmlOutcome(userData->response)); + // log into monitor + if (userData->asyncCallerContext) { + if (!outcome.IsSuccess()) { + userData->asyncCallerContext->GetMonitorContext().OnRequestFailed(userData->request, userData->response); + } else { + userData->asyncCallerContext->GetMonitorContext().OnRequestSucceeded(userData->request, userData->response); + } + } + userData->headObjectResponseHandler(userData->s3CrtClient, *(reinterpret_cast(userData->originalRequest)), + std::move(outcome), userData->asyncCallerContext); + + Aws::Delete(userData); +} + +void S3CrtClient::HeadObjectAsync(const HeadObjectRequest& request, const HeadObjectResponseReceivedHandler& handler, + const std::shared_ptr& handlerContext) const { + AWS_ASYNC_OPERATION_GUARD(HeadObject); + if (!m_endpointProvider) { + return handler(this, request, + HeadObjectOutcome(Aws::Client::AWSError(S3CrtErrors::INTERNAL_FAILURE, "INTERNAL_FAILURE", + "Endpoint provider is not initialized", false)), + handlerContext); + } + if (!request.BucketHasBeenSet()) { + AWS_LOGSTREAM_ERROR("HeadObject", "Required field: Bucket, is not set"); + return handler(this, request, + HeadObjectOutcome(Aws::Client::AWSError(S3CrtErrors::MISSING_PARAMETER, "MISSING_PARAMETER", + "Missing required field [Bucket]", false)), + handlerContext); + } + if (!request.KeyHasBeenSet()) { + AWS_LOGSTREAM_ERROR("HeadObject", "Required field: Key, is not set"); + return handler(this, request, + HeadObjectOutcome(Aws::Client::AWSError(S3CrtErrors::MISSING_PARAMETER, "MISSING_PARAMETER", + "Missing required field [Key]", false)), + handlerContext); + } + auto meter = m_telemetryProvider->getMeter(this->GetServiceClientName(), {}); + auto endpointResolutionOutcome = TracingUtils::MakeCallWithTiming( + [&]() -> ResolveEndpointOutcome { return m_endpointProvider->ResolveEndpoint(request.GetEndpointContextParams()); }, + TracingUtils::SMITHY_CLIENT_ENDPOINT_RESOLUTION_METRIC, *meter, + {{TracingUtils::SMITHY_METHOD_DIMENSION, request.GetServiceRequestName()}, + {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}}); + if (!endpointResolutionOutcome.IsSuccess()) { + handler(this, request, + HeadObjectOutcome(Aws::Client::AWSError(CoreErrors::ENDPOINT_RESOLUTION_FAILURE, "ENDPOINT_RESOLUTION_FAILURE", + endpointResolutionOutcome.GetError().GetMessage(), false)), + handlerContext); + return; + } + endpointResolutionOutcome.GetResult().AddPathSegments(request.GetKey()); + + request.SetServiceSpecificParameters([&]() -> std::shared_ptr { + Aws::Map params; + params.emplace("bucketName", request.GetBucket()); + ServiceSpecificParameters serviceSpecificParameters{params}; + return Aws::MakeShared(ALLOCATION_TAG, serviceSpecificParameters); + }()); + + // make aws_s3_meta_request with callbacks + CrtRequestCallbackUserData* userData = Aws::New(ALLOCATION_TAG); + aws_s3_meta_request_options options; + AWS_ZERO_STRUCT(options); + aws_uri endpoint; + InitCrtEndpointFromUri(endpoint, endpointResolutionOutcome.GetResult().GetURI()); + options.endpoint = &endpoint; + std::unique_ptr endpointCleanup{&endpoint, &aws_uri_clean_up}; + + userData->headObjectResponseHandler = handler; + if (handlerContext) { + userData->asyncCallerContext = handlerContext; + } + InitCommonCrtRequestOption(userData, &options, &request, endpointResolutionOutcome.GetResult().GetURI(), + Aws::Http::HttpMethod::HTTP_HEAD); + if (userData != nullptr && userData->request != nullptr && userData->request->GetContentBody() != nullptr && + userData->request->GetContentBody()->fail()) { + } + if (handlerContext) { + handlerContext->GetMonitorContext().StartMonitorContext(Aws::String{"S3CrtClient"}, request.GetServiceRequestName(), userData->request); + } + options.shutdown_callback = HeadObjectRequestShutdownCallback; + options.type = AWS_S3_META_REQUEST_TYPE_DEFAULT; + options.operation_name = Aws::Crt::ByteCursorFromCString("HeadObject"); + struct aws_signing_config_aws signing_config_override = m_s3CrtSigningConfig; + if (endpointResolutionOutcome.GetResult().GetAttributes() && + endpointResolutionOutcome.GetResult().GetAttributes()->authScheme.GetSigningRegion()) { + signing_config_override.region = + Aws::Crt::ByteCursorFromCString(endpointResolutionOutcome.GetResult().GetAttributes()->authScheme.GetSigningRegion()->c_str()); + } + if (endpointResolutionOutcome.GetResult().GetAttributes() && + endpointResolutionOutcome.GetResult().GetAttributes()->authScheme.GetSigningRegionSet()) { + signing_config_override.region = + Aws::Crt::ByteCursorFromCString(endpointResolutionOutcome.GetResult().GetAttributes()->authScheme.GetSigningRegionSet()->c_str()); + } + if (endpointResolutionOutcome.GetResult().GetAttributes() && + endpointResolutionOutcome.GetResult().GetAttributes()->authScheme.GetSigningName()) { + signing_config_override.service = + Aws::Crt::ByteCursorFromCString(endpointResolutionOutcome.GetResult().GetAttributes()->authScheme.GetSigningName()->c_str()); + } + if (endpointResolutionOutcome.GetResult().GetAttributes() && + endpointResolutionOutcome.GetResult().GetAttributes()->authScheme.GetName() == "S3ExpressSigner") { + signing_config_override.algorithm = aws_signing_algorithm::AWS_SIGNING_ALGORITHM_V4_S3EXPRESS; + } else { + signing_config_override.algorithm = aws_signing_algorithm::AWS_SIGNING_ALGORITHM_V4; + } + options.signing_config = &signing_config_override; + + std::shared_ptr crtHttpRequest = userData->request->ToCrtHttpRequest(); + options.message = crtHttpRequest->GetUnderlyingMessage(); + userData->crtHttpRequest = crtHttpRequest; + + aws_s3_meta_request* meta_request = aws_s3_client_make_meta_request(m_s3CrtClient, &options); + if (meta_request == nullptr) { + return handler(this, request, + HeadObjectOutcome(Aws::Client::AWSError(S3CrtErrors::INTERNAL_FAILURE, "INTERNAL_FAILURE", + "Unable to create s3 meta request", false)), + handlerContext); + } + auto& shouldContinueFn = request.GetContinueRequestHandler(); + const HttpRequest* httpRequest = userData->request ? userData->request.get() : nullptr; + if (shouldContinueFn && !shouldContinueFn(httpRequest)) { + aws_s3_meta_request_cancel(meta_request); + } +} + +HeadObjectOutcome S3CrtClient::HeadObject(const HeadObjectRequest& request) const { + AWS_OPERATION_GUARD(HeadObject); + auto tracer = m_telemetryProvider->getTracer(this->GetServiceClientName(), {}); + auto meter = m_telemetryProvider->getMeter(this->GetServiceClientName(), {}); + AWS_OPERATION_CHECK_PTR(meter, GetObject, CoreErrors, CoreErrors::NOT_INITIALIZED); + return TracingUtils::MakeCallWithTiming( + [&]() -> HeadObjectOutcome { + Aws::Utils::Threading::Semaphore sem(0, 1); + HeadObjectOutcome res; + auto handlerContext = Aws::MakeShared(ALLOCATION_TAG); + auto handler = HeadObjectResponseReceivedHandler{[&](const S3CrtClient*, const HeadObjectRequest&, const HeadObjectOutcome& outcome, + const std::shared_ptr&) { + res = std::move(outcome); + sem.ReleaseAll(); + }}; + + S3CrtClient::HeadObjectAsync(request, handler, handlerContext); + sem.WaitOne(); + return res; + }, + TracingUtils::SMITHY_CLIENT_DURATION_METRIC, *meter, + {{TracingUtils::SMITHY_METHOD_DIMENSION, request.GetServiceRequestName()}, + {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}}); +} + static void PutObjectRequestShutdownCallback(void* user_data) { if (!user_data) { AWS_LOGSTREAM_ERROR("PutObject", "user data passed is NULL "); @@ -2929,27 +3088,6 @@ HeadBucketOutcome S3CrtClient::HeadBucket(const HeadBucketRequest& request) cons return result.IsSuccess() ? HeadBucketOutcome(result.GetResultWithOwnership()) : HeadBucketOutcome(std::move(result.GetError())); } -HeadObjectOutcome S3CrtClient::HeadObject(const HeadObjectRequest& request) const { - if (!request.BucketHasBeenSet()) { - AWS_LOGSTREAM_ERROR("HeadObject", "Required field: Bucket, is not set"); - return HeadObjectOutcome( - Aws::Client::AWSError(S3CrtErrors::MISSING_PARAMETER, "MISSING_PARAMETER", "Missing required field [Bucket]", false)); - } - if (!request.KeyHasBeenSet()) { - AWS_LOGSTREAM_ERROR("HeadObject", "Required field: Key, is not set"); - return HeadObjectOutcome( - Aws::Client::AWSError(S3CrtErrors::MISSING_PARAMETER, "MISSING_PARAMETER", "Missing required field [Key]", false)); - } - - auto uriResolver = [&](Aws::Endpoint::ResolveEndpointOutcome& endpointResolutionOutcome) { - (void)endpointResolutionOutcome; - endpointResolutionOutcome.GetResult().AddPathSegments(request.GetKey()); - }; - - auto result = InvokeServiceOperation(request, uriResolver, request.GetBucket(), Aws::Http::HttpMethod::HTTP_HEAD); - return result.IsSuccess() ? HeadObjectOutcome(result.GetResultWithOwnership()) : HeadObjectOutcome(std::move(result.GetError())); -} - ListBucketAnalyticsConfigurationsOutcome S3CrtClient::ListBucketAnalyticsConfigurations( const ListBucketAnalyticsConfigurationsRequest& request) const { if (!request.BucketHasBeenSet()) { diff --git a/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3Client.h b/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3Client.h index 030784cac80..c1d682f838f 100644 --- a/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3Client.h +++ b/generated/src/aws-cpp-sdk-s3/include/aws/s3/S3Client.h @@ -4692,20 +4692,14 @@ class AWS_S3_API S3Client : public Aws::Client::AWSXMLClient, /** * A Callable wrapper for HeadObject that returns a future to the operation so that it can be executed in parallel to other requests. */ - template - Model::HeadObjectOutcomeCallable HeadObjectCallable(const HeadObjectRequestT& request) const { - return SubmitCallable(&S3Client::HeadObject, request); - } + virtual Model::HeadObjectOutcomeCallable HeadObjectCallable(const Model::HeadObjectRequest& request) const; /** * An Async wrapper for HeadObject that queues the request into a thread executor and triggers associated callback when operation has * finished. */ - template - void HeadObjectAsync(const HeadObjectRequestT& request, const HeadObjectResponseReceivedHandler& handler, - const std::shared_ptr& context = nullptr) const { - return SubmitAsync(&S3Client::HeadObject, request, handler, context); - } + virtual void HeadObjectAsync(const Model::HeadObjectRequest& request, const HeadObjectResponseReceivedHandler& handler, + const std::shared_ptr& context = nullptr) const; /** *

This operation is not supported for directory buckets.

diff --git a/generated/src/aws-cpp-sdk-s3/source/S3Client.cpp b/generated/src/aws-cpp-sdk-s3/source/S3Client.cpp index ca75a7a73ea..e52a98978ae 100644 --- a/generated/src/aws-cpp-sdk-s3/source/S3Client.cpp +++ b/generated/src/aws-cpp-sdk-s3/source/S3Client.cpp @@ -1829,6 +1829,19 @@ HeadObjectOutcome S3Client::HeadObject(const HeadObjectRequest& request) const { return result.IsSuccess() ? HeadObjectOutcome(result.GetResultWithOwnership()) : HeadObjectOutcome(std::move(result.GetError())); } +HeadObjectOutcomeCallable S3Client::HeadObjectCallable(const HeadObjectRequest& request) const { + auto task = + Aws::MakeShared >(ALLOCATION_TAG, [this, request]() { return this->HeadObject(request); }); + auto packagedFunction = [task]() { (*task)(); }; + m_clientConfiguration.executor->Submit(packagedFunction); + return task->get_future(); +} + +void S3Client::HeadObjectAsync(const HeadObjectRequest& request, const HeadObjectResponseReceivedHandler& handler, + const std::shared_ptr& context) const { + m_clientConfiguration.executor->Submit([this, request, handler, context]() { handler(this, request, HeadObject(request), context); }); +} + ListBucketAnalyticsConfigurationsOutcome S3Client::ListBucketAnalyticsConfigurations( const ListBucketAnalyticsConfigurationsRequest& request) const { if (!request.BucketHasBeenSet()) { diff --git a/tools/code-generation/generator/src/main/java/com/amazonaws/util/awsclientgenerator/generators/cpp/s3/S3RestXmlCppClientGenerator.java b/tools/code-generation/generator/src/main/java/com/amazonaws/util/awsclientgenerator/generators/cpp/s3/S3RestXmlCppClientGenerator.java index 17bf260a0e2..1b9b5c895c4 100644 --- a/tools/code-generation/generator/src/main/java/com/amazonaws/util/awsclientgenerator/generators/cpp/s3/S3RestXmlCppClientGenerator.java +++ b/tools/code-generation/generator/src/main/java/com/amazonaws/util/awsclientgenerator/generators/cpp/s3/S3RestXmlCppClientGenerator.java @@ -173,6 +173,7 @@ public class S3RestXmlCppClientGenerator extends RestXmlCppClientGenerator { s3CrtEnabledOps.add("GetObject"); s3CrtEnabledOps.add("PutObject"); s3CrtEnabledOps.add("CopyObject"); + s3CrtEnabledOps.add("HeadObject"); bucketLocationConstraints.add("us-east-1"); bucketLocationConstraints.add("us-east-2"); diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ClientHeader.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ClientHeader.vm index a8b82fbf9a9..4c4340ea3c4 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ClientHeader.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/S3ClientHeader.vm @@ -213,6 +213,7 @@ namespace ${rootNamespace} GetObjectResponseReceivedHandler getResponseHandler; PutObjectResponseReceivedHandler putResponseHandler; CopyObjectResponseReceivedHandler copyResponseHandler; + HeadObjectResponseReceivedHandler headObjectResponseHandler; std::shared_ptr asyncCallerContext; const Aws::AmazonWebServiceRequest *originalRequest; std::shared_ptr request; diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/SmithyS3ClientHeader.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/SmithyS3ClientHeader.vm index 7f00421a0e6..376997c5ed0 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/SmithyS3ClientHeader.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/SmithyS3ClientHeader.vm @@ -229,6 +229,7 @@ namespace ${rootNamespace} GetObjectResponseReceivedHandler getResponseHandler; PutObjectResponseReceivedHandler putResponseHandler; CopyObjectResponseReceivedHandler copyResponseHandler; + HeadObjectResponseReceivedHandler headObjectResponseHandler; std::shared_ptr asyncCallerContext; const Aws::AmazonWebServiceRequest *originalRequest; std::shared_ptr request; diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtSpecificOperations.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtSpecificOperations.vm index 4b0845bcc3d..d74cd520d69 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtSpecificOperations.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtSpecificOperations.vm @@ -523,6 +523,8 @@ static void ${operation.name}RequestShutdownCallback(void *user_data) userData->getResponseHandler(userData->s3CrtClient, *(reinterpret_cast(userData->originalRequest)), std::move(outcome), userData->asyncCallerContext); #elseif($operation.name == "CopyObject") userData->copyResponseHandler(userData->s3CrtClient, *(reinterpret_cast(userData->originalRequest)), std::move(outcome), userData->asyncCallerContext); +#elseif($operation.name == "HeadObject") + userData->headObjectResponseHandler(userData->s3CrtClient, *(reinterpret_cast(userData->originalRequest)), std::move(outcome), userData->asyncCallerContext); #end #else (*handler)(userData->s3CrtClient, outcome, userData->userCallbackContext); @@ -594,6 +596,8 @@ void ${className}::${operation.name}Async(${constText}${operation.request.shape. userData->getResponseHandler = handler; #elseif($operation.name == "CopyObject") userData->copyResponseHandler = handler; +#elseif($operation.name == "HeadObject") + userData->headObjectResponseHandler = handler; #end if(handlerContext) { @@ -684,6 +688,7 @@ void ${className}::${operation.name}Async(${constText}${operation.request.shape. options.type = AWS_S3_META_REQUEST_TYPE_COPY_OBJECT; #else options.type = AWS_S3_META_REQUEST_TYPE_DEFAULT; + options.operation_name = Aws::Crt::ByteCursorFromCString("${operation.name}"); #end struct aws_signing_config_aws signing_config_override = m_s3CrtSigningConfig; #if($serviceModel.endpointRules) @@ -827,6 +832,7 @@ void ${className}::${operation.name}Async(${constText}${operation.name}ResponseR options.type = AWS_S3_META_REQUEST_TYPE_COPY_OBJECT; #else options.type = AWS_S3_META_REQUEST_TYPE_DEFAULT; + options.operation_name = Aws::Crt::ByteCursorFromCString("${operation.name}"); #end struct aws_signing_config_aws signing_config_override = m_s3CrtSigningConfig; signing_config_override.region = Aws::Crt::ByteCursorFromCString(computeEndpointOutcome.GetResult().signerRegion.c_str()); diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/SmithyS3CrtSpecificOperations.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/SmithyS3CrtSpecificOperations.vm index ef1535c8241..4e4177c5ed6 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/SmithyS3CrtSpecificOperations.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/SmithyS3CrtSpecificOperations.vm @@ -523,6 +523,8 @@ static void ${operation.name}RequestShutdownCallback(void *user_data) userData->getResponseHandler(userData->s3CrtClient, *(reinterpret_cast(userData->originalRequest)), std::move(outcome), userData->asyncCallerContext); #elseif($operation.name == "CopyObject") userData->copyResponseHandler(userData->s3CrtClient, *(reinterpret_cast(userData->originalRequest)), std::move(outcome), userData->asyncCallerContext); +#elseif($operation.name == "HeadObject") + userData->headObjectResponseHandler(userData->s3CrtClient, *(reinterpret_cast(userData->originalRequest)), std::move(outcome), userData->asyncCallerContext); #end #else (*handler)(userData->s3CrtClient, outcome, userData->userCallbackContext); @@ -612,6 +614,8 @@ void ${className}::${operation.name}Async(${constText}${operation.request.shape. userData->getResponseHandler = handler; #elseif($operation.name == "CopyObject") userData->copyResponseHandler = handler; +#elseif($operation.name == "HeadObject") + userData->headObjectResponseHandler = handler; #end if(handlerContext) { @@ -702,6 +706,7 @@ void ${className}::${operation.name}Async(${constText}${operation.request.shape. options.type = AWS_S3_META_REQUEST_TYPE_COPY_OBJECT; #else options.type = AWS_S3_META_REQUEST_TYPE_DEFAULT; + options.operation_name = Aws::Crt::ByteCursorFromCString("${operation.name}"); #end struct aws_signing_config_aws signing_config_override = m_s3CrtSigningConfig; #if($serviceModel.endpointRules) @@ -845,6 +850,7 @@ void ${className}::${operation.name}Async(${constText}${operation.name}ResponseR options.type = AWS_S3_META_REQUEST_TYPE_COPY_OBJECT; #else options.type = AWS_S3_META_REQUEST_TYPE_DEFAULT; + options.operation_name = Aws::Crt::ByteCursorFromCString("${operation.name}"); #end struct aws_signing_config_aws signing_config_override = m_s3CrtSigningConfig; signing_config_override.region = Aws::Crt::ByteCursorFromCString(computeEndpointOutcome.GetResult().signerRegion.c_str()); From 42d25680e89dc692cdcac83375c9847b86f3ccbb Mon Sep 17 00:00:00 2001 From: pulimsr Date: Mon, 13 Jul 2026 16:04:02 -0400 Subject: [PATCH 2/2] Regenerating S3CrtClient and updating Velocity templates for HeadObject CRT path --- .../aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp | 6 +- .../BucketAndObjectOperationTest.cpp | 64 +++++++++++++++++++ .../cpp/s3/s3-crt/S3CrtSpecificOperations.vm | 2 +- .../s3-crt/SmithyS3CrtSpecificOperations.vm | 2 +- 4 files changed, 69 insertions(+), 5 deletions(-) diff --git a/generated/src/aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp b/generated/src/aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp index 0094d827847..219de185c1c 100644 --- a/generated/src/aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp +++ b/generated/src/aws-cpp-sdk-s3-crt/source/S3CrtClient.cpp @@ -1212,7 +1212,7 @@ CopyObjectOutcome S3CrtClient::CopyObject(const CopyObjectRequest& request) cons AWS_OPERATION_GUARD(CopyObject); auto tracer = m_telemetryProvider->getTracer(this->GetServiceClientName(), {}); auto meter = m_telemetryProvider->getMeter(this->GetServiceClientName(), {}); - AWS_OPERATION_CHECK_PTR(meter, GetObject, CoreErrors, CoreErrors::NOT_INITIALIZED); + AWS_OPERATION_CHECK_PTR(meter, CopyObject, CoreErrors, CoreErrors::NOT_INITIALIZED); return TracingUtils::MakeCallWithTiming( [&]() -> CopyObjectOutcome { Aws::Utils::Threading::Semaphore sem(0, 1); @@ -1532,7 +1532,7 @@ HeadObjectOutcome S3CrtClient::HeadObject(const HeadObjectRequest& request) cons AWS_OPERATION_GUARD(HeadObject); auto tracer = m_telemetryProvider->getTracer(this->GetServiceClientName(), {}); auto meter = m_telemetryProvider->getMeter(this->GetServiceClientName(), {}); - AWS_OPERATION_CHECK_PTR(meter, GetObject, CoreErrors, CoreErrors::NOT_INITIALIZED); + AWS_OPERATION_CHECK_PTR(meter, HeadObject, CoreErrors, CoreErrors::NOT_INITIALIZED); return TracingUtils::MakeCallWithTiming( [&]() -> HeadObjectOutcome { Aws::Utils::Threading::Semaphore sem(0, 1); @@ -1727,7 +1727,7 @@ PutObjectOutcome S3CrtClient::PutObject(const PutObjectRequest& request) const { AWS_OPERATION_GUARD(PutObject); auto tracer = m_telemetryProvider->getTracer(this->GetServiceClientName(), {}); auto meter = m_telemetryProvider->getMeter(this->GetServiceClientName(), {}); - AWS_OPERATION_CHECK_PTR(meter, GetObject, CoreErrors, CoreErrors::NOT_INITIALIZED); + AWS_OPERATION_CHECK_PTR(meter, PutObject, CoreErrors, CoreErrors::NOT_INITIALIZED); return TracingUtils::MakeCallWithTiming( [&]() -> PutObjectOutcome { Aws::Utils::Threading::Semaphore sem(0, 1); diff --git a/tests/aws-cpp-sdk-s3-crt-integration-tests/BucketAndObjectOperationTest.cpp b/tests/aws-cpp-sdk-s3-crt-integration-tests/BucketAndObjectOperationTest.cpp index 7d307c431a6..bbfb74deb37 100644 --- a/tests/aws-cpp-sdk-s3-crt-integration-tests/BucketAndObjectOperationTest.cpp +++ b/tests/aws-cpp-sdk-s3-crt-integration-tests/BucketAndObjectOperationTest.cpp @@ -2028,4 +2028,68 @@ namespace .WithKey(objectKey)); AWS_EXPECT_SUCCESS(getObjectResponse); } + + TEST_F(BucketAndObjectOperationTest, TestHeadObjectViaCrt) + { + const Aws::String fullBucketName = CalculateBucketName(BASE_PUT_OBJECTS_BUCKET_NAME.c_str()); + SCOPED_TRACE(Aws::String("FullBucketName ") + fullBucketName); + CreateBucketRequest createBucketRequest; + createBucketRequest.SetBucket(fullBucketName); + createBucketRequest.SetACL(BucketCannedACL::private_); + CreateBucketOutcome createBucketOutcome = Client->CreateBucket(createBucketRequest); + AWS_ASSERT_SUCCESS(createBucketOutcome); + ASSERT_TRUE(WaitForBucketToPropagate(fullBucketName)); + TagTestBucket(fullBucketName, Client); + + PutObjectRequest putObjectRequest; + putObjectRequest.SetBucket(fullBucketName); + putObjectRequest.SetKey(TEST_OBJ_KEY); + putObjectRequest.SetContentType("text/plain"); + std::shared_ptr objectStream = Aws::MakeShared(ALLOCATION_TAG, "HeadObject CRT test content"); + putObjectRequest.SetBody(objectStream); + PutObjectOutcome putObjectOutcome = Client->PutObject(putObjectRequest); + AWS_ASSERT_SUCCESS(putObjectOutcome); + ASSERT_TRUE(WaitForObjectToPropagate(fullBucketName, TEST_OBJ_KEY)); + + // Sync HeadObject + HeadObjectRequest headObjectRequest; + headObjectRequest.SetBucket(fullBucketName); + headObjectRequest.SetKey(TEST_OBJ_KEY); + HeadObjectOutcome headObjectOutcome = Client->HeadObject(headObjectRequest); + AWS_ASSERT_SUCCESS(headObjectOutcome); + ASSERT_STREQ("text/plain", headObjectOutcome.GetResult().GetContentType().c_str()); + ASSERT_TRUE(headObjectOutcome.GetResult().GetContentLength() > 0); + + // Async HeadObject + std::mutex mtx; + std::condition_variable cv; + bool handlerCalled = false; + HeadObjectOutcome asyncOutcome; + auto asyncHandler = [&](const S3CrtClient*, + const Model::HeadObjectRequest&, + const Model::HeadObjectOutcome& outcome, + const std::shared_ptr&) + { + std::unique_lock lock(mtx); + asyncOutcome = outcome; + handlerCalled = true; + cv.notify_one(); + }; + Client->HeadObjectAsync(headObjectRequest, asyncHandler); + { + std::unique_lock lock(mtx); + cv.wait_for(lock, std::chrono::seconds(60), [&handlerCalled]() { return handlerCalled; }); + } + ASSERT_TRUE(handlerCalled) << "HeadObjectAsync handler was not called within 60 seconds"; + AWS_ASSERT_SUCCESS(asyncOutcome); + ASSERT_STREQ("text/plain", asyncOutcome.GetResult().GetContentType().c_str()); + ASSERT_TRUE(asyncOutcome.GetResult().GetContentLength() > 0); + + // HeadObject on non-existent key should fail + HeadObjectRequest headMissingRequest; + headMissingRequest.SetBucket(fullBucketName); + headMissingRequest.SetKey("non-existent-key-that-does-not-exist"); + HeadObjectOutcome headMissingOutcome = Client->HeadObject(headMissingRequest); + ASSERT_FALSE(headMissingOutcome.IsSuccess()); + } } diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtSpecificOperations.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtSpecificOperations.vm index d74cd520d69..73f003974f8 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtSpecificOperations.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/S3CrtSpecificOperations.vm @@ -772,7 +772,7 @@ ${operation.name}Outcome ${className}::${operation.name}(${constText}${operation AWS_OPERATION_GUARD(${operation.name}); auto tracer = m_telemetryProvider->getTracer(this->GetServiceClientName(), {}); auto meter = m_telemetryProvider->getMeter(this->GetServiceClientName(), {}); - AWS_OPERATION_CHECK_PTR(meter, GetObject, CoreErrors, CoreErrors::NOT_INITIALIZED); + AWS_OPERATION_CHECK_PTR(meter, ${operation.name}, CoreErrors, CoreErrors::NOT_INITIALIZED); return TracingUtils::MakeCallWithTiming<${operation.name}Outcome>( [&]()-> ${operation.name}Outcome { Aws::Utils::Threading::Semaphore sem(0, 1); diff --git a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/SmithyS3CrtSpecificOperations.vm b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/SmithyS3CrtSpecificOperations.vm index 4e4177c5ed6..db0a8aeda62 100644 --- a/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/SmithyS3CrtSpecificOperations.vm +++ b/tools/code-generation/generator/src/main/resources/com/amazonaws/util/awsclientgenerator/velocity/cpp/s3/s3-crt/SmithyS3CrtSpecificOperations.vm @@ -790,7 +790,7 @@ ${operation.name}Outcome ${className}::${operation.name}(${constText}${operation AWS_OPERATION_GUARD(${operation.name}); auto tracer = m_clientConfiguration.telemetryProvider->getTracer(this->GetServiceClientName(), {}); auto meter = m_clientConfiguration.telemetryProvider->getMeter(this->GetServiceClientName(), {}); - AWS_OPERATION_CHECK_PTR(meter, GetObject, CoreErrors, CoreErrors::NOT_INITIALIZED); + AWS_OPERATION_CHECK_PTR(meter, ${operation.name}, CoreErrors, CoreErrors::NOT_INITIALIZED); return TracingUtils::MakeCallWithTiming<${operation.name}Outcome>( [&]()-> ${operation.name}Outcome { Aws::Utils::Threading::Semaphore sem(0, 1);