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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions generated/src/aws-cpp-sdk-s3/source/S3Errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ static const int NO_SUCH_KEY_HASH = HashingUtils::HashString("NoSuchKey");
static const int OBJECT_NOT_IN_ACTIVE_TIER_HASH = HashingUtils::HashString("ObjectNotInActiveTierError");
static const int BUCKET_ALREADY_EXISTS_HASH = HashingUtils::HashString("BucketAlreadyExists");
static const int INVALID_OBJECT_STATE_HASH = HashingUtils::HashString("InvalidObjectState");
// Wasabi returns this code for transient storage-service failures.
static const int TEMPORARILY_UNAVAILABLE_HASH = HashingUtils::HashString("TemporarilyUnavailable");
// MinIO compatibility error hashes (all MinIO errors handled here for S3 compatibility)
static const int MINIO_INTERNAL_ERROR_HASH = HashingUtils::HashString("InternalError");
static const int MINIO_SERVER_NOT_INITIALIZED_HASH = HashingUtils::HashString("XMinioServerNotInitialized");
Expand Down Expand Up @@ -138,6 +140,10 @@ AWSError<CoreErrors> GetErrorForName(const char* errorName)
{
return AWSError<CoreErrors>(static_cast<CoreErrors>(S3Errors::REQUEST_TIMEOUT), RetryableType::RETRYABLE);
}
else if (hashCode == TEMPORARILY_UNAVAILABLE_HASH)
{
return AWSError<CoreErrors>(CoreErrors::SERVICE_UNAVAILABLE, RetryableType::RETRYABLE);
}
return AWSError<CoreErrors>(CoreErrors::UNKNOWN, false);
}

Expand Down
27 changes: 27 additions & 0 deletions tests/aws-cpp-sdk-s3-unit-tests/S3UnitTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <aws/core/Aws.h>
#include <aws/core/auth/AWSCredentials.h>
#include <aws/core/client/RetryStrategy.h>
#include <aws/core/client/DefaultRetryStrategy.h>
#include <aws/core/utils/HashingUtils.h>
#include <aws/s3/S3Client.h>
#include <aws/s3/model/DeleteObjectsRequest.h>
Expand Down Expand Up @@ -300,6 +301,32 @@ class MockRequest : public Aws::AmazonWebServiceRequest
};

//Set http error and error in body in a way to hit generic xml error marshaller, which sets the exception name
TEST_F(S3UnitTest, TemporarilyUnavailableIsRetryable) {
auto mockRequest = Aws::MakeShared<Standard::StandardHttpRequest>(
ALLOCATION_TAG, "https://mock.example/bucket/key", HttpMethod::HTTP_PUT);
mockRequest->SetResponseStreamFactory([]() -> IOStream* {
return Aws::New<StringStream>(ALLOCATION_TAG);
});
auto mockResponse = Aws::MakeShared<Standard::StandardHttpResponse>(ALLOCATION_TAG, mockRequest);
mockResponse->GetResponseBody()
<< "<Error><Code>TemporarilyUnavailable</Code>"
"<Message>Storage service is temporarily not available.</Message></Error>";
mockResponse->SetResponseCode(HttpResponseCode::SERVICE_UNAVAILABLE);

const auto error = S3ErrorMarshaller().BuildAWSError(mockResponse);
EXPECT_EQ(CoreErrors::SERVICE_UNAVAILABLE, error.GetErrorType());
EXPECT_EQ("TemporarilyUnavailable", error.GetExceptionName());
EXPECT_TRUE(error.ShouldRetry());
EXPECT_TRUE(DefaultRetryStrategy().ShouldRetry(error, 0));
}

TEST_F(S3UnitTest, UnknownS3ErrorRemainsNonRetryable) {
const auto error = S3ErrorMapper::GetErrorForName("UnknownStorageError");
EXPECT_EQ(CoreErrors::UNKNOWN, error.GetErrorType());
EXPECT_FALSE(error.ShouldRetry());
EXPECT_FALSE(S3ErrorMapper::GetErrorForName("NoSuchKey").ShouldRetry());
}

TEST_F(S3UnitTest, S3EmbeddedErrorTestNonOKResponse) {

auto mockRequest = Aws::MakeShared<Standard::StandardHttpRequest>(ALLOCATION_TAG, "mockuri", HttpMethod::HTTP_GET);
Expand Down