diff --git a/src/aws-cpp-sdk-core/include/aws/core/utils/crypto/PrecalculatedHash.h b/src/aws-cpp-sdk-core/include/aws/core/utils/crypto/PrecalculatedHash.h new file mode 100644 index 00000000000..e3bef38d5be --- /dev/null +++ b/src/aws-cpp-sdk-core/include/aws/core/utils/crypto/PrecalculatedHash.h @@ -0,0 +1,37 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#pragma once + +#include <aws/core/Core_EXPORTS.h> +#include <aws/core/utils/crypto/Hash.h> +#include <aws/core/utils/crypto/HashResult.h> +#include <aws/core/utils/Outcome.h> + +namespace Aws { + namespace Utils { + namespace Crypto { + /** + * A hash implementation that stores a pre-calculated hash + * behind the Hash interface. It is a read through wrapper + * around the checksum value and no hashing are actually done. + */ + class AWS_CORE_API PrecalculatedHash : public Hash { + public: + explicit PrecalculatedHash(const Aws::String &hash); + ~PrecalculatedHash() override; + HashResult Calculate(const Aws::String &str) override; + HashResult Calculate(Aws::IStream &stream) override; + void Update(unsigned char *string, size_t bufferSize) override; + HashResult GetHash() override; + + private: + Aws::String m_hashString; + HashResult m_decodedHashString; + + }; + } + } +} diff --git a/src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp b/src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp index 3f6459ef1c1..ae5407ce76d 100644 --- a/src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp +++ b/src/aws-cpp-sdk-core/source/auth/signer/AWSAuthV4Signer.cpp @@ -237,9 +237,10 @@ bool AWSAuthV4Signer::SignRequest(Aws::Http::HttpRequest& request, const char* r return false; } - if (request.GetRequestHash().second != nullptr) + Aws::String checksumHeaderKey = Aws::String("x-amz-checksum-") + request.GetRequestHash().first; + const auto headers = request.GetHeaders(); + if (request.GetRequestHash().second != nullptr && !request.HasHeader(checksumHeaderKey.c_str())) { - Aws::String checksumHeaderKey = Aws::String("x-amz-checksum-") + request.GetRequestHash().first; Aws::String checksumHeaderValue; if (request.GetRequestHash().first == "sha256") { // we already calculated the payload hash so just reverse the hex string to @@ -247,7 +248,7 @@ bool AWSAuthV4Signer::SignRequest(Aws::Http::HttpRequest& request, const char* r checksumHeaderValue = HashingUtils::Base64Encode(HashingUtils::HexDecode(payloadHash)); } else { // if it is one of the other hashes, we must be careful if there is no content body - auto body = request.GetContentBody(); + const auto& body = request.GetContentBody(); checksumHeaderValue = (body) ? HashingUtils::Base64Encode(request.GetRequestHash().second->Calculate(*body).GetResult()) : HashingUtils::Base64Encode(request.GetRequestHash().second->Calculate({}).GetResult()); @@ -263,8 +264,9 @@ bool AWSAuthV4Signer::SignRequest(Aws::Http::HttpRequest& request, const char* r if (request.GetRequestHash().second != nullptr) { payloadHash = STREAMING_UNSIGNED_PAYLOAD_TRAILER; - Aws::String trailerHeaderValue = Aws::String("x-amz-checksum-") + request.GetRequestHash().first; - request.SetHeaderValue(Http::AWS_TRAILER_HEADER, trailerHeaderValue); + Aws::String checksumHeaderValue = Aws::String("x-amz-checksum-") + request.GetRequestHash().first; + request.DeleteHeader(checksumHeaderValue.c_str()); + request.SetHeaderValue(Http::AWS_TRAILER_HEADER, checksumHeaderValue); request.SetTransferEncoding(CHUNKED_VALUE); request.SetHeaderValue(Http::CONTENT_ENCODING_HEADER, Http::AWS_CHUNKED_VALUE); if (request.HasHeader(Http::CONTENT_LENGTH_HEADER)) { diff --git a/src/aws-cpp-sdk-core/source/client/AWSClient.cpp b/src/aws-cpp-sdk-core/source/client/AWSClient.cpp index 7693e9f1742..11b8be9db47 100644 --- a/src/aws-cpp-sdk-core/source/client/AWSClient.cpp +++ b/src/aws-cpp-sdk-core/source/client/AWSClient.cpp @@ -32,6 +32,7 @@ #include <aws/core/utils/crypto/CRC32.h> #include <aws/core/utils/crypto/Sha256.h> #include <aws/core/utils/crypto/Sha1.h> +#include <aws/core/utils/crypto/PrecalculatedHash.h> #include <aws/core/utils/HashingUtils.h> #include <aws/core/utils/crypto/Factories.h> #include <aws/core/utils/event/EventStream.h> @@ -791,56 +792,70 @@ void AWSClient::AddChecksumToRequest(const std::shared_ptr<Aws::Http::HttpReques request.GetServiceSpecificParameters()->parameterMap.find("overrideChecksumDisable") != request.GetServiceSpecificParameters()->parameterMap.end(); - // Request checksums + //Check if user has provided the checksum algorithm if (!checksumAlgorithmName.empty() && !shouldSkipChecksum) { + // Check if user has provided a checksum value for the specified algorithm + const Aws::String checksumType = "x-amz-checksum-" + checksumAlgorithmName; + const HeaderValueCollection &headers = request.GetHeaders(); + const auto checksumHeader = headers.find(checksumType); + bool checksumValueAndAlgorithmProvided = checksumHeader != headers.end(); + // For non-streaming payload, the resolved checksum location is always header. // For streaming payload, the resolved checksum location depends on whether it is an unsigned payload, we let AwsAuthSigner decide it. - if (checksumAlgorithmName == "crc32") + if (request.IsStreaming() && checksumValueAndAlgorithmProvided) + { + const auto hash = Aws::MakeShared<Crypto::PrecalculatedHash>(AWS_CLIENT_LOG_TAG, checksumHeader->second); + httpRequest->SetRequestHash(checksumAlgorithmName,hash); + } + else if (checksumValueAndAlgorithmProvided){ + httpRequest->SetHeaderValue(checksumType, checksumHeader->second); + } + else if (checksumAlgorithmName == "crc32") { if (request.IsStreaming()) { - httpRequest->SetRequestHash("crc32", Aws::MakeShared<Crypto::CRC32>(AWS_CLIENT_LOG_TAG)); + httpRequest->SetRequestHash(checksumAlgorithmName, Aws::MakeShared<Crypto::CRC32>(AWS_CLIENT_LOG_TAG)); } else { - httpRequest->SetHeaderValue("x-amz-checksum-crc32", HashingUtils::Base64Encode(HashingUtils::CalculateCRC32(*(GetBodyStream(request))))); + httpRequest->SetHeaderValue(checksumType, HashingUtils::Base64Encode(HashingUtils::CalculateCRC32(*(GetBodyStream(request))))); } } else if (checksumAlgorithmName == "crc32c") { if (request.IsStreaming()) { - httpRequest->SetRequestHash("crc32c", Aws::MakeShared<Crypto::CRC32C>(AWS_CLIENT_LOG_TAG)); + httpRequest->SetRequestHash(checksumAlgorithmName, Aws::MakeShared<Crypto::CRC32C>(AWS_CLIENT_LOG_TAG)); } else { - httpRequest->SetHeaderValue("x-amz-checksum-crc32c", HashingUtils::Base64Encode(HashingUtils::CalculateCRC32C(*(GetBodyStream(request))))); + httpRequest->SetHeaderValue(checksumType, HashingUtils::Base64Encode(HashingUtils::CalculateCRC32C(*(GetBodyStream(request))))); } } else if (checksumAlgorithmName == "sha256") { if (request.IsStreaming()) { - httpRequest->SetRequestHash("sha256", Aws::MakeShared<Crypto::Sha256>(AWS_CLIENT_LOG_TAG)); + httpRequest->SetRequestHash(checksumAlgorithmName, Aws::MakeShared<Crypto::Sha256>(AWS_CLIENT_LOG_TAG)); } else { - httpRequest->SetHeaderValue("x-amz-checksum-sha256", HashingUtils::Base64Encode(HashingUtils::CalculateSHA256(*(GetBodyStream(request))))); + httpRequest->SetHeaderValue(checksumType, HashingUtils::Base64Encode(HashingUtils::CalculateSHA256(*(GetBodyStream(request))))); } } else if (checksumAlgorithmName == "sha1") { if (request.IsStreaming()) { - httpRequest->SetRequestHash("sha1", Aws::MakeShared<Crypto::Sha1>(AWS_CLIENT_LOG_TAG)); + httpRequest->SetRequestHash(checksumAlgorithmName, Aws::MakeShared<Crypto::Sha1>(AWS_CLIENT_LOG_TAG)); } else { - httpRequest->SetHeaderValue("x-amz-checksum-sha1", HashingUtils::Base64Encode(HashingUtils::CalculateSHA1(*(GetBodyStream(request))))); + httpRequest->SetHeaderValue(checksumType, HashingUtils::Base64Encode(HashingUtils::CalculateSHA1(*(GetBodyStream(request))))); } } - else if (checksumAlgorithmName == "md5") + else if (checksumAlgorithmName == "md5" && headers.find(CONTENT_MD5_HEADER) == headers.end()) { httpRequest->SetHeaderValue(Http::CONTENT_MD5_HEADER, HashingUtils::Base64Encode(HashingUtils::CalculateMD5(*(GetBodyStream(request))))); } diff --git a/src/aws-cpp-sdk-core/source/http/curl/CurlHttpClient.cpp b/src/aws-cpp-sdk-core/source/http/curl/CurlHttpClient.cpp index b721d5e78da..1db37d0ee22 100644 --- a/src/aws-cpp-sdk-core/source/http/curl/CurlHttpClient.cpp +++ b/src/aws-cpp-sdk-core/source/http/curl/CurlHttpClient.cpp @@ -331,8 +331,11 @@ static size_t ReadBody(char* ptr, size_t size, size_t nmemb, void* userdata, boo chunkedTrailer << "0\r\n"; if (request->GetRequestHash().second != nullptr) { - chunkedTrailer << "x-amz-checksum-" << request->GetRequestHash().first << ":" - << HashingUtils::Base64Encode(request->GetRequestHash().second->GetHash().GetResult()) << "\r\n"; + chunkedTrailer << "x-amz-checksum-" + << request->GetRequestHash().first + << ":" + << HashingUtils::Base64Encode(request->GetRequestHash().second->GetHash().GetResult()) + << "\r\n"; } chunkedTrailer << "\r\n"; amountRead = chunkedTrailer.str().size(); diff --git a/src/aws-cpp-sdk-core/source/utils/crypto/PrecalculatedHash.cpp b/src/aws-cpp-sdk-core/source/utils/crypto/PrecalculatedHash.cpp new file mode 100644 index 00000000000..4cc95cb8e64 --- /dev/null +++ b/src/aws-cpp-sdk-core/source/utils/crypto/PrecalculatedHash.cpp @@ -0,0 +1,37 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + + +#include <aws/core/utils/crypto/PrecalculatedHash.h> +#include <aws/core/utils/HashingUtils.h> + +using namespace Aws::Utils::Crypto; + +PrecalculatedHash::PrecalculatedHash(const Aws::String &hash) : m_hashString(hash), m_decodedHashString(HashingUtils::Base64Decode(hash)) {} + +PrecalculatedHash::~PrecalculatedHash() = default; + +HashResult PrecalculatedHash::Calculate(const Aws::String& str) +{ + AWS_UNREFERENCED_PARAM(str); + return m_decodedHashString; +} + +HashResult PrecalculatedHash::Calculate(Aws::IStream& stream) +{ + AWS_UNREFERENCED_PARAM(stream); + return m_decodedHashString; +} + +void PrecalculatedHash::Update(unsigned char* string, size_t bufferSize) +{ + AWS_UNREFERENCED_PARAM(string); + AWS_UNREFERENCED_PARAM(bufferSize); +} + +HashResult PrecalculatedHash::GetHash() +{ + return m_decodedHashString; +} 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 db0d908fd85..c7399e5f79c 100644 --- a/tests/aws-cpp-sdk-s3-crt-integration-tests/BucketAndObjectOperationTest.cpp +++ b/tests/aws-cpp-sdk-s3-crt-integration-tests/BucketAndObjectOperationTest.cpp @@ -76,6 +76,7 @@ namespace static Aws::String BASE_EVENT_STREAM_TEST_BUCKET_NAME = "eventstream"; static Aws::String BASE_EVENT_STREAM_LARGE_FILE_TEST_BUCKET_NAME = "largeeventstream"; static Aws::String BASE_EVENT_STREAM_ERRORS_IN_EVENT_TEST_BUCKET_NAME = "errorsinevent"; + static Aws::String BASE_CHECKSUMS_BUCKET_NAME = "checksums-crt"; static const char* ALLOCATION_TAG = "BucketAndObjectOperationTest"; static const char* TEST_OBJ_KEY = "TestObjectKey"; static const char* TEST_NOT_MODIFIED_OBJ_KEY = "TestNotModifiedObjectKey"; @@ -113,7 +114,8 @@ namespace std::ref(BASE_ERRORS_TESTING_BUCKET), std::ref(BASE_EVENT_STREAM_TEST_BUCKET_NAME), std::ref(BASE_EVENT_STREAM_LARGE_FILE_TEST_BUCKET_NAME), - std::ref(BASE_EVENT_STREAM_ERRORS_IN_EVENT_TEST_BUCKET_NAME) + std::ref(BASE_EVENT_STREAM_ERRORS_IN_EVENT_TEST_BUCKET_NAME), + std::ref(BASE_CHECKSUMS_BUCKET_NAME) }; for (auto& testBucketName : TEST_BUCKETS) @@ -159,6 +161,7 @@ namespace DeleteBucket(CalculateBucketName(BASE_EVENT_STREAM_TEST_BUCKET_NAME.c_str())); DeleteBucket(CalculateBucketName(BASE_EVENT_STREAM_LARGE_FILE_TEST_BUCKET_NAME.c_str())); DeleteBucket(CalculateBucketName(BASE_EVENT_STREAM_ERRORS_IN_EVENT_TEST_BUCKET_NAME.c_str())); + DeleteBucket(CalculateBucketName(BASE_CHECKSUMS_BUCKET_NAME.c_str())); Client = nullptr; oregonClient = nullptr; @@ -1400,4 +1403,114 @@ namespace ASSERT_FALSE(result.IsSuccess()); ASSERT_EQ((Aws::Client::CoreErrors) result.GetError().GetErrorType(), Aws::Client::CoreErrors::NOT_INITIALIZED); } + + TEST_F(BucketAndObjectOperationTest, PutObjectChecksum) { + struct ChecksumTestCase { + std::function<PutObjectRequest(PutObjectRequest)> chucksumRequestMutator; + HttpResponseCode responseCode; + String body; + }; + + const String fullBucketName = CalculateBucketName(BASE_CHECKSUMS_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); + + Vector<ChecksumTestCase> testCases{ + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32).WithChecksumCRC32("Just runnin' scared each place we go"); + }, + HttpResponseCode::BAD_REQUEST, + "Just runnin' scared each place we go" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA1).WithChecksumSHA1("So afraid that he might show"); + }, + HttpResponseCode::BAD_REQUEST, + "So afraid that he might show" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA256).WithChecksumSHA256("Yeah, runnin' scared, what would I do"); + }, + HttpResponseCode::BAD_REQUEST, + "Yeah, runnin' scared, what would I do" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32C).WithChecksumCRC32C("If he came back and wanted you?"); + }, + HttpResponseCode::BAD_REQUEST, + "If he came back and wanted you?" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithContentMD5("Just runnin' scared, feelin' low"); + }, + HttpResponseCode::BAD_REQUEST, + "Just runnin' scared, feelin' low", + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32) + .WithChecksumCRC32(HashingUtils::Base64Encode(HashingUtils::CalculateCRC32("Runnin' scared, you love him so"))); + }, + HttpResponseCode::OK, + "Runnin' scared, you love him so" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA1) + .WithChecksumSHA1(HashingUtils::Base64Encode(HashingUtils::CalculateSHA1("Just runnin' scared, afraid to lose"))); + }, + HttpResponseCode::OK, + "Just runnin' scared, afraid to lose" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA256) + .WithChecksumSHA256(HashingUtils::Base64Encode(HashingUtils::CalculateSHA256("If he came back, which one would you choose?"))); + }, + HttpResponseCode::OK, + "If he came back, which one would you choose?" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32C) + .WithChecksumCRC32C(HashingUtils::Base64Encode(HashingUtils::CalculateCRC32C("Then all at once he was standing there"))); + }, + HttpResponseCode::OK, + "Then all at once he was standing there" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithContentMD5(HashingUtils::Base64Encode(HashingUtils::CalculateMD5("So sure of himself, his head in the air"))); + }, + HttpResponseCode::OK, + "So sure of himself, his head in the air" + } + }; + + for (const auto&testCase: testCases) { + auto request = testCase.chucksumRequestMutator(PutObjectRequest() + .WithBucket(fullBucketName) + .WithKey("RunningScared")); + std::shared_ptr<IOStream> body = Aws::MakeShared<StringStream>(ALLOCATION_TAG, + testCase.body, + std::ios_base::in | std::ios_base::binary); + request.SetBody(body); + const auto response = Client->PutObject(request); + if (!response.IsSuccess()) { + ASSERT_EQ(testCase.responseCode, response.GetError().GetResponseCode()); + } else { + ASSERT_EQ(testCase.responseCode, HttpResponseCode::OK); + ASSERT_TRUE(response.IsSuccess()); + } + } + } } diff --git a/tests/aws-cpp-sdk-s3-crt-integration-tests/S3ExpressTest.cpp b/tests/aws-cpp-sdk-s3-crt-integration-tests/S3ExpressTest.cpp index 07214fa3aad..8c65f60b8df 100644 --- a/tests/aws-cpp-sdk-s3-crt-integration-tests/S3ExpressTest.cpp +++ b/tests/aws-cpp-sdk-s3-crt-integration-tests/S3ExpressTest.cpp @@ -6,6 +6,7 @@ #include <gtest/gtest.h> #include <aws/testing/AwsTestHelpers.h> #include <aws/core/platform/Environment.h> +#include <aws/core/http/HttpResponse.h> #include <aws/s3-crt/S3CrtClient.h> #include <aws/s3-crt/model/DeleteBucketRequest.h> #include <aws/s3-crt/model/CreateBucketRequest.h> @@ -42,14 +43,15 @@ using namespace Aws; using namespace Aws::Client; +using namespace Aws::Http; using namespace Aws::S3Crt; using namespace Aws::S3Crt::Model; using namespace Aws::Utils; namespace { - static const char* ALLOCATION_TAG = "S3CrtClientS3ExpressTest"; - static const char* S3_EXPRESS_SUFFIX = "--use1-az6--x-s3"; + const char* ALLOCATION_TAG = "S3CrtClientS3ExpressTest"; + const char* S3_EXPRESS_SUFFIX = "--use1-az6--x-s3"; class S3ExpressTest : public ::testing::Test { public: @@ -308,8 +310,10 @@ namespace { return StringUtils::ToLower(Aws::String(UUID::RandomUUID()).c_str()); } - private: + protected: std::shared_ptr<S3CrtClient> client; + + private: Aws::Vector<Aws::String> bucketsToCleanup; }; @@ -379,4 +383,98 @@ namespace { AWS_EXPECT_SUCCESS(abortMPUOutcome); EmptyBucketUtil({bucketName}); } + + TEST_F(S3ExpressTest, PutObjectChecksum) { + struct ChecksumTestCase { + std::function<PutObjectRequest(PutObjectRequest)> chucksumRequestMutator; + HttpResponseCode responseCode; + String body; + }; + + auto bucketName = Testing::GetAwsResourcePrefix() + randomString() + S3_EXPRESS_SUFFIX; + auto createOutcome = CreateBucket(bucketName); + AWS_EXPECT_SUCCESS(createOutcome); + + Vector<ChecksumTestCase> testCases{ + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32).WithChecksumCRC32("Just runnin' scared each place we go"); + }, + HttpResponseCode::BAD_REQUEST, + "Just runnin' scared each place we go" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA1).WithChecksumSHA1("So afraid that he might show"); + }, + HttpResponseCode::BAD_REQUEST, + "So afraid that he might show" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA256).WithChecksumSHA256("Yeah, runnin' scared, what would I do"); + }, + HttpResponseCode::BAD_REQUEST, + "Yeah, runnin' scared, what would I do" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32C).WithChecksumCRC32C("If he came back and wanted you?"); + }, + HttpResponseCode::BAD_REQUEST, + "If he came back and wanted you?" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32) + .WithChecksumCRC32(HashingUtils::Base64Encode(HashingUtils::CalculateCRC32("Runnin' scared, you love him so"))); + }, + HttpResponseCode::OK, + "Runnin' scared, you love him so" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA1) + .WithChecksumSHA1(HashingUtils::Base64Encode(HashingUtils::CalculateSHA1("Just runnin' scared, afraid to lose"))); + }, + HttpResponseCode::OK, + "Just runnin' scared, afraid to lose" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA256) + .WithChecksumSHA256( + HashingUtils::Base64Encode(HashingUtils::CalculateSHA256("If he came back, which one would you choose?"))); + }, + HttpResponseCode::OK, + "If he came back, which one would you choose?" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32C) + .WithChecksumCRC32C(HashingUtils::Base64Encode(HashingUtils::CalculateCRC32C("Then all at once he was standing there"))); + }, + HttpResponseCode::OK, + "Then all at once he was standing there" + } + }; + + for (const auto&testCase: testCases) { + auto request = testCase.chucksumRequestMutator(PutObjectRequest() + .WithBucket(bucketName) + .WithKey("RunningScared")); + std::shared_ptr<IOStream> body = Aws::MakeShared<StringStream>(ALLOCATION_TAG, + testCase.body, + std::ios_base::in | std::ios_base::binary); + request.SetBody(body); + const auto response = client->PutObject(request); + if (!response.IsSuccess()) { + ASSERT_EQ(testCase.responseCode, response.GetError().GetResponseCode()); + } + else { + ASSERT_EQ(testCase.responseCode, HttpResponseCode::OK); + ASSERT_TRUE(response.IsSuccess()); + } + } + } } diff --git a/tests/aws-cpp-sdk-s3-integration-tests/BucketAndObjectOperationTest.cpp b/tests/aws-cpp-sdk-s3-integration-tests/BucketAndObjectOperationTest.cpp index 2cada4743ae..727a349397c 100644 --- a/tests/aws-cpp-sdk-s3-integration-tests/BucketAndObjectOperationTest.cpp +++ b/tests/aws-cpp-sdk-s3-integration-tests/BucketAndObjectOperationTest.cpp @@ -58,6 +58,7 @@ #include <aws/core/http/standard/StandardHttpRequest.h> + using namespace Aws; using namespace Aws::Http::Standard; using namespace Aws::Auth; @@ -2406,4 +2407,114 @@ namespace PutObjectOutcome putObjectOutcome = Client->PutObject(putObjectRequest); AWS_ASSERT_SUCCESS(putObjectOutcome); } + + TEST_F(BucketAndObjectOperationTest, PutObjectChecksum) { + struct ChecksumTestCase { + std::function<PutObjectRequest(PutObjectRequest)> chucksumRequestMutator; + HttpResponseCode responseCode; + String body; + }; + + const String fullBucketName = CalculateBucketName(BASE_CHECKSUMS_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); + + Vector<ChecksumTestCase> testCases{ + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32).WithChecksumCRC32("Just runnin' scared each place we go"); + }, + HttpResponseCode::BAD_REQUEST, + "Just runnin' scared each place we go" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA1).WithChecksumSHA1("So afraid that he might show"); + }, + HttpResponseCode::BAD_REQUEST, + "So afraid that he might show" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA256).WithChecksumSHA256("Yeah, runnin' scared, what would I do"); + }, + HttpResponseCode::BAD_REQUEST, + "Yeah, runnin' scared, what would I do" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32C).WithChecksumCRC32C("If he came back and wanted you?"); + }, + HttpResponseCode::BAD_REQUEST, + "If he came back and wanted you?" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithContentMD5("Just runnin' scared, feelin' low"); + }, + HttpResponseCode::BAD_REQUEST, + "Just runnin' scared, feelin' low", + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32) + .WithChecksumCRC32(HashingUtils::Base64Encode(HashingUtils::CalculateCRC32("Runnin' scared, you love him so"))); + }, + HttpResponseCode::OK, + "Runnin' scared, you love him so" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA1) + .WithChecksumSHA1(HashingUtils::Base64Encode(HashingUtils::CalculateSHA1("Just runnin' scared, afraid to lose"))); + }, + HttpResponseCode::OK, + "Just runnin' scared, afraid to lose" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA256) + .WithChecksumSHA256(HashingUtils::Base64Encode(HashingUtils::CalculateSHA256("If he came back, which one would you choose?"))); + }, + HttpResponseCode::OK, + "If he came back, which one would you choose?" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32C) + .WithChecksumCRC32C(HashingUtils::Base64Encode(HashingUtils::CalculateCRC32C("Then all at once he was standing there"))); + }, + HttpResponseCode::OK, + "Then all at once he was standing there" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithContentMD5(HashingUtils::Base64Encode(HashingUtils::CalculateMD5("So sure of himself, his head in the air"))); + }, + HttpResponseCode::OK, + "So sure of himself, his head in the air" + } + }; + + for (const auto&testCase: testCases) { + auto request = testCase.chucksumRequestMutator(PutObjectRequest() + .WithBucket(fullBucketName) + .WithKey("RunningScared")); + std::shared_ptr<IOStream> body = Aws::MakeShared<StringStream>(ALLOCATION_TAG, + testCase.body, + std::ios_base::in | std::ios_base::binary); + request.SetBody(body); + const auto response = Client->PutObject(request); + if (!response.IsSuccess()) { + ASSERT_EQ(testCase.responseCode, response.GetError().GetResponseCode()); + } else { + ASSERT_EQ(testCase.responseCode, HttpResponseCode::OK); + ASSERT_TRUE(response.IsSuccess()); + } + } + } } diff --git a/tests/aws-cpp-sdk-s3-integration-tests/S3ExpressTest.cpp b/tests/aws-cpp-sdk-s3-integration-tests/S3ExpressTest.cpp index 269eb7007d5..bacf812260f 100644 --- a/tests/aws-cpp-sdk-s3-integration-tests/S3ExpressTest.cpp +++ b/tests/aws-cpp-sdk-s3-integration-tests/S3ExpressTest.cpp @@ -6,6 +6,7 @@ #include <gtest/gtest.h> #include <aws/testing/AwsTestHelpers.h> #include <aws/core/platform/Environment.h> +#include <aws/core/http/HttpResponse.h> #include <aws/s3/S3Client.h> #include <aws/s3/model/DeleteBucketRequest.h> #include <aws/s3/model/CreateBucketRequest.h> @@ -42,14 +43,15 @@ using namespace Aws; using namespace Aws::Client; +using namespace Aws::Http; using namespace Aws::S3; using namespace Aws::S3::Model; using namespace Aws::Utils; namespace { - static const char* ALLOCATION_TAG = "S3ClientS3ExpressTest"; - static const char* S3_EXPRESS_SUFFIX = "--use1-az6--x-s3"; + const char* ALLOCATION_TAG = "S3ClientS3ExpressTest"; + const char* S3_EXPRESS_SUFFIX = "--use1-az6--x-s3"; class S3ExpressTest : public ::testing::Test { public: @@ -308,8 +310,10 @@ namespace { return StringUtils::ToLower(Aws::String(UUID::RandomUUID()).c_str()); } - private: + protected: std::shared_ptr<S3Client> client; + + private: Aws::Vector<Aws::String> bucketsToCleanup; }; @@ -379,4 +383,98 @@ namespace { AWS_EXPECT_SUCCESS(abortMPUOutcome); EmptyBucketUtil({bucketName}); } + + TEST_F(S3ExpressTest, PutObjectChecksum) { + struct ChecksumTestCase { + std::function<PutObjectRequest(PutObjectRequest)> chucksumRequestMutator; + HttpResponseCode responseCode; + String body; + }; + + auto bucketName = Testing::GetAwsResourcePrefix() + randomString() + S3_EXPRESS_SUFFIX; + auto createOutcome = CreateBucket(bucketName); + AWS_EXPECT_SUCCESS(createOutcome); + + Vector<ChecksumTestCase> testCases{ + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32).WithChecksumCRC32("Just runnin' scared each place we go"); + }, + HttpResponseCode::BAD_REQUEST, + "Just runnin' scared each place we go" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA1).WithChecksumSHA1("So afraid that he might show"); + }, + HttpResponseCode::BAD_REQUEST, + "So afraid that he might show" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA256).WithChecksumSHA256("Yeah, runnin' scared, what would I do"); + }, + HttpResponseCode::BAD_REQUEST, + "Yeah, runnin' scared, what would I do" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32C).WithChecksumCRC32C("If he came back and wanted you?"); + }, + HttpResponseCode::BAD_REQUEST, + "If he came back and wanted you?" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32) + .WithChecksumCRC32(HashingUtils::Base64Encode(HashingUtils::CalculateCRC32("Runnin' scared, you love him so"))); + }, + HttpResponseCode::OK, + "Runnin' scared, you love him so" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA1) + .WithChecksumSHA1(HashingUtils::Base64Encode(HashingUtils::CalculateSHA1("Just runnin' scared, afraid to lose"))); + }, + HttpResponseCode::OK, + "Just runnin' scared, afraid to lose" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::SHA256) + .WithChecksumSHA256( + HashingUtils::Base64Encode(HashingUtils::CalculateSHA256("If he came back, which one would you choose?"))); + }, + HttpResponseCode::OK, + "If he came back, which one would you choose?" + }, + { + [](PutObjectRequest request) -> PutObjectRequest { + return request.WithChecksumAlgorithm(ChecksumAlgorithm::CRC32C) + .WithChecksumCRC32C(HashingUtils::Base64Encode(HashingUtils::CalculateCRC32C("Then all at once he was standing there"))); + }, + HttpResponseCode::OK, + "Then all at once he was standing there" + } + }; + + for (const auto&testCase: testCases) { + auto request = testCase.chucksumRequestMutator(PutObjectRequest() + .WithBucket(bucketName) + .WithKey("RunningScared")); + std::shared_ptr<IOStream> body = Aws::MakeShared<StringStream>(ALLOCATION_TAG, + testCase.body, + std::ios_base::in | std::ios_base::binary); + request.SetBody(body); + const auto response = client->PutObject(request); + if (!response.IsSuccess()) { + ASSERT_EQ(testCase.responseCode, response.GetError().GetResponseCode()); + } + else { + ASSERT_EQ(testCase.responseCode, HttpResponseCode::OK); + ASSERT_TRUE(response.IsSuccess()); + } + } + } }