File: System\ServiceModel\Activation\HostedHttpRequestAsyncResult.cs
Project: ndp\cdf\src\WCF\System.ServiceModel.Activation\System.ServiceModel.Activation.csproj (System.ServiceModel.Activation)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Activation
{
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Net;
    using System.Runtime;
    using System.Runtime.Diagnostics;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.Authentication.ExtendedProtection;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Permissions;
    using System.Security.Principal;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Activation.Configuration;
    using System.ServiceModel.Activation.Diagnostics;
    using System.Threading;
    using System.Web;
    using System.Web.Management;
    using System.Web.Routing;
    using TD2 = System.ServiceModel.Diagnostics.Application.TD;
 
    class HostedHttpRequestAsyncResult : AsyncResult, HttpChannelListener.IHttpAuthenticationContext
    {
        [Fx.Tag.SecurityNote(Critical = "Stores the securitycritical callback values, we need to protect these values")]
        [SecurityCritical]
        static WindowsIdentity anonymousIdentity;
        [SecurityCritical]
        static Action<object> waitOnBeginRequest;
        [SecurityCritical]
        static Action<object> waitOnBeginRequestWithFlow;
        [SecurityCritical]
        static ContextCallback contextOnBeginRequest;
        [SecurityCritical]
        static AsyncCallback processRequestCompleteCallback;
 
 
        [ThreadStatic]
        static AutoResetEvent waitObject;
 
        static Nullable<bool> iisSupportsExtendedProtection;
 
        [Fx.Tag.SecurityNote(Critical = "Keeps track of impersonated user, caller must use with care")]
        [SecurityCritical]
        HostedImpersonationContext impersonationContext;
 
        [Fx.Tag.SecurityNote(Critical = "Keeps track of thread static data (HttpContext, CurrentCulture, CurrentUICulture) that is used for AspNetCompatibility mode, caller must use with care")]
        [SecurityCritical]
        HostedThreadData hostedThreadData;
 
        [Fx.Tag.SecurityNote(Critical =
            "This field is used to manipulate request/responses using APIs protected by LinkDemand." +
            "It is critical because we use it to determine whether we believe we're being hosted in ASP.NET or not." +
            "The field is set in the constructor of this class and we deem it safe because:" +
            "    1) all paths that lead to the .ctor are SecurityCritical and" +
            "    2) those paths have called ServiceHostingEnvironment.EnsureInitialized (which is also critical)" +
            "So if the field is non-null, it's safe to say that we're hosted in ASP.NET, hence all the helper methods in this class that touch this field can be SecurityTreatAsSafe")]
        [SecurityCritical]
        HttpApplication context;
 
        int state;
 
        int streamedReadState;
 
        [Fx.Tag.SecurityNote(Critical = "Determines whether to set the HttpContext on the outgoing thread.")]
        [SecurityCritical]
        bool flowContext;
        bool ensureWFService;
        string configurationBasedServiceVirtualPath;
        EventTraceActivity eventTraceActivity;
 
        readonly bool isWebSocketRequest;
 
        [Fx.Tag.SecurityNote(Critical = "Captures HostedImpersonationContext which must be done in the right place, and calls unsafe" +
        "ScheduleCallbackLowPriNoFlow and ScriptTimeout.  Called outside of user security context.")]
        [SecurityCritical]
        public HostedHttpRequestAsyncResult(HttpApplication context, bool flowContext, bool ensureWFService, AsyncCallback callback, object state) :
            this(context, null, flowContext, ensureWFService, callback, state)
        {
        }
 
        [Fx.Tag.SecurityNote(Critical = "Captures HostedImpersonationContext which must be done in the right place, and calls unsafe" +
            "ScheduleCallbackLowPriNoFlow and ScriptTimeout.  Called outside of user security context.")]
        [SecurityCritical]
        public HostedHttpRequestAsyncResult(HttpApplication context, string aspNetRouteServiceVirtualPath, bool flowContext, bool ensureWFService, AsyncCallback callback, object state) :
            base(callback, state)
        {
            if (context == null)
            {
                throw FxTrace.Exception.ArgumentNull("context");
            }
 
            AspNetPartialTrustHelpers.FailIfInPartialTrustOutsideAspNet();
 
            HostedAspNetEnvironment.TrySetWebSocketVersion(context);
            this.context = context;
            // WebSockets require the integrated pipeline mode and the WebSocket IIS module to be loaded. If these conditions 
            // are not met, the HttpContext.IsWebSocketRequest property throws. Also, if these conditions are not met,
            // we do not let WebSocket listeners to be started (we fail the service activation), so setting the 'isWebSocketRequest' flag 
            // to false in this case will not create confusion (or make troubleshooting difficult).
            this.isWebSocketRequest = HttpRuntime.UsingIntegratedPipeline && AspNetEnvironment.Current.IsWebSocketModuleLoaded && this.context.Context.IsWebSocketRequest;
            this.flowContext = flowContext;
            if (ensureWFService)
            {
                // check for CBA scenario. if true, service should be handled by WCF instead of WF, 
                // set this.ensureWFservice to false
                if (ServiceHostingEnvironment.IsConfigurationBasedService(context, out this.configurationBasedServiceVirtualPath))
                {
                    this.ensureWFService = false;
                }
                else
                {
                    this.ensureWFService = true;
                }
            }
 
            if (!string.IsNullOrEmpty(aspNetRouteServiceVirtualPath))
            {
                // aspnet routing can hijack CBA request as we append {*pathInfo} to urlpattern and there is no real file for CBA 
                // check for CBA scenario. if the request is hijacked. i.e., 
                // 1) route maps to a virtual directory:
                // aspNetRouteServiceVirtualPath <> context.Request.AppRelativeCurrentExecutionFilePath == configurationBasedServiceVirtualPath 
                // if RouteExistingFiles <> true, set aspnetRouteServiceVirtualPath to null so that the request will be treated as CBA
                // if RouteExistingFiles == true, this hijack is by-design, do nothing
                // 2) route maps to a CBA entry:
                // aspNetRouteServiceVirtualPath == context.Request.AppRelativeCurrentExecutionFilePath == configurationBasedServiceVirtualPath
                // we will use RouteExistingFiles to decide which service should be activated. We do it in ServiceHostingEnviroment.HostingManager, 
                // as we cannot pass this info to the latter. 
                if (!RouteTable.Routes.RouteExistingFiles &&
                    ServiceHostingEnvironment.IsConfigurationBasedService(context, out this.configurationBasedServiceVirtualPath))
                {
                    this.AspNetRouteServiceVirtualPath = null;
                }
                else
                {
                    this.AspNetRouteServiceVirtualPath = aspNetRouteServiceVirtualPath;
                }
            }
 
            // If this is a DEBUG request, complete right away and let ASP.NET handle it.
            string method = context.Request.HttpMethod ?? "";
            char firstMethodChar = method.Length == 5 ? method[0] : '\0';
            if ((firstMethodChar == 'd' || firstMethodChar == 'D') &&
                string.Compare(method, "DEBUG", StringComparison.OrdinalIgnoreCase) == 0)
            {
                if (DiagnosticUtility.ShouldTraceVerbose)
                {
                    TraceUtility.TraceEvent(TraceEventType.Verbose, TraceCode.WebHostDebugRequest, SR.TraceCodeWebHostDebugRequest, this);
                }
 
                this.state = State.Completed;
                Complete(true, null);
                return;
            }
 
            this.impersonationContext = new HostedImpersonationContext();
 
            if (flowContext)
            {
                if (ServiceHostingEnvironment.AspNetCompatibilityEnabled)
                {
                    // Capture HttpContext/culture context if necessary.  Can be used later by HostedHttpInput to re-apply
                    // the culture during dispatch.  Also flowed here.
                    hostedThreadData = new HostedThreadData();
                }
            }
 
            // Set this up before calling IncrementRequestCount so if it fails, we don't leak a count.
            Action<object> iotsCallback = (AspNetPartialTrustHelpers.NeedPartialTrustInvoke || flowContext) ?
                WaitOnBeginRequestWithFlow : WaitOnBeginRequest;
 
            // Tell ASPNET to by-pass all the other events so no other http modules will
            // be invoked, Indigo basically takes over the request completely. This should
            // only be called in non-AspNetCompatibilityEnabled mode.
            if (!ServiceHostingEnvironment.AspNetCompatibilityEnabled && !this.ensureWFService)
            {
                context.CompleteRequest();
            }
 
            // Prevent ASP.NET from generating thread aborts in relation to this request.
            context.Server.ScriptTimeout = int.MaxValue;
 
            ServiceHostingEnvironment.IncrementRequestCount(ref this.eventTraceActivity, context.Request.AppRelativeCurrentExecutionFilePath);
 
            IOThreadScheduler.ScheduleCallbackLowPriNoFlow(iotsCallback, this);
        }
 
        public static WindowsIdentity AnonymousIdentity
        {
            [Fx.Tag.SecurityNote(Critical = "Access the value of corresponding static field and prevent someone from changing its value")]
            [SecuritySafeCritical]
            get
            {
                if (anonymousIdentity == null)
                {
                    anonymousIdentity = WindowsIdentity.GetAnonymous();
                }
                return anonymousIdentity;
            }
        }
 
        public static Action<object> WaitOnBeginRequest
        {
            [Fx.Tag.SecurityNote(Critical = "Access the value of corresponding static field and prevent someone from changing its value")]
            [SecuritySafeCritical]
            get
            {
                if (waitOnBeginRequest == null)
                {
                    waitOnBeginRequest = new Action<object>(OnBeginRequest);
                }
                return waitOnBeginRequest;
            }
        }
 
        public static Action<object> WaitOnBeginRequestWithFlow
        {
            [Fx.Tag.SecurityNote(Critical = "Access the value of corresponding static field and prevent someone from changing its value")]
            [SecuritySafeCritical]
            get
            {
                if (waitOnBeginRequestWithFlow == null)
                {
                    waitOnBeginRequestWithFlow = new Action<object>(OnBeginRequestWithFlow);
                }
                return waitOnBeginRequestWithFlow;
            }
        }
 
        public static ContextCallback ContextOnBeginRequest
        {
            [Fx.Tag.SecurityNote(Critical = "Access the value of corresponding static field and prevent someone from changing its value")]
            [SecuritySafeCritical]
            get
            {
                if (contextOnBeginRequest == null)
                {
                    contextOnBeginRequest = new ContextCallback(OnBeginRequest);
                }
                return contextOnBeginRequest;
            }
        }
 
        public static AsyncCallback ProcessRequestCompleteCallback
        {
            [Fx.Tag.SecurityNote(Critical = "Access the value of corresponding static field and prevent someone from changing its value")]
            [SecuritySafeCritical]
            get
            {
                if (processRequestCompleteCallback == null)
                {
                    processRequestCompleteCallback = Fx.ThunkCallback(new AsyncCallback(ProcessRequestComplete));
                }
                return processRequestCompleteCallback;
            }
        }
 
        public bool IISSupportsExtendedProtection
        {
            get
            {
                if (HostedHttpRequestAsyncResult.iisSupportsExtendedProtection == null)
                {
                    HostedHttpRequestAsyncResult.iisSupportsExtendedProtection = this.IISSupportsExtendedProtectionInternal();
                }
                return HostedHttpRequestAsyncResult.iisSupportsExtendedProtection.Value;
            }
        }
 
        public bool IsWebSocketRequest
        {
            get { return this.isWebSocketRequest; }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Touches critical field context.", Safe = "Does not leak control or data, no potential for harm.")]
        [SecuritySafeCritical]
        [MethodImpl(MethodImplOptions.NoInlining)]
        [PermissionSetAttribute(SecurityAction.Assert, Unrestricted = true)]
        private bool IISSupportsExtendedProtectionInternal()
        {
            DiagnosticUtility.DebugAssert(ExtendedProtectionPolicy.OSSupportsExtendedProtection, "OS must support ExtendedProtection");
 
            try
            {
                ChannelBinding cbt = this.context.Request.HttpChannelBinding;
                return true;
            }
            catch (PlatformNotSupportedException)
            {
                // contract with Asp.Net is that they will always throw a PlatformNotSupportedException if IIS is not patched for CBT yet
                return false;
            }
            catch (COMException)
            {
                // If IIS is patched for CBT and an error occurs when trying to retrieve the token a COMException is thrown. Even in this
                // case we know that IIS is patched for CBT.
                return true;
            }
        }
 
 
        [Fx.Tag.SecurityNote(Critical = "Captures HostedImpersonationContext which must be done in the right place, and calls unsafe" +
            "ScheduleCallbackLowPriNoFlow and ScriptTimeout.  Called outside of user security context." +
            "Callers of this function must call ServiceHostingEnvironment.EnsureInitialized")]
        [SecurityCritical]
        public static void ExecuteSynchronous(HttpApplication context, bool flowContext, bool ensureWFService)
        {
            ExecuteSynchronous(context, null, flowContext, ensureWFService);
        }
 
        [Fx.Tag.SecurityNote(Critical = "Captures HostedImpersonationContext which must be done in the right place, and calls unsafe" +
            "ScheduleCallbackLowPriNoFlow and ScriptTimeout.  Called outside of user security context." +
            "Callers of this function must call ServiceHostingEnvironment.EnsureInitialized")]
        [SecurityCritical]
        public static void ExecuteSynchronous(HttpApplication context, string routeServiceVirtualPath, bool flowContext, bool ensureWFService)
        {
            AutoResetEvent wait = HostedHttpRequestAsyncResult.waitObject;
            if (wait == null)
            {
                wait = new AutoResetEvent(false);
                HostedHttpRequestAsyncResult.waitObject = wait;
            }
 
            HostedHttpRequestAsyncResult result;
            try
            {
                result = new HostedHttpRequestAsyncResult(context, routeServiceVirtualPath, flowContext, ensureWFService, ProcessRequestCompleteCallback, wait);
                if (!result.CompletedSynchronously)
                {
                    wait.WaitOne();
                }
                wait = null;
            }
            finally
            {
                if (wait != null)
                {
                    // Not sure of the state anymore.
                    HostedHttpRequestAsyncResult.waitObject = null;
                    wait.Close();
                }
            }
 
            HostedHttpRequestAsyncResult.End(result);
        }
 
        [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Can be called outside of a user context.")]
        static void ProcessRequestComplete(IAsyncResult result)
        {
            if (!result.CompletedSynchronously)
            {
                try
                {
                    ((AutoResetEvent)result.AsyncState).Set();
                }
                catch (ObjectDisposedException exception)
                {
                    DiagnosticUtility.TraceHandledException(exception, TraceEventType.Warning);
                }
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Can be called outside of user context, accesses hostedThreadData.",
            Safe = "Uses hostedThreadData to set HttpContext.Current, cultures to the one attached to this async-result instance.")]
        [SecuritySafeCritical]
        static void OnBeginRequestWithFlow(object state)
        {
            HostedHttpRequestAsyncResult self = (HostedHttpRequestAsyncResult)state;
 
            IDisposable hostedThreadContext = null;
            try
            {
                if (self.flowContext)
                {
                    // In AspCompat case, these are the three things that need to be flowed.  See HostedHttpInput.
                    if (self.hostedThreadData != null)
                    {
                        hostedThreadContext = self.hostedThreadData.CreateContext();
                    }
                }
 
                // In full-trust, this simply calls the delegate.
                AspNetPartialTrustHelpers.PartialTrustInvoke(ContextOnBeginRequest, self);
            }
            finally
            {
                if (hostedThreadContext != null)
                {
                    hostedThreadContext.Dispose();
                }
            }
        }
 
        static void OnBeginRequest(object state)
        {
            HostedHttpRequestAsyncResult self = (HostedHttpRequestAsyncResult)state;
 
            Exception completionException = null;
            try
            {
                self.BeginRequest();
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e))
                {
                    throw;
                }
 
                completionException = e;
            }
 
            if (completionException != null)
            {
                self.CompleteOperation(completionException);
            }
        }
 
        void BeginRequest()
        {
            try
            {
                HandleRequest();
            }
            catch (EndpointNotFoundException exception)
            {
                // HTTP-GET is special cased to avoid that the ServiceActivation-HTTP-response is treated as service response.
                // For WebSocket requests we treat the ServiceActivation in the same way like for SOAP (HTTP-POST) requests.
                if (string.Compare(GetHttpMethod(), "GET", StringComparison.OrdinalIgnoreCase) == 0 &&
                    !this.isWebSocketRequest)
                {
                    // Wrap the exception into HttpException
                    throw FxTrace.Exception.AsError(new HttpException((int)HttpStatusCode.NotFound, exception.Message, exception));
                }
 
                SetStatusCode((int)HttpStatusCode.NotFound);
                CompleteOperation(null);
            }
            catch (ServiceActivationException exception)
            {
                // HTTP-GET is special cased to avoid that the ServiceActivation-HTTP-response is treated as service response.
                // For WebSocket requests we treat the ServiceActivation in the same way like for SOAP (HTTP-POST) requests.
                if (string.Compare(GetHttpMethod(), "GET", StringComparison.OrdinalIgnoreCase) == 0 &&
                    !this.isWebSocketRequest)
                {
                    if (exception.InnerException is HttpException)
                    {
                        throw exception.InnerException;
                    }
                    else
                    {
                        throw;
                    }
                }
 
                SetStatusCode((int)HttpStatusCode.InternalServerError);
                SetStatusDescription(
                    HttpChannelUtilities.StatusDescriptionStrings.HttpStatusServiceActivationException);
                CompleteOperation(null);
            }
            finally
            {
                ReleaseImpersonation();
            }
        }
 
        public WindowsIdentity LogonUserIdentity
        {
            get
            {
                IPrincipal user = this.Application.User;
                if (user == null)
                {
                    return AnonymousIdentity;
                }
 
                WindowsIdentity identity = user.Identity as WindowsIdentity;
                if (identity == null)
                {
                    return AnonymousIdentity;
                }
 
                return identity;
            }
        }
 
        WindowsIdentity HttpChannelListener.IHttpAuthenticationContext.LogonUserIdentity
        {
            get
            {
                return this.LogonUserIdentity;
            }
        }
 
        public HostedImpersonationContext ImpersonationContext
        {
            [Fx.Tag.SecurityNote(Critical = "Keeps track of impersonated user, caller must use with care.",
                Safe = "Safe for Get, individual members of HostedImpersonationContext are protected.")]
            [SecuritySafeCritical]
            get
            {
                return this.impersonationContext;
            }
        }
 
        public HostedThreadData HostedThreadData
        {
            [Fx.Tag.SecurityNote(Critical = "Keeps track of impersonated user, caller must use with care.",
                Safe = "Safe for Get, individual members of HostedThreadData are protected.")]
            [SecuritySafeCritical]
            get
            {
                return this.hostedThreadData;
            }
        }
 
        public EventTraceActivity EventTraceActivity
        {
            get
            {
                return this.eventTraceActivity;
            }
        }
 
        public Uri OriginalRequestUri
        {
            get;
            private set;
 
        }
 
        public Uri RequestUri
        {
            get;
            private set;
        }
 
        public HttpApplication Application
        {
            [Fx.Tag.SecurityNote(Critical = "Touches critical field context.", Safe = "Does not leak control or data, no potential for harm.")]
            [SecuritySafeCritical]
            get
            {
                return this.context;
            }
        }
 
        public string AspNetRouteServiceVirtualPath
        {
            get;
            private set;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.", Safe = "Does not leak control or data, no potential for harm.")]
        [SecuritySafeCritical]
        public Stream GetInputStream()
        {
            try
            {
                // CSDMain #133228: "Consume GetBufferlessInputStream"
                // The ReadEntityBodyMode property on the HttpRequest keeps track of whether the request stream has already been accessed, and if so, what API was used to access the request.
                //     - "None" means that the request stream hasn't been accessed.
                //     - "Bufferless" means that GetBufferlessInputStream() was used to access it.
                //     - "Buffered" means GetBufferedInputStream() was used to access it.
                //     - "Classic" means that either the InputStream, Form, Files, or BinaryRead APIs were invoked already.
                // In general, these values are incompatible with one another, meaning that once the request was accessed in a "Classic" way, only "Classic" APIs can be invoked on the HttpRequest.
                // If incompatible APIs are invoked, an HttpException is thrown.
                // In order to prevent HttpExceptions from being thrown for this reason, we will check the ReadEntityBodyMode, and access the request stream with the corresponding API
                // If the request stream hasn't been accessed yet (eg, by an HttpModule which executed earlier), then we will use GetBufferlessInputStream by default.
                ReadEntityBodyMode mode = this.context.Request.ReadEntityBodyMode;
                Fx.Assert(mode == ReadEntityBodyMode.None || mode == ReadEntityBodyMode.Bufferless || mode == ReadEntityBodyMode.Buffered || mode == ReadEntityBodyMode.Classic,
                    "Unknown value for System.Web.ReadEntityBodyMode enum");
 
                if (mode == ReadEntityBodyMode.None && ServiceHostingEnvironment.AspNetCompatibilityEnabled && AppSettings.UseClassicReadEntityMode)
                {
                    mode = ReadEntityBodyMode.Classic;
                }
 
                switch (mode)
                {
                    case ReadEntityBodyMode.None:
                    case ReadEntityBodyMode.Bufferless:
                        return this.context.Request.GetBufferlessInputStream(true);  // ignores system.web/httpRuntime/maxRequestLength
                    case ReadEntityBodyMode.Buffered:
                        return this.context.Request.GetBufferedInputStream();
                    default: 
                        // ReadEntityBodyMode.Classic:
                        return this.context.Request.InputStream;
                }
            }
            catch (HttpException hostedException)
            {
                if (hostedException.WebEventCode == WebEventCodes.RuntimeErrorPostTooLarge)
                {
                    throw FxTrace.Exception.AsError(HttpInput.CreateHttpProtocolException(SR.Hosting_MaxRequestLengthExceeded, HttpStatusCode.RequestEntityTooLarge, null, hostedException));
                }
                else
                {
                    throw FxTrace.Exception.AsError(new CommunicationException(hostedException.Message, hostedException));
                }
            }
        }
 
        public void OnReplySent()
        {
            CompleteOperation(null);
        }
 
        internal void CompleteOperation(Exception exception)
        {
            if (this.state == State.Running &&
                Interlocked.CompareExchange(ref this.state, State.Completed, State.Running) == State.Running)
            {
                this.CompleteAsynchronously(exception);
            }
        }
 
        public void Abort()
        {
            if (this.state == State.Running &&
                Interlocked.CompareExchange(ref this.state, State.Aborted, State.Running) == State.Running)
            {
                int currentStreamedReadState = Interlocked.Exchange(ref this.streamedReadState, StreamedReadState.AbortStarted);
                // Closes the socket connection to the client
                if (HttpRuntime.UsingIntegratedPipeline)
                {
                    Application.Request.Abort();
                }
                else
                {
                    Application.Response.Close();
                }
 
                if (currentStreamedReadState == StreamedReadState.None)
                {
                    this.CompleteAsynchronously(null);
                }
                else
                {
                    Fx.Assert(currentStreamedReadState == StreamedReadState.ReceiveStarted, string.Format(CultureInfo.InvariantCulture, "currentStramedReadState is not ReceivedStarted: {0}", currentStreamedReadState));
                    if (Interlocked.CompareExchange(ref this.streamedReadState, StreamedReadState.Aborted, StreamedReadState.AbortStarted) == StreamedReadState.AbortStarted)
                    {
                        return;
                    }
 
                    Fx.Assert(this.streamedReadState == StreamedReadState.ReceiveFinishedAfterAbortStarted, string.Format(CultureInfo.InvariantCulture, "currentStramedReadState is not ReceiveFinished: {0}", this.streamedReadState));
                    this.CompleteAsynchronously(null);
                }
            }
        }
 
        void CompleteAsynchronously(Exception ex)
        {
            Complete(false, ex);
            ServiceHostingEnvironment.DecrementRequestCount(this.eventTraceActivity);
        }
 
        internal bool TryStartStreamedRead()
        {
            return Interlocked.CompareExchange(ref this.streamedReadState, StreamedReadState.ReceiveStarted, StreamedReadState.None) == StreamedReadState.None;
        }
 
        internal void SetStreamedReadFinished()
        {
            if (Interlocked.CompareExchange(ref this.streamedReadState, StreamedReadState.None, StreamedReadState.ReceiveStarted) == StreamedReadState.ReceiveStarted)
            {
                return;
            }
 
            if (Interlocked.CompareExchange(ref this.streamedReadState, StreamedReadState.ReceiveFinishedAfterAbortStarted, StreamedReadState.AbortStarted) == StreamedReadState.AbortStarted)
            {
                return;
            }
 
            Fx.Assert(this.streamedReadState == StreamedReadState.Aborted, string.Format(CultureInfo.InvariantCulture, "currentStramedReadState is not Aborted: {0}", this.streamedReadState));
 
            this.CompleteAsynchronously(null);
        }
 
        [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - Can be called outside of a user context.")]
        public static void End(IAsyncResult result)
        {
            try
            {
                AsyncResult.End<HostedHttpRequestAsyncResult>(result);
            }
            catch (Exception exception)
            {
                if (!Fx.IsFatal(exception))
                {
                    // Log the exception.
                    DiagnosticUtility.EventLog.LogEvent(TraceEventType.Error, (ushort)System.Runtime.Diagnostics.EventLogCategory.WebHost, 
                        (uint)System.Runtime.Diagnostics.EventLogEventId.WebHostFailedToProcessRequest,
                      TraceUtility.CreateSourceString(result),
                      exception == null ? string.Empty : exception.ToString());
                }
                throw;
            }
        }
 
        X509Certificate2 HttpChannelListener.IHttpAuthenticationContext.GetClientCertificate(out bool isValidCertificate)
        {
            HttpClientCertificate certificateInfo = this.Application.Request.ClientCertificate;
            isValidCertificate = certificateInfo.IsValid;
            if (certificateInfo.IsPresent)
            {
                return new X509Certificate2(certificateInfo.Certificate);
            }
            else
            {
                return null;
            }
        }
 
        TraceRecord HttpChannelListener.IHttpAuthenticationContext.CreateTraceRecord()
        {
            return new System.ServiceModel.Diagnostics.HttpRequestTraceRecord(this.Application.Request);
        }
 
        void HandleRequest()
        {
            this.OriginalRequestUri = GetUrl();
            string relativeVirtualPath;
            if (!string.IsNullOrEmpty(this.AspNetRouteServiceVirtualPath))
            {
                relativeVirtualPath = this.AspNetRouteServiceVirtualPath;
            }
            else if (!string.IsNullOrEmpty(this.configurationBasedServiceVirtualPath))
            {
                relativeVirtualPath = this.configurationBasedServiceVirtualPath;
 
            }
            else
            {
                relativeVirtualPath = GetAppRelativeCurrentExecutionFilePath();
            }
 
            if (ensureWFService)
            {
                bool bypass = false;
                try
                {
                    if (!ServiceHostingEnvironment.EnsureWorkflowService(relativeVirtualPath))
                    {
                        CompleteOperation(null);
                        bypass = true;
                        return;
                    }
                }
                finally
                {
                    if (!bypass)
                    {
                        CompleteRequest();
                    }
                }
            }
 
            // Support for Cassini.
            if (ServiceHostingEnvironment.IsSimpleApplicationHost)
            {
                HostedTransportConfigurationManager.EnsureInitializedForSimpleApplicationHost(this);
            }
 
            HttpHostedTransportConfiguration transportConfiguration = HostedTransportConfigurationManager.GetConfiguration(this.OriginalRequestUri.Scheme)
                as HttpHostedTransportConfiguration;
            HostedHttpTransportManager transportManager = null;
 
            // There must be a transport binding that matches the request.
            if (transportConfiguration != null)
            {
                transportManager = transportConfiguration.GetHttpTransportManager(this.OriginalRequestUri);
            }
 
            if (transportManager == null)
            {
                InvalidOperationException invalidOpException = new InvalidOperationException(SR.Hosting_TransportBindingNotFound(OriginalRequestUri.ToString()));
 
                ServiceActivationException activationException = new ServiceActivationException(invalidOpException.Message, invalidOpException);
 
                LogServiceActivationException(activationException);
 
                throw FxTrace.Exception.AsError(activationException);
            }
 
            this.RequestUri = new Uri(transportManager.ListenUri, this.OriginalRequestUri.PathAndQuery);
            Fx.Assert(
                object.ReferenceEquals(this.RequestUri.Scheme, Uri.UriSchemeHttp) || object.ReferenceEquals(this.RequestUri.Scheme, Uri.UriSchemeHttps),
                "Scheme must be Http or Https.");
 
            ServiceHostingEnvironment.EnsureServiceAvailableFast(relativeVirtualPath, this.eventTraceActivity);
 
            transportManager.HttpContextReceived(this);
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls into an unsafe UnsafeLogEvent method",
            Safe = "Event identities cannot be spoofed as they are constants determined inside the method")]
        [SecuritySafeCritical]
        void LogServiceActivationException(ServiceActivationException activationException)
        {
            if (TD2.ServiceExceptionIsEnabled())
            {
                TD2.ServiceException(this.eventTraceActivity, activationException.ToString(), typeof(ServiceActivationException).FullName);
            }
 
            if (TD.ServiceActivationExceptionIsEnabled())
            {
                TD.ServiceActivationException(activationException != null ? activationException.ToString() : string.Empty, activationException);
            }
            DiagnosticUtility.UnsafeEventLog.UnsafeLogEvent(TraceEventType.Error, (ushort)System.Runtime.Diagnostics.EventLogCategory.WebHost, 
                (uint)System.Runtime.Diagnostics.EventLogEventId.WebHostFailedToProcessRequest, true,
                    TraceUtility.CreateSourceString(this), activationException.ToString());
        }
 
        [Fx.Tag.SecurityNote(Critical = "manipulates impersonation object",
           Safe = "Does not leak control or mutable/harmful data, no potential for harm except memory leak.")]
        [SecuritySafeCritical]
        internal void AddRefForImpersonation()
        {
            if (this.impersonationContext != null)
            {
                this.impersonationContext.AddRef();
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "manipulates impersonation object",
            Safe = "Releasing the SafeHandle early could only cause a future impersonation attempt to fail. We have to handle impersonation failures well already.")]
        [SecuritySafeCritical]
        internal void ReleaseImpersonation()
        {
            if (this.impersonationContext != null)
            {
                this.impersonationContext.Release();
            }
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects, changes properties of the HTTP response.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal void SetContentType(string contentType)
        {
            this.context.Response.ContentType = contentType;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects, changes properties of the HTTP response.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal void SetContentEncoding(string contentEncoding)
        {
            this.context.Response.AddHeader(HttpChannelUtilities.ContentEncodingHeader, contentEncoding);
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects, completes the request.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal void CompleteRequest()
        {
            this.context.CompleteRequest();
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects, changes properties of the HTTP response.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal void SetTransferModeToStreaming()
        {
            this.context.Response.BufferOutput = false;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects, changes properties of the HTTP response.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal void AppendHeader(string name, string value)
        {
            this.context.Response.AppendHeader(name, value);
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects, changes properties of the HTTP response.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal void SetStatusCode(int statusCode)
        {
            this.context.Response.TrySkipIisCustomErrors = true;
            this.context.Response.StatusCode = statusCode;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects, changes properties of the HTTP response.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal void SetStatusDescription(string statusDescription)
        {
            this.context.Response.StatusDescription = statusDescription;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects, changes properties of the HTTP response.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal void SetConnectionClose()
        {
            this.context.Response.AppendHeader("Connection", "close");
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal byte[] GetPrereadBuffer(ref int contentLength)
        {
            byte[] preReadBuffer = new byte[1];
            if (this.GetInputStream().Read(preReadBuffer, 0, 1) > 0)
            {
                contentLength = -1;
                return preReadBuffer;
            }
            return null;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal Stream GetOutputStream()
        {
            return this.context.Response.OutputStream;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal string GetHttpMethod()
        {
            return this.context.Request.HttpMethod;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal string GetContentType()
        {
            const string ContentTypeHeaderName = "Content-Type";
            return this.context.Request.Headers[ContentTypeHeaderName];
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal string GetAcceptEncoding()
        {
            return this.context.Request.Headers[HttpChannelUtilities.AcceptEncodingHeader];
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal string GetContentTypeFast()
        {
            return this.context.Request.ContentType;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal int GetContentLength()
        {
            return this.context.Request.ContentLength;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal string GetSoapAction()
        {
            const string SoapActionHeaderName = "SOAPAction";
            return this.context.Request.Headers[SoapActionHeaderName];
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        internal ChannelBinding GetChannelBinding()
        {
            if (!this.IISSupportsExtendedProtection)
            {
                return null;
            }
 
            return this.context.Request.HttpChannelBinding;
        }
 
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        string GetAppRelativeCurrentExecutionFilePath()
        {
            return this.context.Request.AppRelativeCurrentExecutionFilePath;
        }
 
        [Fx.Tag.SecurityNote(Critical = "Calls getters with LinkDemands in ASP .NET objects.",
            Safe = "Does not leak control or mutable/harmful data, no potential for harm.")]
        [SecuritySafeCritical]
        Uri GetUrl()
        {
            return this.context.Request.Url;
        }
 
        static class State
        {
            internal const int Running = 0;
            internal const int Completed = 1;
            internal const int Aborted = 2;
        }
 
        static class StreamedReadState
        {
            internal const int None = 0;
            internal const int ReceiveStarted = 1;
            internal const int ReceiveFinishedAfterAbortStarted = 2;
            internal const int AbortStarted = 3;
            internal const int Aborted = 4;
        }
    }
}