/*
* 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;
}
}
}