1/*
2 * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15
16#include <aws/core/client/AWSErrorMarshaller.h>
17#include <aws/core/utils/logging/LogMacros.h>
18#include <aws/core/utils/json/JsonSerializer.h>
19#include <aws/core/utils/xml/XmlSerializer.h>
20#include <aws/core/utils/StringUtils.h>
21#include <aws/core/client/AWSError.h>
22#include <aws/core/client/CoreErrors.h>
23
24using namespace Aws::Utils::Logging;
25using namespace Aws::Utils::Json;
26using namespace Aws::Utils::Xml;
27using namespace Aws::Http;
28using namespace Aws::Utils;
29using namespace Aws::Client;
30
31static const char AWS_ERROR_MARSHALLER_LOG_TAG[] = "AWSErrorMarshaller";
32AWS_CORE_API extern const char MESSAGE_LOWER_CASE[] = "message";
33AWS_CORE_API extern const char MESSAGE_CAMEL_CASE[] = "Message";
34AWS_CORE_API extern const char ERROR_TYPE_HEADER[] = "x-amzn-ErrorType";
35AWS_CORE_API extern const char TYPE[] = "__type";
36
37AWSError<CoreErrors> JsonErrorMarshaller::Marshall(const Aws::Http::HttpResponse& httpResponse) const
38{
39 JsonValue exceptionPayload(httpResponse.GetResponseBody());
40 JsonView payloadView(exceptionPayload);
41 if (!exceptionPayload.WasParseSuccessful())
42 {
43 return AWSError<CoreErrors>(CoreErrors::UNKNOWN, "", "Failed to parse error payload", false);
44 }
45
46 AWS_LOGSTREAM_TRACE(AWS_ERROR_MARSHALLER_LOG_TAG, "Error response is " << payloadView.WriteReadable());
47
48 Aws::String message(payloadView.ValueExists(MESSAGE_CAMEL_CASE) ? payloadView.GetString(MESSAGE_CAMEL_CASE) :
49 payloadView.ValueExists(MESSAGE_LOWER_CASE) ? payloadView.GetString(MESSAGE_LOWER_CASE) : "");
50
51 if (httpResponse.HasHeader(ERROR_TYPE_HEADER))
52 {
53 return Marshall(httpResponse.GetHeader(ERROR_TYPE_HEADER), message);
54 }
55 else if (payloadView.ValueExists(TYPE))
56 {
57 return Marshall(payloadView.GetString(TYPE), message);
58 }
59 else
60 {
61 return FindErrorByHttpResponseCode(httpResponse.GetResponseCode());
62 }
63}
64
65AWSError<CoreErrors> XmlErrorMarshaller::Marshall(const Aws::Http::HttpResponse& httpResponse) const
66{
67 XmlDocument doc = XmlDocument::CreateFromXmlStream(httpResponse.GetResponseBody());
68 AWS_LOGSTREAM_TRACE(AWS_ERROR_MARSHALLER_LOG_TAG, "Error response is " << doc.ConvertToString());
69 bool errorParsed = false;
70 AWSError<CoreErrors> error;
71 if (doc.WasParseSuccessful())
72 {
73 XmlNode errorNode = doc.GetRootElement();
74 if (errorNode.GetName() != "Error")
75 {
76 errorNode = doc.GetRootElement().FirstChild("Error");
77 }
78 if (errorNode.IsNull())
79 {
80 errorNode = doc.GetRootElement().FirstChild("Errors");
81 if(!errorNode.IsNull())
82 {
83 errorNode = errorNode.FirstChild("Error");
84 }
85 }
86
87 if (!errorNode.IsNull())
88 {
89 XmlNode codeNode = errorNode.FirstChild("Code");
90 XmlNode messageNode = errorNode.FirstChild("Message");
91
92 if (!codeNode.IsNull())
93 {
94 error = Marshall(StringUtils::Trim(codeNode.GetText().c_str()),
95 StringUtils::Trim(messageNode.GetText().c_str()));
96 errorParsed = true;
97 }
98 }
99 }
100
101 if(!errorParsed)
102 {
103 // An error occurred attempting to parse the httpResponse as an XML stream, so we're just
104 // going to dump the XML parsing error and the http response code as a string
105 AWS_LOGSTREAM_WARN(AWS_ERROR_MARSHALLER_LOG_TAG, "Unable to generate a proper httpResponse from the response "
106 "stream. Response code: " << static_cast< uint32_t >(httpResponse.GetResponseCode()));
107 error = FindErrorByHttpResponseCode(httpResponse.GetResponseCode());
108 }
109
110 return error;
111}
112
113AWSError<CoreErrors> AWSErrorMarshaller::Marshall(const Aws::String& exceptionName, const Aws::String& message) const
114{
115 if(exceptionName.empty())
116 {
117 return AWSError<CoreErrors>(CoreErrors::UNKNOWN, "", message, false);
118 }
119
120 auto locationOfPound = exceptionName.find_first_of('#');
121 auto locationOfColon = exceptionName.find_first_of(':');
122 Aws::String formalExceptionName;
123
124 if (locationOfPound != Aws::String::npos)
125 {
126 formalExceptionName = exceptionName.substr(locationOfPound + 1);
127 }
128 else if (locationOfColon != Aws::String::npos)
129 {
130 formalExceptionName = exceptionName.substr(0, locationOfColon);
131 }
132 else
133 {
134 formalExceptionName = exceptionName;
135 }
136
137 AWSError<CoreErrors> error = FindErrorByName(formalExceptionName.c_str());
138 if (error.GetErrorType() != CoreErrors::UNKNOWN)
139 {
140 AWS_LOGSTREAM_WARN(AWS_ERROR_MARSHALLER_LOG_TAG, "Encountered AWSError '" << formalExceptionName.c_str() <<
141 "': " << message.c_str());
142 error.SetExceptionName(formalExceptionName);
143 error.SetMessage(message);
144 return error;
145 }
146
147 AWS_LOGSTREAM_WARN(AWS_ERROR_MARSHALLER_LOG_TAG, "Encountered Unknown AWSError '" << exceptionName.c_str() <<
148 "': " << message.c_str());
149
150 return AWSError<CoreErrors>(CoreErrors::UNKNOWN, exceptionName, "Unable to parse ExceptionName: " + exceptionName + " Message: " + message, false);
151}
152
153AWSError<CoreErrors> AWSErrorMarshaller::FindErrorByName(const char* errorName) const
154{
155 return CoreErrorsMapper::GetErrorForName(errorName);
156}
157
158AWSError<CoreErrors> AWSErrorMarshaller::FindErrorByHttpResponseCode(Aws::Http::HttpResponseCode code) const
159{
160 return CoreErrorsMapper::GetErrorForHttpResponseCode(code);
161}
162