/*
* Copyright 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 System;
using Amazon.Runtime.Internal.Transform;
using Amazon.Util;
namespace Amazon.Runtime.Internal
{
///
/// This handler unmarshalls the HTTP response.
///
public class Unmarshaller : PipelineHandler
{
private bool _supportsResponseLogging;
///
/// The constructor for Unmarshaller.
///
///
/// Boolean value which indicated if the unmarshaller
/// handler supports response logging.
///
public Unmarshaller(bool supportsResponseLogging)
{
_supportsResponseLogging = supportsResponseLogging;
}
///
/// Unmarshalls the response returned by the HttpHandler.
///
/// The execution context which contains both the
/// requests and response context.
public override void InvokeSync(IExecutionContext executionContext)
{
base.InvokeSync(executionContext);
if (executionContext.ResponseContext.HttpResponse.IsSuccessStatusCode)
{
// Unmarshall the http response.
Unmarshall(executionContext);
}
}
#if BCL45
///
/// Unmarshalls the response returned by the HttpHandler.
///
/// The response type for the current request.
/// The execution context, it contains the
/// request and response context.
/// A task that represents the asynchronous operation.
public override async System.Threading.Tasks.Task InvokeAsync(IExecutionContext executionContext)
{
await base.InvokeAsync(executionContext).ConfigureAwait(false);
// Unmarshall the response
Unmarshall(executionContext);
return (T)executionContext.ResponseContext.Response;
}
#elif NETSTANDARD
///
/// Unmarshalls the response returned by the HttpHandler.
///
/// The response type for the current request.
/// The execution context, it contains the
/// request and response context.
/// A task that represents the asynchronous operation.
public override async System.Threading.Tasks.Task InvokeAsync(IExecutionContext executionContext)
{
await base.InvokeAsync(executionContext).ConfigureAwait(false);
// Unmarshall the response
await UnmarshallAsync(executionContext).ConfigureAwait(false);
return (T)executionContext.ResponseContext.Response;
}
#elif AWS_APM_API
///
/// Unmarshalls the response returned by the HttpHandler.
///
/// The execution context, it contains the
/// request and response context.
protected override void InvokeAsyncCallback(IAsyncExecutionContext executionContext)
{
// Unmarshall the response if an exception hasn't occured
if (executionContext.ResponseContext.AsyncResult.Exception == null)
{
Unmarshall(ExecutionContext.CreateFromAsyncContext(executionContext));
}
base.InvokeAsyncCallback(executionContext);
}
#endif
///
/// Unmarshalls the HTTP response.
///
///
/// The execution context, it contains the request and response context.
///
private void Unmarshall(IExecutionContext executionContext)
{
var requestContext = executionContext.RequestContext;
var responseContext = executionContext.ResponseContext;
using (requestContext.Metrics.StartEvent(Metric.ResponseProcessingTime))
{
var unmarshaller = requestContext.Unmarshaller;
try
{
var readEntireResponse = _supportsResponseLogging;
var context = unmarshaller.CreateContext(responseContext.HttpResponse,
readEntireResponse,
responseContext.HttpResponse.ResponseBody.OpenResponse(),
requestContext.Metrics,
false,
requestContext);
try
{
var response = UnmarshallResponse(context, requestContext);
responseContext.Response = response;
}
catch(Exception e)
{
// Rethrow Amazon service or client exceptions
if (e is AmazonServiceException ||
e is AmazonClientException)
{
throw;
}
// Else, there was an issue with the response body, throw AmazonUnmarshallingException
var requestId = responseContext.HttpResponse.GetHeaderValue(HeaderKeys.RequestIdHeader);
var body = context.ResponseBody;
throw new AmazonUnmarshallingException(requestId, lastKnownLocation: null, responseBody: body, innerException: e,
statusCode: responseContext.HttpResponse.StatusCode);
}
}
finally
{
if (!unmarshaller.HasStreamingProperty)
responseContext.HttpResponse.ResponseBody.Dispose();
}
}
}
#if NETSTANDARD
///
/// Unmarshalls the HTTP response.
///
///
/// The execution context, it contains the request and response context.
///
private async System.Threading.Tasks.Task UnmarshallAsync(IExecutionContext executionContext)
{
var requestContext = executionContext.RequestContext;
var responseContext = executionContext.ResponseContext;
using (requestContext.Metrics.StartEvent(Metric.ResponseProcessingTime))
{
var unmarshaller = requestContext.Unmarshaller;
try
{
var readEntireResponse = _supportsResponseLogging &&
(requestContext.ClientConfig.LogResponse || requestContext.ClientConfig.ReadEntireResponse
|| AWSConfigs.LoggingConfig.LogResponses != ResponseLoggingOption.Never);
var responseStream = await responseContext.HttpResponse.
ResponseBody.OpenResponseAsync().ConfigureAwait(false);
var context = unmarshaller.CreateContext(responseContext.HttpResponse,
readEntireResponse,
responseStream,
requestContext.Metrics,
false,
requestContext);
var response = UnmarshallResponse(context, requestContext);
responseContext.Response = response;
}
finally
{
if (!unmarshaller.HasStreamingProperty)
responseContext.HttpResponse.ResponseBody.Dispose();
}
}
}
#endif
private AmazonWebServiceResponse UnmarshallResponse(UnmarshallerContext context,
IRequestContext requestContext)
{
try
{
var unmarshaller = requestContext.Unmarshaller;
AmazonWebServiceResponse response = null;
using (requestContext.Metrics.StartEvent(Metric.ResponseUnmarshallTime))
{
response = unmarshaller.UnmarshallResponse(context);
}
requestContext.Metrics.AddProperty(Metric.StatusCode, response.HttpStatusCode);
requestContext.Metrics.AddProperty(Metric.BytesProcessed, response.ContentLength);
if (response.ResponseMetadata != null)
{
requestContext.Metrics.AddProperty(Metric.AWSRequestID, response.ResponseMetadata.RequestId);
}
context.ValidateCRC32IfAvailable();
context.ValidateFlexibleCheckumsIfAvailable(response.ResponseMetadata);
return response;
}
finally
{
var logResponseBody = ShouldLogResponseBody(_supportsResponseLogging, requestContext);
if (logResponseBody)
{
this.Logger.DebugFormat("Received response (truncated to {0} bytes): [{1}]",
AWSConfigs.LoggingConfig.LogResponsesSizeLimit,
context.ResponseBody);
}
}
}
private static bool ShouldLogResponseBody(bool supportsResponseLogging, IRequestContext requestContext)
{
return supportsResponseLogging &&
(requestContext.ClientConfig.LogResponse || AWSConfigs.LoggingConfig.LogResponses == ResponseLoggingOption.Always);
}
}
}