/* * Copyright 2010-2014 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.Util; using Amazon.Util.Internal; using System; using System.Collections.Generic; using System.Net; namespace Amazon.Runtime.Internal { /// <summary> /// This handler processes exceptions thrown from the HTTP handler and /// unmarshalls error responses. /// </summary> public class ErrorHandler : PipelineHandler { /// <summary> /// Default set of exception handlers. /// </summary> private IDictionary<Type, IExceptionHandler> _exceptionHandlers; /// <summary> /// Default set of exception handlers. /// </summary> public IDictionary<Type, IExceptionHandler> ExceptionHandlers { get { return _exceptionHandlers; } } /// <summary> /// Constructor for ErrorHandler. /// </summary> /// <param name="logger">an ILogger instance.</param> public ErrorHandler(ILogger logger) { this.Logger = logger; _exceptionHandlers = new Dictionary<Type, IExceptionHandler> { #if BCL {typeof(WebException), new WebExceptionHandler(this.Logger)}, #endif {typeof(HttpErrorResponseException), new HttpErrorResponseExceptionHandler(this.Logger)} }; } /// <summary> /// Handles and processes any exception thrown from underlying handlers. /// </summary> /// <param name="executionContext">The execution context which contains both the /// requests and response context.</param> public override void InvokeSync(IExecutionContext executionContext) { try { base.InvokeSync(executionContext); return; } catch (Exception exception) { DisposeReponse(executionContext.ResponseContext); bool rethrowOriginalException = ProcessException(executionContext, exception); if (rethrowOriginalException) { throw; } } } #if AWS_ASYNC_API /// <summary> /// Handles and processes any exception thrown from underlying handlers. /// </summary> /// <typeparam name="T">The response type for the current request.</typeparam> /// <param name="executionContext">The execution context, it contains the /// request and response context.</param> /// <returns>A task that represents the asynchronous operation.</returns> public override async System.Threading.Tasks.Task<T> InvokeAsync<T>(IExecutionContext executionContext) { try { return await base.InvokeAsync<T>(executionContext).ConfigureAwait(false); } catch (Exception exception) { DisposeReponse(executionContext.ResponseContext); bool rethrowOriginalException = ProcessException(executionContext, exception); if (rethrowOriginalException) { throw; } } // If response if set and an exception is not rethrown, return the response. // E.g. S3 GetLifecycleConfiguration, GetBucket policy and few other operations // return a 404 which is not returned back as an exception but as a empty response with // error code. if(executionContext.ResponseContext != null && executionContext.ResponseContext.Response != null) { return executionContext.ResponseContext.Response as T; } return null; } #elif AWS_APM_API /// <summary> /// Handles and processes any exception thrown from underlying handlers. /// </summary> /// <param name="executionContext">The execution context, it contains the /// request and response context.</param> protected override void InvokeAsyncCallback(IAsyncExecutionContext executionContext) { var requestContext = executionContext.RequestContext; var responseContext = executionContext.ResponseContext; var exception = responseContext.AsyncResult.Exception; if (exception != null) { try { DisposeReponse(executionContext.ResponseContext); bool rethrow = ProcessException( ExecutionContext.CreateFromAsyncContext(executionContext), exception); // Suppress exception if (!rethrow) responseContext.AsyncResult.Exception = null; } catch (Exception processedException) { // Catch any new exception thrown by ProcessException() responseContext.AsyncResult.Exception = processedException; } } // Call outer handler base.InvokeAsyncCallback(executionContext); } #endif /// <summary> /// Disposes the response body. /// </summary> /// <param name="responseContext">The response context.</param> private static void DisposeReponse(IResponseContext responseContext) { if (responseContext.HttpResponse != null && responseContext.HttpResponse.ResponseBody != null) { responseContext.HttpResponse.ResponseBody.Dispose(); } } /// <summary> /// Processes an exception by invoking a matching exception handler /// for the given exception. /// </summary> /// <param name="executionContext">The execution context, it contains the /// request and response context.</param> /// <param name="exception">The exception to be processed.</param> /// <returns> /// This method returns a boolean value which indicates if the original exception /// should be rethrown. /// This method can also throw a new exception that may be thrown by exception /// processing by a matching exception handler. /// </returns> private bool ProcessException(IExecutionContext executionContext, Exception exception) { // Log the exception this.Logger.Error(exception, "An exception of type {0} was handled in ErrorHandler.", exception.GetType().Name); executionContext.RequestContext.Metrics.AddProperty(Metric.Exception, exception); // Find the matching handler which can process the exception // Start by checking if there is a matching handler for the specific exception type, // if not check for handlers for it's base type till we find a match. var exceptionType = exception.GetType(); var exceptionTypeInfo = TypeFactory.GetTypeInfo(exception.GetType()); do { IExceptionHandler exceptionHandler = null; if (this.ExceptionHandlers.TryGetValue(exceptionType, out exceptionHandler)) { return exceptionHandler.Handle(executionContext, exception); } exceptionType = exceptionTypeInfo.BaseType; exceptionTypeInfo = TypeFactory.GetTypeInfo(exceptionTypeInfo.BaseType); } while (exceptionType != typeof(Exception)); // No match found, rethrow the original exception. return true; } } }