Describe the feature
On S3CrtClient, GetObject/PutObject/CopyObject are all dispatched as CRT meta-requests (aws_s3_client_make_meta_request) and complete on a CRT event-loop thread. HeadObject is not: the synchronous method uses the blocking InvokeServiceOperation HTTP path, and the generated HeadObjectAsync is just a thin SubmitAsync(&S3CrtClient::HeadObject, ...) wrapper that runs that blocking call on the SDK thread executor (parking one executor thread per in-flight HEAD).
Please add a CRT-native HeadObjectAsync (event-loop dispatch, completion on an event-loop thread) alongside the other three.
Use Case
Workloads that gate a write on object existence (Head-theb-PUT) currently can't get the HEAD off the blocking path. When every other operation on the hot path is async on the event loop, the synchronous HEAD becomes the bottleneck: concurrency is capped by the SDK executor pool size and one executor thread is parked per in-flight HEAD, rather than being bounded by the caller's own in-flight limit like the async GET/PUT. A CRT-native HeadObjectAsync lets existence checks scale with the same concurrency budget as the rest of the I/O.
Proposed Solution
aws_s3_clint_make_meta_request already issues arbitrary single requests on the event loop via AWS_S3_META_REQUEST_TYPE_DEFUALT with operation_name set (that is exactly how CopyObjectAsync is implemented today) and "HeadObject" is a registered canonical operation name.
Implementation sketch:
Mirror CopyObjectAsync (the body-less DEFAULT meta-request sibling), with:
options.type = AWS_S3_META_REQUEST_TYPE_DEFAULT
options.operation_name = "HeadObject"
- HTTP method
HEAD
- A shutdown callback that builds
HeadObjectResult purely from response headers. HEAD has no body.
- A HEAD-specific finish callback that preserves the body-less HTTP-status → error mapping. See below.
Other Information
One correctness subtlety:
A correct HeadObjectAsync must preserve the body-less HTTP-status → error mapping — e.g. a HEAD-specific finish callback that records the response status and any error headers but does not set a client-error type when an HTTP response was received, leaving the client-error type for the transport / no-response case only.
Related (pre-existing): userData leak when make_meta_request returns null
Independent of HeadObject, the existing async Get/Put/Copy paths leak their CrtRequestCallbackUserData (allocated with Aws::New) when aws_s3_client_make_meta_request returns null: that branch returns via the handler without the shutdown callback ever running, and the shutdown callback is the only place Aws::Delete(userData) is called (see CopyObjectAsync / GetObjectAsync / PutObjectAsync). It only triggers on meta-request creation failure (effectively allocation failure), so the practical impact is negligible — but any new HeadObjectAsync should avoid reproducing it (free userData on the null-meta-request branch), and the existing methods could be fixed in passing.
Acknowledgements
Describe the feature
On
S3CrtClient,GetObject/PutObject/CopyObjectare all dispatched as CRT meta-requests (aws_s3_client_make_meta_request) and complete on a CRT event-loop thread.HeadObjectis not: the synchronous method uses the blockingInvokeServiceOperationHTTP path, and the generatedHeadObjectAsyncis just a thinSubmitAsync(&S3CrtClient::HeadObject, ...)wrapper that runs that blocking call on the SDK thread executor (parking one executor thread per in-flight HEAD).Please add a CRT-native
HeadObjectAsync(event-loop dispatch, completion on an event-loop thread) alongside the other three.Use Case
Workloads that gate a write on object existence (Head-theb-PUT) currently can't get the HEAD off the blocking path. When every other operation on the hot path is async on the event loop, the synchronous HEAD becomes the bottleneck: concurrency is capped by the SDK executor pool size and one executor thread is parked per in-flight HEAD, rather than being bounded by the caller's own in-flight limit like the async GET/PUT. A CRT-native
HeadObjectAsynclets existence checks scale with the same concurrency budget as the rest of the I/O.Proposed Solution
aws_s3_clint_make_meta_requestalready issues arbitrary single requests on the event loop viaAWS_S3_META_REQUEST_TYPE_DEFUALTwithoperation_nameset (that is exactly howCopyObjectAsyncis implemented today) and"HeadObject"is a registered canonical operation name.Implementation sketch:
Mirror
CopyObjectAsync(the body-less DEFAULT meta-request sibling), with:options.type = AWS_S3_META_REQUEST_TYPE_DEFAULToptions.operation_name = "HeadObject"HEADHeadObjectResultpurely from response headers.HEADhas no body.Other Information
One correctness subtlety:
A correct HeadObjectAsync must preserve the body-less HTTP-status → error mapping — e.g. a HEAD-specific finish callback that records the response status and any error headers but does not set a client-error type when an HTTP response was received, leaving the client-error type for the transport / no-response case only.
Related (pre-existing):
userDataleak whenmake_meta_requestreturns nullIndependent of HeadObject, the existing async Get/Put/Copy paths leak their
CrtRequestCallbackUserData(allocated withAws::New) whenaws_s3_client_make_meta_requestreturns null: that branch returns via the handler without the shutdown callback ever running, and the shutdown callback is the only placeAws::Delete(userData)is called (seeCopyObjectAsync/GetObjectAsync/PutObjectAsync). It only triggers on meta-request creation failure (effectively allocation failure), so the practical impact is negligible — but any newHeadObjectAsyncshould avoid reproducing it (freeuserDataon the null-meta-request branch), and the existing methods could be fixed in passing.Acknowledgements