diff --git a/examples/SelectObjectContent.cc b/examples/SelectObjectContent.cc index 78731b20..9c751098 100644 --- a/examples/SelectObjectContent.cc +++ b/examples/SelectObjectContent.cc @@ -32,10 +32,12 @@ int main() { std::string expression = "select * from S3Object"; minio::s3::CsvInputSerialization csv_input; minio::s3::FileHeaderInfo file_header_info = minio::s3::FileHeaderInfo::kUse; - csv_input.file_header_info = &file_header_info; + csv_input.file_header_info = + std::make_shared(file_header_info); minio::s3::CsvOutputSerialization csv_output; minio::s3::QuoteFields quote_fields = minio::s3::QuoteFields::kAsNeeded; - csv_output.quote_fields = "e_fields; + csv_output.quote_fields = + std::make_shared(quote_fields); minio::s3::SelectRequest request(expression, &csv_input, &csv_output); std::string records; diff --git a/include/miniocpp/args.h b/include/miniocpp/args.h index 13548ce4..19870928 100644 --- a/include/miniocpp/args.h +++ b/include/miniocpp/args.h @@ -337,6 +337,9 @@ struct PutObjectArgs : public PutObjectBaseArgs { // Exactly one of (stream, buf) must be set; Validate() enforces. // When buf is set, the call attempts RDMA and falls back to a // streaming HTTP upload from the same buffer on RDMA decline. + // + // For async callers (PutObjectAsync): *stream must outlive the + // returned std::future, exactly as it must for sync PutObject. std::istream* stream = nullptr; char* buf = nullptr; std::optional size; @@ -425,7 +428,7 @@ struct RemoveObjectsArgs : public BucketArgs { }; // struct RemoveObjectsArgs struct SelectObjectContentArgs : public ObjectReadArgs { - SelectRequest& request; + SelectRequest request; SelectResultFunction resultfunc = nullptr; SelectObjectContentArgs(SelectRequest& req, SelectResultFunction func) @@ -466,7 +469,7 @@ using DeleteBucketNotificationArgs = BucketArgs; using GetBucketNotificationArgs = BucketArgs; struct SetBucketNotificationArgs : public BucketArgs { - NotificationConfig& config; + NotificationConfig config; explicit SetBucketNotificationArgs(NotificationConfig& configvalue) : config(configvalue) {} @@ -479,7 +482,7 @@ using DeleteBucketEncryptionArgs = BucketArgs; using GetBucketEncryptionArgs = BucketArgs; struct SetBucketEncryptionArgs : public BucketArgs { - SseConfig& config; + SseConfig config; explicit SetBucketEncryptionArgs(SseConfig& sseconfig) : config(sseconfig) {} @@ -505,7 +508,7 @@ using DeleteBucketReplicationArgs = BucketArgs; using GetBucketReplicationArgs = BucketArgs; struct SetBucketReplicationArgs : public BucketArgs { - ReplicationConfig& config; + ReplicationConfig config; explicit SetBucketReplicationArgs(ReplicationConfig& value) : config(value) {} @@ -517,7 +520,7 @@ using DeleteBucketLifecycleArgs = BucketArgs; using GetBucketLifecycleArgs = BucketArgs; struct SetBucketLifecycleArgs : public BucketArgs { - LifecycleConfig& config; + LifecycleConfig config; explicit SetBucketLifecycleArgs(LifecycleConfig& value) : config(value) {} diff --git a/include/miniocpp/baseclient.h b/include/miniocpp/baseclient.h index 82f47805..1b9c3364 100644 --- a/include/miniocpp/baseclient.h +++ b/include/miniocpp/baseclient.h @@ -18,6 +18,7 @@ #ifndef MINIO_CPP_BASECLIENT_H_INCLUDED #define MINIO_CPP_BASECLIENT_H_INCLUDED +#include #include #include #include @@ -171,6 +172,99 @@ class BaseClient { UploadPartResponse UploadPart(UploadPartArgs args); UploadPartCopyResponse UploadPartCopy(UploadPartCopyArgs args); + // Async overloads — return std::future backed by std::async. + // These are additive and non-breaking; sync methods remain unchanged. + std::future AbortMultipartUploadAsync( + AbortMultipartUploadArgs args); + std::future BucketExistsAsync(BucketExistsArgs args); + std::future CompleteMultipartUploadAsync( + CompleteMultipartUploadArgs args); + std::future CreateMultipartUploadAsync( + CreateMultipartUploadArgs args); + std::future DeleteBucketEncryptionAsync( + DeleteBucketEncryptionArgs args); + std::future DeleteBucketLifecycleAsync( + DeleteBucketLifecycleArgs args); + std::future DeleteBucketNotificationAsync( + DeleteBucketNotificationArgs args); + std::future DeleteBucketPolicyAsync( + DeleteBucketPolicyArgs args); + std::future DeleteBucketReplicationAsync( + DeleteBucketReplicationArgs args); + std::future DeleteBucketTagsAsync( + DeleteBucketTagsArgs args); + std::future DeleteObjectLockConfigAsync( + DeleteObjectLockConfigArgs args); + std::future DeleteObjectTagsAsync( + DeleteObjectTagsArgs args); + std::future DisableObjectLegalHoldAsync( + DisableObjectLegalHoldArgs args); + std::future EnableObjectLegalHoldAsync( + EnableObjectLegalHoldArgs args); + std::future GetBucketEncryptionAsync( + GetBucketEncryptionArgs args); + std::future GetBucketLifecycleAsync( + GetBucketLifecycleArgs args); + std::future GetBucketNotificationAsync( + GetBucketNotificationArgs args); + std::future GetBucketPolicyAsync( + GetBucketPolicyArgs args); + std::future GetBucketReplicationAsync( + GetBucketReplicationArgs args); + std::future GetBucketTagsAsync(GetBucketTagsArgs args); + std::future GetBucketVersioningAsync( + GetBucketVersioningArgs args); + std::future GetObjectAsync(GetObjectArgs args); + std::future GetObjectLockConfigAsync( + GetObjectLockConfigArgs args); + std::future GetObjectRetentionAsync( + GetObjectRetentionArgs args); + std::future GetObjectTagsAsync(GetObjectTagsArgs args); + std::future GetPresignedObjectUrlAsync( + GetPresignedObjectUrlArgs args); + std::future GetPresignedPostFormDataAsync( + PostPolicy policy); + std::future IsObjectLegalHoldEnabledAsync( + IsObjectLegalHoldEnabledArgs args); + std::future ListBucketsAsync(ListBucketsArgs args); + std::future ListBucketsAsync(); + std::future ListenBucketNotificationAsync( + ListenBucketNotificationArgs args); + std::future ListObjectsV1Async(ListObjectsV1Args args); + std::future ListObjectsV2Async(ListObjectsV2Args args); + std::future ListObjectVersionsAsync( + ListObjectVersionsArgs args); + std::future MakeBucketAsync(MakeBucketArgs args); + std::future PutObjectAsync(PutObjectApiArgs args); + std::future RemoveBucketAsync(RemoveBucketArgs args); + std::future RemoveObjectAsync(RemoveObjectArgs args); + std::future RemoveObjectsAsync( + RemoveObjectsApiArgs args); + std::future SelectObjectContentAsync( + SelectObjectContentArgs args); + std::future SetBucketEncryptionAsync( + SetBucketEncryptionArgs args); + std::future SetBucketLifecycleAsync( + SetBucketLifecycleArgs args); + std::future SetBucketNotificationAsync( + SetBucketNotificationArgs args); + std::future SetBucketPolicyAsync( + SetBucketPolicyArgs args); + std::future SetBucketReplicationAsync( + SetBucketReplicationArgs args); + std::future SetBucketTagsAsync(SetBucketTagsArgs args); + std::future SetBucketVersioningAsync( + SetBucketVersioningArgs args); + std::future SetObjectLockConfigAsync( + SetObjectLockConfigArgs args); + std::future SetObjectRetentionAsync( + SetObjectRetentionArgs args); + std::future SetObjectTagsAsync(SetObjectTagsArgs args); + std::future StatObjectAsync(StatObjectArgs args); + std::future UploadPartAsync(UploadPartArgs args); + std::future UploadPartCopyAsync( + UploadPartCopyArgs args); + // Windows API fix: // // Windows API headers define `GetObject()` as a macro that expands to either diff --git a/include/miniocpp/client.h b/include/miniocpp/client.h index 563f1542..dd4e3e3a 100644 --- a/include/miniocpp/client.h +++ b/include/miniocpp/client.h @@ -18,6 +18,7 @@ #ifndef MINIO_CPP_CLIENT_H_INCLUDED #define MINIO_CPP_CLIENT_H_INCLUDED +#include #include #include @@ -138,6 +139,20 @@ class Client : public BaseClient { GetObjectResponse GetObject(GetObjectArgs args); UploadObjectResponse UploadObject(UploadObjectArgs args); RemoveObjectsResult RemoveObjects(RemoveObjectsArgs args); + + // Async overloads — return std::future backed by std::async. + // + // Lifetime note for PutObjectAsync: the caller must ensure + // args.stream (if set) outlives the returned std::future, + // exactly as it must for the synchronous PutObject call. + std::future ComposeObjectAsync(ComposeObjectArgs args); + std::future CopyObjectAsync(CopyObjectArgs args); + std::future DownloadObjectAsync( + DownloadObjectArgs args); + std::future GetObjectAsync(GetObjectArgs args); + std::future ListObjectsAsync(ListObjectsArgs args); + std::future PutObjectAsync(PutObjectArgs args); + std::future UploadObjectAsync(UploadObjectArgs args); }; // class Client } // namespace minio::s3 diff --git a/include/miniocpp/types.h b/include/miniocpp/types.h index 54234c06..262bca50 100644 --- a/include/miniocpp/types.h +++ b/include/miniocpp/types.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -191,11 +192,11 @@ constexpr const char* QuoteFieldsToString(QuoteFields qtype) noexcept { } struct CsvInputSerialization { - CompressionType* compression_type = nullptr; + std::shared_ptr compression_type; bool allow_quoted_record_delimiter = false; char comments = 0; char field_delimiter = 0; - FileHeaderInfo* file_header_info = nullptr; + std::shared_ptr file_header_info; char quote_character = 0; char quote_escape_character = 0; char record_delimiter = 0; @@ -205,8 +206,8 @@ struct CsvInputSerialization { }; // struct CsvInputSerialization struct JsonInputSerialization { - CompressionType* compression_type = nullptr; - JsonType* json_type = nullptr; + std::shared_ptr compression_type; + std::shared_ptr json_type; JsonInputSerialization() = default; ~JsonInputSerialization() = default; @@ -221,7 +222,7 @@ struct CsvOutputSerialization { char field_delimiter = 0; char quote_character = 0; char quote_escape_character = 0; - QuoteFields* quote_fields = nullptr; + std::shared_ptr quote_fields; char record_delimiter = 0; CsvOutputSerialization() = default; diff --git a/src/args.cc b/src/args.cc index 2a4bef4a..deb8e022 100644 --- a/src/args.cc +++ b/src/args.cc @@ -466,8 +466,8 @@ error::Error SelectObjectContentArgs::Validate() const { return error::Error("SQL expression must not be empty"); } - if (!((request.csv_input != nullptr) ^ (request.json_input != nullptr) ^ - (request.parquet_input != nullptr))) { + if (((request.csv_input != nullptr) + (request.json_input != nullptr) + + (request.parquet_input != nullptr)) != 1) { return error::Error( "One of CSV, JSON or Parquet input serialization must be set"); } diff --git a/src/baseclient.cc b/src/baseclient.cc index 155a61ac..afac5dce 100644 --- a/src/baseclient.cc +++ b/src/baseclient.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1373,6 +1374,7 @@ MakeBucketResponse BaseClient::MakeBucket(MakeBucketArgs args) { Response resp = Execute(req); if (resp) { + std::unique_lock lock(region_map_mutex_); region_map_[args.bucket] = region; } return MakeBucketResponse(resp); @@ -2049,4 +2051,432 @@ UploadPartCopyResponse BaseClient::UploadPartCopy(UploadPartCopyArgs args) { return resp; } +// ---- Async overloads ---- + +std::future BaseClient::AbortMultipartUploadAsync( + AbortMultipartUploadArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return AbortMultipartUpload(std::move(args)); + }); +} + +std::future BaseClient::BucketExistsAsync( + BucketExistsArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return BucketExists(std::move(args)); + }); +} + +std::future +BaseClient::CompleteMultipartUploadAsync(CompleteMultipartUploadArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return CompleteMultipartUpload(std::move(args)); + }); +} + +std::future +BaseClient::CreateMultipartUploadAsync(CreateMultipartUploadArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return CreateMultipartUpload(std::move(args)); + }); +} + +std::future +BaseClient::DeleteBucketEncryptionAsync(DeleteBucketEncryptionArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return DeleteBucketEncryption(std::move(args)); + }); +} + +std::future +BaseClient::DeleteBucketLifecycleAsync(DeleteBucketLifecycleArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return DeleteBucketLifecycle(std::move(args)); + }); +} + +std::future +BaseClient::DeleteBucketNotificationAsync(DeleteBucketNotificationArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return DeleteBucketNotification(std::move(args)); + }); +} + +std::future BaseClient::DeleteBucketPolicyAsync( + DeleteBucketPolicyArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return DeleteBucketPolicy(std::move(args)); + }); +} + +std::future +BaseClient::DeleteBucketReplicationAsync(DeleteBucketReplicationArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return DeleteBucketReplication(std::move(args)); + }); +} + +std::future BaseClient::DeleteBucketTagsAsync( + DeleteBucketTagsArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return DeleteBucketTags(std::move(args)); + }); +} + +std::future +BaseClient::DeleteObjectLockConfigAsync(DeleteObjectLockConfigArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return DeleteObjectLockConfig(std::move(args)); + }); +} + +std::future BaseClient::DeleteObjectTagsAsync( + DeleteObjectTagsArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return DeleteObjectTags(std::move(args)); + }); +} + +std::future +BaseClient::DisableObjectLegalHoldAsync(DisableObjectLegalHoldArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return DisableObjectLegalHold(std::move(args)); + }); +} + +std::future +BaseClient::EnableObjectLegalHoldAsync(EnableObjectLegalHoldArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return EnableObjectLegalHold(std::move(args)); + }); +} + +std::future BaseClient::GetBucketEncryptionAsync( + GetBucketEncryptionArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetBucketEncryption(std::move(args)); + }); +} + +std::future BaseClient::GetBucketLifecycleAsync( + GetBucketLifecycleArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetBucketLifecycle(std::move(args)); + }); +} + +std::future +BaseClient::GetBucketNotificationAsync(GetBucketNotificationArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetBucketNotification(std::move(args)); + }); +} + +std::future BaseClient::GetBucketPolicyAsync( + GetBucketPolicyArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetBucketPolicy(std::move(args)); + }); +} + +std::future BaseClient::GetBucketReplicationAsync( + GetBucketReplicationArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetBucketReplication(std::move(args)); + }); +} + +std::future BaseClient::GetBucketTagsAsync( + GetBucketTagsArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetBucketTags(std::move(args)); + }); +} + +std::future BaseClient::GetBucketVersioningAsync( + GetBucketVersioningArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetBucketVersioning(std::move(args)); + }); +} + +std::future BaseClient::GetObjectAsync(GetObjectArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetObject(std::move(args)); + }); +} + +std::future BaseClient::GetObjectLockConfigAsync( + GetObjectLockConfigArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetObjectLockConfig(std::move(args)); + }); +} + +std::future BaseClient::GetObjectRetentionAsync( + GetObjectRetentionArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetObjectRetention(std::move(args)); + }); +} + +std::future BaseClient::GetObjectTagsAsync( + GetObjectTagsArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetObjectTags(std::move(args)); + }); +} + +std::future +BaseClient::GetPresignedObjectUrlAsync(GetPresignedObjectUrlArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return GetPresignedObjectUrl(std::move(args)); + }); +} + +std::future +BaseClient::GetPresignedPostFormDataAsync(PostPolicy policy) { + return std::async(std::launch::async, + [this, policy = std::move(policy)]() mutable { + return GetPresignedPostFormData(std::move(policy)); + }); +} + +std::future +BaseClient::IsObjectLegalHoldEnabledAsync(IsObjectLegalHoldEnabledArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return IsObjectLegalHoldEnabled(std::move(args)); + }); +} + +std::future BaseClient::ListBucketsAsync( + ListBucketsArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return ListBuckets(std::move(args)); + }); +} + +std::future BaseClient::ListBucketsAsync() { + return std::async(std::launch::async, [this]() { return ListBuckets(); }); +} + +std::future +BaseClient::ListenBucketNotificationAsync(ListenBucketNotificationArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return ListenBucketNotification(std::move(args)); + }); +} + +std::future BaseClient::ListObjectsV1Async( + ListObjectsV1Args args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return ListObjectsV1(std::move(args)); + }); +} + +std::future BaseClient::ListObjectsV2Async( + ListObjectsV2Args args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return ListObjectsV2(std::move(args)); + }); +} + +std::future BaseClient::ListObjectVersionsAsync( + ListObjectVersionsArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return ListObjectVersions(std::move(args)); + }); +} + +std::future BaseClient::MakeBucketAsync( + MakeBucketArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return MakeBucket(std::move(args)); + }); +} + +std::future BaseClient::PutObjectAsync( + PutObjectApiArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return BaseClient::PutObject(std::move(args)); + }); +} + +std::future BaseClient::RemoveBucketAsync( + RemoveBucketArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return RemoveBucket(std::move(args)); + }); +} + +std::future BaseClient::RemoveObjectAsync( + RemoveObjectArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return RemoveObject(std::move(args)); + }); +} + +std::future BaseClient::RemoveObjectsAsync( + RemoveObjectsApiArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return BaseClient::RemoveObjects(std::move(args)); + }); +} + +// SelectObjectContentArgs owns request by value (deep-copied via +// SelectRequest's shared_ptr chain). Direct move is safe. +std::future BaseClient::SelectObjectContentAsync( + SelectObjectContentArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SelectObjectContent(std::move(args)); + }); +} + +// SetBucketEncryptionArgs owns config by value. +std::future BaseClient::SetBucketEncryptionAsync( + SetBucketEncryptionArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SetBucketEncryption(std::move(args)); + }); +} + +// SetBucketLifecycleArgs owns config by value. +std::future BaseClient::SetBucketLifecycleAsync( + SetBucketLifecycleArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SetBucketLifecycle(std::move(args)); + }); +} + +// SetBucketNotificationArgs owns config by value. +// config. +std::future +BaseClient::SetBucketNotificationAsync(SetBucketNotificationArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SetBucketNotification(std::move(args)); + }); +} + +// SetBucketReplicationArgs owns config by value. +std::future BaseClient::SetBucketReplicationAsync( + SetBucketReplicationArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SetBucketReplication(std::move(args)); + }); +} + +std::future BaseClient::SetBucketPolicyAsync( + SetBucketPolicyArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SetBucketPolicy(std::move(args)); + }); +} + +std::future BaseClient::SetBucketTagsAsync( + SetBucketTagsArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SetBucketTags(std::move(args)); + }); +} + +std::future BaseClient::SetBucketVersioningAsync( + SetBucketVersioningArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SetBucketVersioning(std::move(args)); + }); +} + +std::future BaseClient::SetObjectLockConfigAsync( + SetObjectLockConfigArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SetObjectLockConfig(std::move(args)); + }); +} + +std::future BaseClient::SetObjectRetentionAsync( + SetObjectRetentionArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SetObjectRetention(std::move(args)); + }); +} + +std::future BaseClient::SetObjectTagsAsync( + SetObjectTagsArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return SetObjectTags(std::move(args)); + }); +} + +std::future BaseClient::StatObjectAsync( + StatObjectArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return StatObject(std::move(args)); + }); +} + +std::future BaseClient::UploadPartAsync( + UploadPartArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return UploadPart(std::move(args)); + }); +} + +std::future BaseClient::UploadPartCopyAsync( + UploadPartCopyArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return UploadPartCopy(std::move(args)); + }); +} + } // namespace minio::s3 diff --git a/src/client.cc b/src/client.cc index a543fecd..82388b0c 100644 --- a/src/client.cc +++ b/src/client.cc @@ -1429,4 +1429,58 @@ RemoveObjectsResult Client::RemoveObjects(RemoveObjectsArgs args) { return RemoveObjectsResult(this, std::move(args)); } +// ---- Async overloads ---- + +std::future Client::ComposeObjectAsync( + ComposeObjectArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return ComposeObject(std::move(args)); + }); +} + +std::future Client::CopyObjectAsync(CopyObjectArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return CopyObject(std::move(args)); + }); +} + +std::future Client::DownloadObjectAsync( + DownloadObjectArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return DownloadObject(std::move(args)); + }); +} + +std::future Client::GetObjectAsync(GetObjectArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return Client::GetObject(std::move(args)); + }); +} + +std::future Client::ListObjectsAsync(ListObjectsArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return ListObjects(std::move(args)); + }); +} + +std::future Client::PutObjectAsync(PutObjectArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return PutObject(std::move(args)); + }); +} + +std::future Client::UploadObjectAsync( + UploadObjectArgs args) { + return std::async(std::launch::async, + [this, args = std::move(args)]() mutable { + return UploadObject(std::move(args)); + }); +} + } // namespace minio::s3 diff --git a/tests/tests.cc b/tests/tests.cc index d9c3e255..6eb3c54c 100644 --- a/tests/tests.cc +++ b/tests/tests.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -598,10 +599,12 @@ class Tests { minio::s3::CsvInputSerialization csv_input; minio::s3::FileHeaderInfo file_header_info = minio::s3::FileHeaderInfo::kUse; - csv_input.file_header_info = &file_header_info; + csv_input.file_header_info = + std::make_shared(file_header_info); minio::s3::CsvOutputSerialization csv_output; minio::s3::QuoteFields quote_fields = minio::s3::QuoteFields::kAsNeeded; - csv_output.quote_fields = "e_fields; + csv_output.quote_fields = + std::make_shared(quote_fields); minio::s3::SelectRequest request(expression, &csv_input, &csv_output); try { @@ -780,6 +783,748 @@ class Tests { } } } + + void TestAsyncOperations() { + std::cout << "TestAsyncOperations()" << std::endl; + + // --- async MakeBucket + BucketExists --- + { + std::string bucket_name = RandBucketName(); + + auto remove_bucket_best_effort = [this](const std::string& b) { + try { + minio::s3::RemoveBucketArgs args; + args.bucket = b; + client_.RemoveBucket(args); + } catch (...) { + } + }; + + try { + { + minio::s3::MakeBucketArgs args; + args.bucket = bucket_name; + auto make_fut = client_.MakeBucketAsync(args); + minio::s3::MakeBucketResponse make_resp = make_fut.get(); + if (!make_resp) { + throw std::runtime_error("MakeBucketAsync(): " + + make_resp.Error().String()); + } + } + + { + minio::s3::BucketExistsArgs args; + args.bucket = bucket_name; + auto exists_fut = client_.BucketExistsAsync(args); + minio::s3::BucketExistsResponse exists_resp = exists_fut.get(); + if (!exists_resp) { + throw std::runtime_error("BucketExistsAsync(): " + + exists_resp.Error().String()); + } + if (!exists_resp.exist) { + throw std::runtime_error( + "BucketExistsAsync(): expected bucket to exist"); + } + } + + { + minio::s3::RemoveBucketArgs args; + args.bucket = bucket_name; + auto rm_fut = client_.RemoveBucketAsync(args); + minio::s3::RemoveBucketResponse rm_resp = rm_fut.get(); + if (!rm_resp) { + throw std::runtime_error("RemoveBucketAsync(): " + + rm_resp.Error().String()); + } + } + } catch (...) { + remove_bucket_best_effort(bucket_name); + throw; + } + } + + // --- async PutObject + GetObject + StatObject + RemoveObject --- + { + std::string object_name = RandObjectName(); + std::string data = "TestAsyncOperations"; + { + std::stringstream ss(data); + minio::s3::PutObjectArgs args(ss, static_cast(data.length()), + 0); + args.bucket = bucket_name_; + args.object = object_name; + auto put_fut = client_.PutObjectAsync(std::move(args)); + minio::s3::PutObjectResponse put_resp = put_fut.get(); + if (!put_resp) { + throw std::runtime_error("PutObjectAsync(): " + + put_resp.Error().String()); + } + } + + // async StatObject + try { + { + minio::s3::StatObjectArgs args; + args.bucket = bucket_name_; + args.object = object_name; + auto stat_fut = client_.StatObjectAsync(std::move(args)); + minio::s3::StatObjectResponse stat_resp = stat_fut.get(); + if (!stat_resp) { + throw std::runtime_error("StatObjectAsync(): " + + stat_resp.Error().String()); + } + if (stat_resp.size != data.length()) { + throw std::runtime_error("StatObjectAsync(): expected size: " + + std::to_string(data.length()) + "; got: " + + std::to_string(stat_resp.size)); + } + } + + // async GetObject + { + std::string content; + minio::s3::GetObjectArgs gargs; + gargs.bucket = bucket_name_; + gargs.object = object_name; + gargs.datafunc = + [&content](minio::http::DataFunctionArgs args) -> bool { + content += args.datachunk; + return true; + }; + auto get_fut = client_.GetObjectAsync(std::move(gargs)); + minio::s3::GetObjectResponse get_resp = get_fut.get(); + if (!get_resp) { + throw std::runtime_error("GetObjectAsync(): " + + get_resp.Error().String()); + } + if (data != content) { + throw std::runtime_error("GetObjectAsync(): expected: " + data + + "; got: " + content); + } + } + + // async RemoveObject + { + minio::s3::RemoveObjectArgs args; + args.bucket = bucket_name_; + args.object = object_name; + auto rm_fut = client_.RemoveObjectAsync(std::move(args)); + minio::s3::RemoveObjectResponse rm_resp = rm_fut.get(); + if (!rm_resp) { + throw std::runtime_error("RemoveObjectAsync(): " + + rm_resp.Error().String()); + } + } + } catch (const std::runtime_error&) { + RemoveObject(bucket_name_, object_name); + throw; + } + } + + // --- async ListBuckets --- + { + auto fut = client_.ListBucketsAsync(minio::s3::ListBucketsArgs()); + minio::s3::ListBucketsResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("ListBucketsAsync(): " + + resp.Error().String()); + } + } + + // --- async CopyObject --- + { + std::string src_object = RandObjectName(); + std::string dst_object = RandObjectName(); + std::string data = "CopyObjectAsync-test"; + try { + { + std::stringstream ss(data); + minio::s3::PutObjectArgs args( + ss, static_cast(data.length()), 0); + args.bucket = bucket_name_; + args.object = src_object; + minio::s3::PutObjectResponse resp = client_.PutObject(args); + if (!resp) { + throw std::runtime_error("PutObject(): " + resp.Error().String()); + } + } + + minio::s3::CopySource source; + source.bucket = bucket_name_; + source.object = src_object; + minio::s3::CopyObjectArgs cargs; + cargs.bucket = bucket_name_; + cargs.object = dst_object; + cargs.source = source; + auto copy_fut = client_.CopyObjectAsync(std::move(cargs)); + minio::s3::CopyObjectResponse copy_resp = copy_fut.get(); + if (!copy_resp) { + throw std::runtime_error("CopyObjectAsync(): " + + copy_resp.Error().String()); + } + + RemoveObject(bucket_name_, src_object); + RemoveObject(bucket_name_, dst_object); + } catch (const std::runtime_error&) { + RemoveObject(bucket_name_, src_object); + RemoveObject(bucket_name_, dst_object); + throw; + } + } + + // --- async UploadObject --- + { + std::string data = "UploadObjectAsync-test"; + std::string filename = RandObjectName(); + { + std::ofstream file(filename); + file << data; + } + + std::string object_name = RandObjectName(); + try { + minio::s3::UploadObjectArgs args; + args.bucket = bucket_name_; + args.object = object_name; + args.filename = filename; + auto up_fut = client_.UploadObjectAsync(std::move(args)); + minio::s3::UploadObjectResponse up_resp = up_fut.get(); + if (!up_resp) { + throw std::runtime_error("UploadObjectAsync(): " + + up_resp.Error().String()); + } + std::filesystem::remove(filename); + RemoveObject(bucket_name_, object_name); + } catch (const std::runtime_error&) { + std::filesystem::remove(filename); + RemoveObject(bucket_name_, object_name); + throw; + } + } + + // --- async GetPresignedObjectUrl --- + { + std::string object_name = RandObjectName(); + std::string data = "PresignedAsync"; + std::stringstream ss(data); + { + minio::s3::PutObjectArgs args(ss, static_cast(data.length()), + 0); + args.bucket = bucket_name_; + args.object = object_name; + minio::s3::PutObjectResponse resp = client_.PutObject(args); + if (!resp) { + throw std::runtime_error("PutObject(): " + resp.Error().String()); + } + } + + try { + minio::s3::GetPresignedObjectUrlArgs args; + args.bucket = bucket_name_; + args.object = object_name; + args.method = minio::http::Method::kGet; + auto url_fut = client_.GetPresignedObjectUrlAsync(std::move(args)); + minio::s3::GetPresignedObjectUrlResponse url_resp = url_fut.get(); + if (!url_resp) { + throw std::runtime_error("GetPresignedObjectUrlAsync(): " + + url_resp.Error().String()); + } + if (url_resp.url.empty()) { + throw std::runtime_error( + "GetPresignedObjectUrlAsync(): url is empty"); + } + RemoveObject(bucket_name_, object_name); + } catch (const std::runtime_error&) { + RemoveObject(bucket_name_, object_name); + throw; + } + } + + // --- async Bucket versioning (set + get) --- + { + std::string bucket_name = RandBucketName(); + { + minio::s3::MakeBucketArgs args; + args.bucket = bucket_name; + auto fut = client_.MakeBucketAsync(args); + minio::s3::MakeBucketResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("MakeBucketAsync(): " + + resp.Error().String()); + } + } + + try { + // Set versioning enabled. + { + minio::s3::SetBucketVersioningArgs args; + args.bucket = bucket_name; + minio::s3::Boolean status(true); + args.status = status; + auto fut = client_.SetBucketVersioningAsync(std::move(args)); + minio::s3::SetBucketVersioningResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("SetBucketVersioningAsync(): " + + resp.Error().String()); + } + } + + // Get and verify versioning status. + { + minio::s3::GetBucketVersioningArgs args; + args.bucket = bucket_name; + auto fut = client_.GetBucketVersioningAsync(std::move(args)); + minio::s3::GetBucketVersioningResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("GetBucketVersioningAsync(): " + + resp.Error().String()); + } + if (!resp.status || !resp.status.Get()) { + throw std::runtime_error( + "GetBucketVersioningAsync(): expected Enabled"); + } + } + + { + minio::s3::RemoveBucketArgs args; + args.bucket = bucket_name; + auto fut = client_.RemoveBucketAsync(args); + minio::s3::RemoveBucketResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("RemoveBucketAsync(): " + + resp.Error().String()); + } + } + } catch (const std::runtime_error&) { + minio::s3::RemoveBucketArgs args; + args.bucket = bucket_name; + client_.RemoveBucket(args); + throw; + } + } + + // --- async Object tags (set + get + delete) --- + { + std::string object_name = RandObjectName(); + std::string data = "TagsAsync"; + std::stringstream ss(data); + { + minio::s3::PutObjectArgs args(ss, static_cast(data.length()), + 0); + args.bucket = bucket_name_; + args.object = object_name; + minio::s3::PutObjectResponse resp = client_.PutObject(args); + if (!resp) { + throw std::runtime_error("PutObject(): " + resp.Error().String()); + } + } + + try { + // Set tags. + { + minio::s3::SetObjectTagsArgs args; + args.bucket = bucket_name_; + args.object = object_name; + args.tags = {{"key1", "value1"}, {"key2", "value2"}}; + auto fut = client_.SetObjectTagsAsync(std::move(args)); + minio::s3::SetObjectTagsResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("SetObjectTagsAsync(): " + + resp.Error().String()); + } + } + + // Get and verify tags. + { + minio::s3::GetObjectTagsArgs args; + args.bucket = bucket_name_; + args.object = object_name; + auto fut = client_.GetObjectTagsAsync(std::move(args)); + minio::s3::GetObjectTagsResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("GetObjectTagsAsync(): " + + resp.Error().String()); + } + if (resp.tags.size() != 2 || resp.tags["key1"] != "value1" || + resp.tags["key2"] != "value2") { + throw std::runtime_error("GetObjectTagsAsync(): tag mismatch"); + } + } + + // Delete tags. + { + minio::s3::DeleteObjectTagsArgs args; + args.bucket = bucket_name_; + args.object = object_name; + auto fut = client_.DeleteObjectTagsAsync(std::move(args)); + minio::s3::DeleteObjectTagsResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("DeleteObjectTagsAsync(): " + + resp.Error().String()); + } + } + + RemoveObject(bucket_name_, object_name); + } catch (const std::runtime_error&) { + RemoveObject(bucket_name_, object_name); + throw; + } + } + + // --- async Bucket tags (set + get + delete) --- + { + std::string bucket_name = RandBucketName(); + { + minio::s3::MakeBucketArgs args; + args.bucket = bucket_name; + auto fut = client_.MakeBucketAsync(args); + minio::s3::MakeBucketResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("MakeBucketAsync(): " + + resp.Error().String()); + } + } + + try { + // Set bucket tags. + { + minio::s3::SetBucketTagsArgs args; + args.bucket = bucket_name; + args.tags = {{"department", "engineering"}}; + auto fut = client_.SetBucketTagsAsync(std::move(args)); + minio::s3::SetBucketTagsResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("SetBucketTagsAsync(): " + + resp.Error().String()); + } + } + + // Get and verify bucket tags. + { + minio::s3::GetBucketTagsArgs args; + args.bucket = bucket_name; + auto fut = client_.GetBucketTagsAsync(std::move(args)); + minio::s3::GetBucketTagsResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("GetBucketTagsAsync(): " + + resp.Error().String()); + } + if (resp.tags.size() != 1 || + resp.tags["department"] != "engineering") { + throw std::runtime_error("GetBucketTagsAsync(): tag mismatch"); + } + } + + // Delete bucket tags. + { + minio::s3::DeleteBucketTagsArgs args; + args.bucket = bucket_name; + auto fut = client_.DeleteBucketTagsAsync(std::move(args)); + minio::s3::DeleteBucketTagsResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("DeleteBucketTagsAsync(): " + + resp.Error().String()); + } + } + + { + minio::s3::RemoveBucketArgs args; + args.bucket = bucket_name; + auto fut = client_.RemoveBucketAsync(args); + minio::s3::RemoveBucketResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("RemoveBucketAsync(): " + + resp.Error().String()); + } + } + } catch (const std::runtime_error&) { + minio::s3::RemoveBucketArgs args; + args.bucket = bucket_name; + client_.RemoveBucket(args); + throw; + } + } + + // --- async ListBuckets (no-arg overload) --- + { + auto fut = client_.ListBucketsAsync(); + minio::s3::ListBucketsResponse resp = fut.get(); + if (!resp) { + throw std::runtime_error("ListBucketsAsync() (no-arg): " + + resp.Error().String()); + } + } + + // --- Error path: async call on non-existent object --- + { + minio::s3::StatObjectArgs args; + args.bucket = bucket_name_; + args.object = "__nonexistent_object_async_test__"; + auto fut = client_.StatObjectAsync(std::move(args)); + minio::s3::StatObjectResponse resp = fut.get(); + if (resp) { + throw std::runtime_error( + "StatObjectAsync() on nonexistent object: expected failure"); + } + if (resp.Error().String().empty()) { + throw std::runtime_error( + "StatObjectAsync() on nonexistent object: expected error message"); + } + } + + // --- Concurrency: start multiple async ops before any get() --- + { + std::string b1 = RandBucketName(); + std::string b2 = RandBucketName(); + std::string b3 = RandBucketName(); + + minio::s3::MakeBucketArgs a1, a2, a3; + a1.bucket = b1; + a2.bucket = b2; + a3.bucket = b3; + + auto f1 = client_.MakeBucketAsync(std::move(a1)); + auto f2 = client_.MakeBucketAsync(std::move(a2)); + auto f3 = client_.MakeBucketAsync(std::move(a3)); + + auto cleanup = [this](const std::string& b1, const std::string& b2, + const std::string& b3) { + for (auto& b : {b1, b2, b3}) { + try { + minio::s3::RemoveBucketArgs args; + args.bucket = b; + client_.RemoveBucket(args); + } catch (...) { + } + } + }; + + try { + auto r1 = f1.get(); + auto r2 = f2.get(); + auto r3 = f3.get(); + + if (!r1 || !r2 || !r3) { + cleanup(b1, b2, b3); + throw std::runtime_error("concurrent MakeBucketAsync(): one failed"); + } + + minio::s3::BucketExistsArgs be1, be2, be3; + be1.bucket = b1; + be2.bucket = b2; + be3.bucket = b3; + auto fe1 = client_.BucketExistsAsync(std::move(be1)); + auto fe2 = client_.BucketExistsAsync(std::move(be2)); + auto fe3 = client_.BucketExistsAsync(std::move(be3)); + if (!fe1.get().exist || !fe2.get().exist || !fe3.get().exist) { + cleanup(b1, b2, b3); + throw std::runtime_error( + "concurrent BucketExistsAsync(): expected true"); + } + + cleanup(b1, b2, b3); + } catch (...) { + cleanup(b1, b2, b3); + throw; + } + } + + // --- Reference-holding arg: SetBucketEncryptionAsync --- + { + std::string bucket_name = RandBucketName(); + { + minio::s3::MakeBucketArgs args; + args.bucket = bucket_name; + auto fut = client_.MakeBucketAsync(args); + if (!fut.get()) { + throw std::runtime_error("MakeBucketAsync(): failed"); + } + } + + std::future enc_fut; + { + // SseConfig lives only in this scope; async must own a copy. + minio::s3::SseConfig sse_config = minio::s3::SseConfig::S3(); + minio::s3::SetBucketEncryptionArgs enc_args(sse_config); + enc_args.bucket = bucket_name; + enc_fut = client_.SetBucketEncryptionAsync(std::move(enc_args)); + // sse_config and enc_args go out of scope here. + } + + minio::s3::SetBucketEncryptionResponse enc_resp = enc_fut.get(); + if (!enc_resp) { + std::cout << " SetBucketEncryptionAsync skipped: " + << enc_resp.Error().String() << std::endl; + } + + { + minio::s3::RemoveBucketArgs args; + args.bucket = bucket_name; + client_.RemoveBucket(args); + } + } + + // --- Reference-holding arg: SetBucketLifecycleAsync --- + { + std::string bucket_name = RandBucketName(); + { + minio::s3::MakeBucketArgs args; + args.bucket = bucket_name; + auto fut = client_.MakeBucketAsync(args); + if (!fut.get()) { + throw std::runtime_error("MakeBucketAsync(): failed"); + } + } + + std::future lc_fut; + { + minio::s3::LifecycleConfig lc_config; + minio::s3::SetBucketLifecycleArgs lc_args(lc_config); + lc_args.bucket = bucket_name; + lc_fut = client_.SetBucketLifecycleAsync(std::move(lc_args)); + // lc_config and lc_args go out of scope here. + } + + minio::s3::SetBucketLifecycleResponse lc_resp = lc_fut.get(); + if (!lc_resp) { + std::cout << " SetBucketLifecycleAsync skipped: " + << lc_resp.Error().String() << std::endl; + } + + { + minio::s3::RemoveBucketArgs args; + args.bucket = bucket_name; + client_.RemoveBucket(args); + } + } + + // --- Reference-holding arg: SetBucketNotificationAsync --- + { + std::string bucket_name = RandBucketName(); + { + minio::s3::MakeBucketArgs args; + args.bucket = bucket_name; + auto fut = client_.MakeBucketAsync(args); + if (!fut.get()) { + throw std::runtime_error("MakeBucketAsync(): failed"); + } + } + + std::future notif_fut; + { + minio::s3::NotificationConfig notif_config; + minio::s3::SetBucketNotificationArgs notif_args(notif_config); + notif_args.bucket = bucket_name; + notif_fut = client_.SetBucketNotificationAsync(std::move(notif_args)); + // notif_config and notif_args go out of scope here. + } + + minio::s3::SetBucketNotificationResponse notif_resp = notif_fut.get(); + if (!notif_resp) { + std::cout << " SetBucketNotificationAsync skipped: " + << notif_resp.Error().String() << std::endl; + } + + { + minio::s3::RemoveBucketArgs args; + args.bucket = bucket_name; + client_.RemoveBucket(args); + } + } + + // --- Reference-holding arg: SetBucketReplicationAsync --- + { + std::string bucket_name = RandBucketName(); + { + minio::s3::MakeBucketArgs args; + args.bucket = bucket_name; + auto fut = client_.MakeBucketAsync(args); + if (!fut.get()) { + throw std::runtime_error("MakeBucketAsync(): failed"); + } + } + + std::future repl_fut; + { + minio::s3::ReplicationConfig repl_config; + minio::s3::SetBucketReplicationArgs repl_args(repl_config); + repl_args.bucket = bucket_name; + repl_fut = client_.SetBucketReplicationAsync(std::move(repl_args)); + // repl_config and repl_args go out of scope here. + } + + minio::s3::SetBucketReplicationResponse repl_resp = repl_fut.get(); + if (!repl_resp) { + std::cout << " SetBucketReplicationAsync skipped: " + << repl_resp.Error().String() << std::endl; + } + + { + minio::s3::RemoveBucketArgs args; + args.bucket = bucket_name; + client_.RemoveBucket(args); + } + } + + // --- Reference-holding arg: SelectObjectContentAsync --- + { + std::string object_name = RandObjectName(); + std::string data = + "1997,Ford,E350,\"ac, abs, moon\",3000.00\n" + "1999,Chevy,\"Venture \"\"Extended Edition\"\"\",,4900.00\n"; + std::stringstream ss("Year,Make,Model,Description,Price\n" + data); + { + minio::s3::PutObjectArgs args( + ss, static_cast(ss.str().length()), 0); + args.bucket = bucket_name_; + args.object = object_name; + auto fut = client_.PutObjectAsync(std::move(args)); + if (!fut.get()) { + throw std::runtime_error("PutObject(): failed"); + } + } + + std::future sel_fut; + std::string records; + { + std::string expression = "select * from S3Object"; + minio::s3::CsvInputSerialization csv_input; + minio::s3::FileHeaderInfo file_header_info = + minio::s3::FileHeaderInfo::kUse; + csv_input.file_header_info = + std::make_shared(file_header_info); + minio::s3::CsvOutputSerialization csv_output; + minio::s3::QuoteFields quote_fields = minio::s3::QuoteFields::kAsNeeded; + csv_output.quote_fields = + std::make_shared(quote_fields); + + minio::s3::SelectRequest request(expression, &csv_input, &csv_output); + auto func = [&records](minio::s3::SelectResult result) -> bool { + if (result.err) return false; + records += result.records; + return true; + }; + + minio::s3::SelectObjectContentArgs sel_args(request, func); + sel_args.bucket = bucket_name_; + sel_args.object = object_name; + + sel_fut = client_.SelectObjectContentAsync(std::move(sel_args)); + // csv_input, csv_output, request, etc. go out of scope here. + } + + minio::s3::SelectObjectContentResponse sel_resp = sel_fut.get(); + if (!sel_resp && sel_resp.code == "MethodNotAllowed") { + std::cout << " SelectObjectContentAsync skipped: server does not " + "implement S3 Select" + << std::endl; + } else if (!sel_resp) { + throw std::runtime_error("SelectObjectContentAsync(): " + + sel_resp.Error().String()); + } + RemoveObject(bucket_name_, object_name); + } + } // TestAsyncOperations }; // class Tests int main(int /*argc*/, char* /*argv*/[]) { @@ -838,6 +1583,7 @@ int main(int /*argc*/, char* /*argv*/[]) { tests.RemoveObjects(); tests.SelectObjectContent(); tests.ListenBucketNotification(); + tests.TestAsyncOperations(); return EXIT_SUCCESS; }