diff --git a/src/aws-cpp-sdk-core/include/aws/core/client/AWSErrorMarshaller.h b/src/aws-cpp-sdk-core/include/aws/core/client/AWSErrorMarshaller.h index 6b0bc2d03d9..eda113ed3e9 100644 --- a/src/aws-cpp-sdk-core/include/aws/core/client/AWSErrorMarshaller.h +++ b/src/aws-cpp-sdk-core/include/aws/core/client/AWSErrorMarshaller.h @@ -73,7 +73,8 @@ namespace Aws */ virtual Aws::String ExtractEndpoint(const AWSError&) const { return {}; } protected: - AWSError Marshall(const Aws::String& exceptionName, const Aws::String& message) const; + virtual AWSError Marshall(const Aws::String& exceptionName, const Aws::String& message) const; + virtual void MarshallError(AWSError&, const Http::HttpResponse&) const {}; }; class AWS_CORE_API JsonErrorMarshaller : public AWSErrorMarshaller @@ -91,16 +92,12 @@ namespace Aws protected: const Aws::Utils::Json::JsonValue& GetJsonPayloadFromError(const AWSError&) const; + static Aws::Utils::Json::JsonValue GetJsonPayloadHttpResponse(const Http::HttpResponse& httpResponse); }; class AWS_CORE_API JsonErrorMarshallerQueryCompatible : public JsonErrorMarshaller { - public: - /** - * Converts an exceptionName and message into an Error object, if it - * can be parsed. Otherwise, it returns and AWSError with - * CoreErrors::UNKNOWN as the error type. - */ - AWSError Marshall(const Aws::Http::HttpResponse& response) const override; + protected: + void MarshallError(AWSError&, const Http::HttpResponse&) const override; }; class AWS_CORE_API XmlErrorMarshaller : public AWSErrorMarshaller { diff --git a/src/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp b/src/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp index 418b2cf1e5a..53c1e7026d8 100644 --- a/src/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp +++ b/src/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp @@ -40,14 +40,18 @@ static CoreErrors GuessBodylessErrorType(const Aws::Http::HttpResponseCode respo } } -AWSError JsonErrorMarshaller::Marshall(const Aws::Http::HttpResponse& httpResponse) const { +JsonValue JsonErrorMarshaller::GetJsonPayloadHttpResponse(const Http::HttpResponse& httpResponse) { Aws::StringStream memoryStream; std::copy(std::istreambuf_iterator(httpResponse.GetResponseBody()), std::istreambuf_iterator(), std::ostreambuf_iterator(memoryStream)); Aws::String rawPayloadStr = memoryStream.str(); - JsonValue exceptionPayload(rawPayloadStr); - JsonView payloadView(exceptionPayload); + return JsonValue(rawPayloadStr); +} + +AWSError JsonErrorMarshaller::Marshall(const Aws::Http::HttpResponse& httpResponse) const { + auto exceptionPayload = GetJsonPayloadHttpResponse(httpResponse); + auto payloadView = JsonView(exceptionPayload); AWSError error; if (exceptionPayload.WasParseSuccessful()) { AWS_LOGSTREAM_TRACE(AWS_ERROR_MARSHALLER_LOG_TAG, "Error response is " << payloadView.WriteReadable()); @@ -68,10 +72,12 @@ AWSError JsonErrorMarshaller::Marshall(const Aws::Http::HttpResponse } else { bool isRetryable = IsRetryableHttpResponseCode(httpResponse.GetResponseCode()); AWS_LOGSTREAM_ERROR(AWS_ERROR_MARSHALLER_LOG_TAG, - "Failed to parse error payload: " << httpResponse.GetResponseCode() << ": " << rawPayloadStr); - error = AWSError(CoreErrors::UNKNOWN, "", "Failed to parse error payload: " + rawPayloadStr, isRetryable); + "Failed to parse error payload: " << httpResponse.GetResponseCode() << ": " << payloadView.AsString()); + error = AWSError(CoreErrors::UNKNOWN, "", "Failed to parse error payload: " + payloadView.AsString(), isRetryable); } + MarshallError(error, httpResponse); + error.SetRequestId(httpResponse.HasHeader(REQUEST_ID_HEADER) ? httpResponse.GetHeader(REQUEST_ID_HEADER) : ""); error.SetJsonPayload(std::move(exceptionPayload)); return error; @@ -249,79 +255,46 @@ AWSError AWSErrorMarshaller::FindErrorByHttpResponseCode(Aws::Http:: return CoreErrorsMapper::GetErrorForHttpResponseCode(code); } -AWSError JsonErrorMarshallerQueryCompatible::Marshall(const Aws::Http::HttpResponse& httpResponse) const { - Aws::StringStream memoryStream; - std::copy(std::istreambuf_iterator(httpResponse.GetResponseBody()), std::istreambuf_iterator(), - std::ostreambuf_iterator(memoryStream)); - Aws::String rawPayloadStr = memoryStream.str(); - - JsonValue exceptionPayload(rawPayloadStr); - JsonView payloadView(exceptionPayload); - AWSError error; - if (exceptionPayload.WasParseSuccessful()) { - AWS_LOGSTREAM_TRACE(AWS_ERROR_MARSHALLER_LOG_TAG, "Error response is " << payloadView.WriteReadable()); - - Aws::String message(payloadView.ValueExists(MESSAGE_CAMEL_CASE) ? payloadView.GetString(MESSAGE_CAMEL_CASE) - : payloadView.ValueExists(MESSAGE_LOWER_CASE) ? payloadView.GetString(MESSAGE_LOWER_CASE) - : ""); +void JsonErrorMarshallerQueryCompatible::MarshallError(AWSError& error, const Http::HttpResponse& httpResponse) const { + if (!error.GetExceptionName().empty()) { + auto exceptionPayload = GetJsonPayloadHttpResponse(httpResponse); + auto payloadView = JsonView(exceptionPayload); + /* + AWS Query-Compatible mode: This is a special setting that allows + certain AWS services to communicate using a specific "query" + format, which can send customized error codes. Users are divided + into different groups based on how they communicate with the + service: Group #1: Users using the AWS Query format, receiving + custom error codes. Group #2: Users using the regular AWS JSON + format without the trait, receiving standard error codes. Group #3: + Users using the AWS JSON format with the trait, receiving custom + error codes. + + The header "x-amzn-query-error" shouldn't be present if it's not + awsQueryCompatible, so added checks for it. + */ + + if (httpResponse.HasHeader(QUERY_ERROR_HEADER)) { + auto errorCodeString = httpResponse.GetHeader(QUERY_ERROR_HEADER); + auto locationOfSemicolon = errorCodeString.find_first_of(';'); + Aws::String errorCode; + + if (locationOfSemicolon != Aws::String::npos) { + errorCode = errorCodeString.substr(0, locationOfSemicolon); + } else { + errorCode = errorCodeString; + } - if (httpResponse.HasHeader(ERROR_TYPE_HEADER)) { - error = AWSErrorMarshaller::Marshall(httpResponse.GetHeader(ERROR_TYPE_HEADER), message); - } else if (payloadView.ValueExists(TYPE)) { - error = AWSErrorMarshaller::Marshall(payloadView.GetString(TYPE), message); - } else { - error = FindErrorByHttpResponseCode(httpResponse.GetResponseCode()); - error.SetMessage(message); + error.SetExceptionName(errorCode); } - - if (!error.GetExceptionName().empty()) { - /* - AWS Query-Compatible mode: This is a special setting that allows - certain AWS services to communicate using a specific "query" - format, which can send customized error codes. Users are divided - into different groups based on how they communicate with the - service: Group #1: Users using the AWS Query format, receiving - custom error codes. Group #2: Users using the regular AWS JSON - format without the trait, receiving standard error codes. Group #3: - Users using the AWS JSON format with the trait, receiving custom - error codes. - - The header "x-amzn-query-error" shouldn't be present if it's not - awsQueryCompatible, so added checks for it. - */ - - if (httpResponse.HasHeader(QUERY_ERROR_HEADER)) { - auto errorCodeString = httpResponse.GetHeader(QUERY_ERROR_HEADER); - auto locationOfSemicolon = errorCodeString.find_first_of(';'); - Aws::String errorCode; - - if (locationOfSemicolon != Aws::String::npos) { - errorCode = errorCodeString.substr(0, locationOfSemicolon); - } else { - errorCode = errorCodeString; - } - - error.SetExceptionName(errorCode); - } - // check for exception name from payload field 'type' - else if (payloadView.ValueExists(TYPE)) { - // handle missing header and parse code from message - const auto& typeStr = payloadView.GetString(TYPE); - auto locationOfPound = typeStr.find_first_of('#'); - if (locationOfPound != Aws::String::npos) { - error.SetExceptionName(typeStr.substr(locationOfPound + 1)); - } + // check for exception name from payload field 'type' + else if (payloadView.ValueExists(TYPE)) { + // handle missing header and parse code from message + const auto& typeStr = payloadView.GetString(TYPE); + auto locationOfPound = typeStr.find_first_of('#'); + if (locationOfPound != Aws::String::npos) { + error.SetExceptionName(typeStr.substr(locationOfPound + 1)); } } - - } else { - bool isRetryable = IsRetryableHttpResponseCode(httpResponse.GetResponseCode()); - AWS_LOGSTREAM_ERROR(AWS_ERROR_MARSHALLER_LOG_TAG, - "Failed to parse error payload: " << httpResponse.GetResponseCode() << ": " << rawPayloadStr); - error = AWSError(CoreErrors::UNKNOWN, "", "Failed to parse error payload: " + rawPayloadStr, isRetryable); } - - error.SetRequestId(httpResponse.HasHeader(REQUEST_ID_HEADER) ? httpResponse.GetHeader(REQUEST_ID_HEADER) : ""); - error.SetJsonPayload(std::move(exceptionPayload)); - return error; }