/* * 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 Amazon.Runtime.Internal.Util; using System; namespace Amazon.Runtime.Internal { /// /// An abstract pipeline handler that has implements IPipelineHandler, /// and has the default implmentation. This is the base class for most of /// the handler implementations. /// public abstract partial class PipelineHandler : IPipelineHandler { /// /// The logger used to log messages. /// public virtual ILogger Logger { get; set; } /// /// The inner handler which is called after the current /// handler completes it's processing. /// public IPipelineHandler InnerHandler { get; set; } /// /// The outer handler which encapsulates the current handler. /// public IPipelineHandler OuterHandler { get; set; } /// /// Contains the processing logic for a synchronous request invocation. /// This method calls InnerHandler.InvokeSync to continue processing of the /// request by the pipeline. /// /// The execution context which contains both the /// requests and response context. // [System.Diagnostics.DebuggerHidden] public virtual void InvokeSync(IExecutionContext executionContext) { if (this.InnerHandler != null) { InnerHandler.InvokeSync(executionContext); return; } throw new InvalidOperationException("Cannot invoke InnerHandler. InnerHandler is not set."); } #if AWS_APM_API /// /// Contains the processing logic for an asynchronous request invocation. /// This method should calls InnerHandler.InvokeSync to continue processing of the /// request by the pipeline. /// /// The execution context which contains both the /// requests and response context. /// IAsyncResult which represent an async operation. [System.Diagnostics.DebuggerHidden] public virtual IAsyncResult InvokeAsync(IAsyncExecutionContext executionContext) { if (this.InnerHandler != null) { return InnerHandler.InvokeAsync(executionContext); } throw new InvalidOperationException("Cannot invoke InnerHandler. InnerHandler is not set."); } /// /// This callback method is called by the callback method of the inner handler /// as part of asynchronous processing after any underlying asynchronous /// operations complete. /// /// The execution context, it contains the /// request and response context. [System.Diagnostics.DebuggerHidden] public void AsyncCallback(IAsyncExecutionContext executionContext) { try { this.InvokeAsyncCallback(executionContext); } catch (Exception exception) { // Log exception this.Logger.Error(exception, "An exception of type {0} was thrown from InvokeAsyncCallback().", exception.GetType().Name); executionContext.RequestContext.Metrics.AddProperty(Metric.Exception, exception); // An unhandled exception occured in the callback implementation. // Capture the exception and end the callback processing by signalling the // wait handle. executionContext.RequestContext.Metrics.StopEvent(Metric.ClientExecuteTime); LogMetrics(ExecutionContext.CreateFromAsyncContext(executionContext)); // Sets the exception on the AsyncResult, signals the wait handle and calls the user callback. executionContext.ResponseContext.AsyncResult.HandleException(exception); } } /// /// This callback method contains the processing logic that should be executed /// after the underlying asynchronous operation completes. /// This method is called as part of a callback chain which starts /// from the InvokeAsyncCallback method of the bottommost handler and then invokes /// each callback method of the handler above it. /// This method calls OuterHandler.AsyncCallback to continue processing of the /// request by the pipeline, unless it's the topmost handler. /// /// The execution context, it contains the /// request and response context. [System.Diagnostics.DebuggerHidden] protected virtual void InvokeAsyncCallback(IAsyncExecutionContext executionContext) { if (this.OuterHandler!=null) { this.OuterHandler.AsyncCallback(executionContext); } else { // No more outer handlers to process, signal completion executionContext.ResponseContext.AsyncResult.Response = executionContext.ResponseContext.Response; // Signals the wait handle and calls the user callback. executionContext.ResponseContext.AsyncResult.InvokeCallback(); } } #endif #if AWS_ASYNC_API /// /// Contains the processing logic for an asynchronous request invocation. /// This method calls InnerHandler.InvokeSync to continue processing of the /// request by the pipeline. /// /// The response type for the current request. /// The execution context, it contains the /// request and response context. /// A task that represents the asynchronous operation. #if !NETSTANDARD [System.Diagnostics.DebuggerHidden] #endif public virtual System.Threading.Tasks.Task InvokeAsync(IExecutionContext executionContext) where T : AmazonWebServiceResponse, new() { if (this.InnerHandler != null) { return InnerHandler.InvokeAsync(executionContext); } throw new InvalidOperationException("Cannot invoke InnerHandler. InnerHandler is not set."); } #endif /// /// Logs the metrics for the current execution context. /// /// The execution context, it contains the /// request and response context. protected void LogMetrics(IExecutionContext executionContext) { var metrics = executionContext.RequestContext.Metrics; if (executionContext.RequestContext.ClientConfig.LogMetrics) { string errors = metrics.GetErrors(); if (!string.IsNullOrEmpty(errors)) this.Logger.InfoFormat("Request metrics errors: {0}", errors); this.Logger.InfoFormat("Request metrics: {0}", metrics); } } } }