Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: handle errors in 200 response from S3 #1266

Merged
merged 11 commits into from
Jan 4, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation
import XCTest
import AWSS3
import AWSClientRuntime
import AwsCommonRuntimeKit
import ClientRuntime

public class MockHttpClientEngine: HttpClientEngine {

// Public initializer
public init() {}

func successHttpResponse(request: SdkHttpRequest) -> HttpResponse {
let errorResponsePayload = """
<Error>
<Code>SlowDown</Code>
<Message>Please reduce your request rate.</Message>
<RequestId>K2H6N7ZGQT6WHCEG</RequestId>
<HostId>WWoZlnK4pTjKCYn6eNV7GgOurabfqLkjbSyqTvDMGBaI9uwzyNhSaDhOCPs8paFGye7S6b/AB3A=</HostId>
</Error>
"""
return HttpResponse(
headers: request.headers,
body: ByteStream.data(errorResponsePayload.data(using: .utf8)),
statusCode: HttpStatusCode.ok
)
}

public func execute(request: SdkHttpRequest) async throws -> HttpResponse {
return successHttpResponse(request: request)
}
}

class S3ErrorIn200Test: XCTestCase {

override class func setUp() {
AwsCommonRuntimeKit.CommonRuntimeKit.initialize()
}

/// S3Client throws expected error in response (200) with <Error> tag
func test_foundExpectedError() async throws {
let config = try await S3Client.S3ClientConfiguration(region: "us-west-2")
config.httpClientEngine = MockHttpClientEngine()
let client = S3Client(config: config)

do {
// any method on S3Client where the output shape doesnt have a stream
_ = try await client.listBuckets(input: .init())
XCTFail("Expected an error to be thrown, but it was not.")
} catch let error as UnknownAWSHTTPServiceError {
// check for the error we added in our mock client
XCTAssertEqual("Please reduce your request rate.", error.message)
} catch {
XCTFail("Unexpected error: \(error)")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import ClientRuntime

public struct AWSS3ErrorWith200StatusXMLMiddleware<OperationStackOutput>: Middleware {
public let id: String = "AWSS3ErrorWith200StatusXMLMiddleware"

public init() {}

public func handle<H>(context: Context,
input: SdkHttpRequest,
next: H) async throws -> OperationOutput<OperationStackOutput>
where H: Handler,
Self.MInput == H.Input,
Self.MOutput == H.Output,
Self.Context == H.Context {

// Let the next handler in the chain process the input
let response = try await next.handle(context: context, input: input)

// Check if the status code is OK (200)
guard response.httpResponse.statusCode == .ok else {
return response
}

// Check if the response body contains an XML Error
guard let data = try await response.httpResponse.body.readData() else {
return response
}

let xmlString = String(data: data, encoding: .utf8) ?? ""
if xmlString.contains("<Error>") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer not working with XML data directly.

This can stay for now, but as part of XML project I'll look at how we can do this within existing XML handling.

// Handle the error as a 500 Internal Server Error
var modifiedResponse = response
modifiedResponse.httpResponse.statusCode = .internalServerError
return modifiedResponse
}

return response
}

public typealias MInput = SdkHttpRequest
public typealias MOutput = OperationOutput<OperationStackOutput>
public typealias Context = HttpContext
}
Loading
Loading