//----------------------------------------------------------------------------- // // Copyright 2016 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 System.Diagnostics.CodeAnalysis; using System.Runtime.Remoting.Messaging; using System.Web; using Amazon.XRay.Recorder.Core.Exceptions; using Amazon.XRay.Recorder.Core.Internal.Entities; namespace Amazon.XRay.Recorder.Core.Internal.Context { /// /// This context is a hybrid context of and , used for ASP.NET middleware. /// The segment created by AWS X-Ray ASP.NET middleware is stored in CallContext and HttpContext. CRUD operations are performed only on CallContext. Subsegments created /// along the lifecycle of the request are stored in CallContext. /// On operation, if the value is not present in CallContext, the CallContext is populated with the segment that is stored in HttpContext. /// public class HybridContextContainer : TraceContextImpl { /// /// Key to store Entity in . /// public const String XRayEntity = "XRayEntity"; /// /// The default trace context used for CRUD operations. /// private ITraceContext _defaultContext = new CallContextContainer(); /// /// Gets entity (segment/subsegment) from the call context. If entity is not present in call context, it populates the callcontext with the entity from . /// /// The entity (segment/subsegment) /// Thrown when the entity is not available to get. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "It's a wrapper for CallContext.LogicalGetData().")] public override Entity GetEntity() { if (_defaultContext.IsEntityPresent()) // If CallContext has entity, return the entity { return _defaultContext.GetEntity(); } // If CallContext has no value, set the entity from HTTPContext into CallContext Entity entity = InjectEntityInTraceContext(); return entity; } /// /// Set the specified entity (segment/subsegment) into call context. If the entity is segment, its also stored in .(HTTPContext.Items[XRayEntity]) /// /// The segment to be set /// Thrown when the entity is not available to set public override void SetEntity(Entity entity) { _defaultContext.SetEntity(entity); // If the entity is segment, store it in HTTPContext.Items[XRayEntity] Segment segment = entity as Segment; if (segment != null) { StoreEntityInHTTPContext(segment); } } /// /// Checks whether entity is present in CallContext. If not, check in HttpContext /// /// True if entity is present in or else false. public override bool IsEntityPresent() { if(_defaultContext.IsEntityPresent()) { return true; } var entity = GetEntityFromHTTPContext(); // If entity not present in CallContext, check in HTTPContext. return entity != null; } /// /// Clear entity from CallContext and HTTPContext for cleanup. /// public override void ClearEntity() { _defaultContext.ClearEntity(); HttpContext httpContext = GetHTTPContext(); if (httpContext != null) { httpContext.Items.Remove(XRayEntity); } } /// /// Gets instance of /// /// Instance of . private static HttpContext GetHTTPContext() { return HttpContext.Current; } /// /// Gets segment from if available, else null. /// /// Entity from context.Items[XRayEntity private static Entity GetEntityFromHTTPContext() { try { HttpContext context = GetHTTPContext(); if (context != null) { return (Segment) context.Items[XRayEntity]; // strict check, only segment should be present } } catch (InvalidCastException e) { throw new EntityNotAvailableException("The data in HTTPContext is not a valid entity.", e); } return null; } /// /// Gets Segment from and sets it to . /// /// Segment that is populated in CallContext. public Entity InjectEntityInTraceContext() { Entity entity = GetEntityFromHTTPContext(); if (entity == null) { throw new EntityNotAvailableException("Entity doesn't exist in HTTPContext"); } _defaultContext.SetEntity(entity); return entity; } /// /// Populates with the segment. /// /// Segment that is stored in HTTPContext private static void StoreEntityInHTTPContext(Segment segment) { HttpContext context = GetHTTPContext(); if (context != null) { context.Items[XRayEntity] = segment; } } } }