using Amazon.Runtime; using Amazon.Runtime.Internal; using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Amazon.Route53.Internal { /// /// Custom pipeline handler /// public class AmazonRoute53PostMarshallHandler : PipelineHandler { private static readonly HashSet FixPathLookup = new HashSet(new string[] { "/hostedzone/", "/change/", "/delegationset/" }); /// /// Calls pre invoke logic before calling the next handler /// in the pipeline. /// /// The execution context which contains both the /// requests and response context. public override void InvokeSync(IExecutionContext executionContext) { PreInvoke(executionContext); base.InvokeSync(executionContext); } #if AWS_ASYNC_API /// /// Calls pre invoke logic before calling the next handler /// in 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. public override System.Threading.Tasks.Task InvokeAsync(IExecutionContext executionContext) { PreInvoke(executionContext); return base.InvokeAsync(executionContext); } #elif AWS_APM_API /// /// Calls pre invoke logic before calling the next handler /// in the pipeline. /// /// The execution context which contains both the /// requests and response context. /// IAsyncResult which represent an async operation. public override IAsyncResult InvokeAsync(IAsyncExecutionContext executionContext) { PreInvoke(ExecutionContext.CreateFromAsyncContext(executionContext)); return base.InvokeAsync(executionContext); } #endif /// /// Custom pipeline handler /// /// protected virtual void PreInvoke(IExecutionContext executionContext) { ProcessRequestHandlers(executionContext); } /// /// Remove duplicates in resource path which can happen if the exact return values from the CreateHostedZone /// operation are used. /// /// Execution context. private static void ProcessRequestHandlers(IExecutionContext executionContext) { var request = executionContext.RequestContext.Request; FixDuplicationInResourcePath(request.ResourcePath, request.PathResources); } /// /// Removes duplicated values out of the resourcePath in Route52 paths for the limited set /// of values: "/hostedzone/", "/change/", "/delegationset/". Example: /hostedzone/{id} /// where {id} = /hostedzone/123456 would result in /hostedzone//hostedzone/123456 /// but must result in /hostedzone/123456 /// /// The patterned resource path /// The key/values to key replacement in the patterned resource path private static void FixDuplicationInResourcePath(string resourcePath, IDictionary pathResources) { var segments = resourcePath.Split(new char[] { '/' }, StringSplitOptions.None); var testSegments = segments.Select(segment => segment.Length == 0 || segment.EndsWith("}", StringComparison.Ordinal) ? segment : "/" + segment + "/"); var prevSegment = string.Empty; foreach(var segment in testSegments) { if(segment.Length == 0 || segment.StartsWith("/", StringComparison.Ordinal)) { //Not a {key} so don't need to check for duplication. prevSegment = segment; continue; } //Else this is a {key} which could contain previous segment duplication that must be removed. //Example: /hostedzone/{id} where {id} = /hostedzone/123456 would result in /hostedzone//hostedzone/123456 var keyValue = pathResources[segment]; if(FixPathLookup.Contains(prevSegment) && keyValue != null && keyValue.StartsWith(prevSegment, StringComparison.Ordinal)) { pathResources[segment] = prevSegment = keyValue.Substring(prevSegment.Length); } } } } }