/* * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ using Amazon.Runtime.Internal; using System; using Amazon.Util; using System.Xml; using ThirdParty.Json.LitJson; using System.Text; using System.IO; namespace Amazon.Runtime.Internal.Transform { /// /// Response Unmarshaller for all Errors /// public class JsonErrorResponseUnmarshaller : IUnmarshaller { /// /// Build an ErrorResponse from json /// /// The json parsing context. /// Usually an Amazon.Runtime.Internal.JsonUnmarshallerContext. /// An ErrorResponse object. public ErrorResponse Unmarshall(JsonUnmarshallerContext context) { ErrorResponse response; if (context.Peek() == 60) //starts with '<' so assuming XML. { ErrorResponseUnmarshaller xmlUnmarshaller = new ErrorResponseUnmarshaller(); using (var stream = new MemoryStream(context.GetResponseBodyBytes())) { XmlUnmarshallerContext xmlContext = new XmlUnmarshallerContext(stream, false, null); response = xmlUnmarshaller.Unmarshall(xmlContext); } } else { string type; string message; string code; GetValuesFromJsonIfPossible(context, out type, out message, out code); // If an error code was not found, check for the x-amzn-ErrorType header. // This header is returned by rest-json services. if (string.IsNullOrEmpty(type) && context.ResponseData.IsHeaderPresent(HeaderKeys.XAmzErrorType)) { var errorType = context.ResponseData.GetHeaderValue(HeaderKeys.XAmzErrorType); if (!string.IsNullOrEmpty(errorType)) { // The error type can contain additional information, with ":" as a delimiter // We are only interested in the initial part which is the error type var index = errorType.IndexOf(":", StringComparison.Ordinal); if(index != -1) { errorType = errorType.Substring(0, index); } type = errorType; } } // Check for the x-amzn-error-message header. This header is returned by rest-json services. // If the header is present it is preferred over any value provided in the response body. if (context.ResponseData.IsHeaderPresent(HeaderKeys.XAmznErrorMessage)) { var errorMessage = context.ResponseData.GetHeaderValue(HeaderKeys.XAmznErrorMessage); if (!string.IsNullOrEmpty(errorMessage)) message = errorMessage; } // if both "__type" and HeaderKeys.XAmzErrorType were not specified, use "code" as type // this impacts Glacier if (string.IsNullOrEmpty(type) && !string.IsNullOrEmpty(code)) { type = code; } // strip extra data from type, leaving only the exception type name type = type == null ? null : type.Substring(type.LastIndexOf("#", StringComparison.Ordinal) + 1); // if no message was found create a generic message if (string.IsNullOrEmpty(message)) { if (string.IsNullOrEmpty(type)) { if (string.IsNullOrEmpty(context.ResponseBody)) message = "The service returned an error. See inner exception for details."; else message = "The service returned an error with HTTP Body: " + context.ResponseBody; } else { if (string.IsNullOrEmpty(context.ResponseBody)) message = "The service returned an error with Error Code " + type + "."; else message = "The service returned an error with Error Code " + type + " and HTTP Body: " + context.ResponseBody; } } response = new ErrorResponse { Code = type, Message = message, // type is not applicable to JSON services, setting to Unknown Type = ErrorType.Unknown }; } return response; } private static void GetValuesFromJsonIfPossible(JsonUnmarshallerContext context, out string type, out string message, out string code) { code = null; type = null; message = null; while (TryReadContext(context)) { if (context.TestExpression("__type")) { type = StringUnmarshaller.GetInstance().Unmarshall(context); continue; } if (context.TestExpression("message")) { message = StringUnmarshaller.GetInstance().Unmarshall(context); continue; } if (context.TestExpression("code")) { code = StringUnmarshaller.GetInstance().Unmarshall(context); continue; } } } private static bool TryReadContext(JsonUnmarshallerContext context) { try { return context.Read(); } catch (JsonException) { return false; } } private static JsonErrorResponseUnmarshaller instance; /// /// Return an instance of JsonErrorResponseUnmarshaller. /// /// public static JsonErrorResponseUnmarshaller GetInstance() { if (instance == null) instance = new JsonErrorResponseUnmarshaller(); return instance; } } }