File: Hosting\IPipelineRuntime.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="IIS7Runtime.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
/*
 * The ASP.NET/IIS 7 integrated pipeline runtime service host
 *
 * Copyright (c) 2004 Microsoft Corporation
 */
 
namespace System.Web.Hosting {
    using System.Collections;
    using System.Globalization;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Security.Permissions;
    using System.Security.Principal;
    using System.Text;
    using System.Threading;
    using System.Web.Util;
    using System.Web;
    using System.Web.Management;
    using System.IO;
 
    using IIS = UnsafeIISMethods;
 
    delegate void AsyncCompletionDelegate(
        IntPtr rootedObjectsPointer, 
        int bytesRead, 
        int hresult,
        IntPtr pAsyncCompletionContext);
 
    delegate void AsyncDisconnectNotificationDelegate(
        IntPtr pManagedRootedObjects);
 
    // this delegate is called from native code
    // each time a native-managed
    // transition is made to process a request state
    delegate int ExecuteFunctionDelegate(
            IntPtr rootedObjectsPointer,
            IntPtr nativeRequestContext,
            IntPtr moduleData,
            int flags);
 
    delegate IntPtr PrincipalFunctionDelegate(
            IntPtr rootedObjectsPointer,
            int requestingAppDomainId);
 
    delegate int RoleFunctionDelegate(
            IntPtr pRootedObjects,
            IntPtr pszRole,
            int cchRole,
            out bool isInRole);
 
    // this delegate is called from native code when the request is complete
    // to free any managed resources associated with the request
    delegate void DisposeFunctionDelegate( [In] IntPtr rootedObjectsPointer );
 
    [ComImport, Guid("c96cb854-aec2-4208-9ada-a86a96860cb6"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IPipelineRuntime {
        void StartProcessing();
        void StopProcessing();
        void InitializeApplication([In] IntPtr appContext);
        IntPtr GetAsyncCompletionDelegate();
        IntPtr GetAsyncDisconnectNotificationDelegate();
        IntPtr GetExecuteDelegate();
        IntPtr GetDisposeDelegate();
        IntPtr GetRoleDelegate();
        IntPtr GetPrincipalDelegate();
    }
 
    /// <include file='doc\ISAPIRuntime.uex' path='docs/doc[@for="ISAPIRuntime"]/*' />
    /// <devdoc>
    ///    <para>[To be supplied.]</para>
    /// </devdoc>
    /// <internalonly/>
    internal sealed class PipelineRuntime : MarshalByRefObject, IPipelineRuntime, IRegisteredObject {
 
        // initialization error handling
        internal const string InitExceptionModuleName = "AspNetInitializationExceptionModule";
        private const string s_InitExceptionModulePrecondition = "";
 
        // to control removal from unmanaged table (to it only once)
        private static int s_isThisAppDomainRemovedFromUnmanagedTable;
        private static IntPtr s_ApplicationContext;
        private static string s_thisAppDomainsIsapiAppId;
 
        // when GL_APPLICATION_STOP fires, this is set to true to indicate that we can unload the AppDomain
        private static bool s_StopProcessingCalled;
        private static bool s_InitializationCompleted;
 
        // keep rooted through the app domain lifetime
        private static object _delegatelock = new object();
 
        private static int _inIndicateCompletionCount;
 
        private static IntPtr _asyncCompletionDelegatePointer = IntPtr.Zero;
        private static AsyncCompletionDelegate _asyncCompletionDelegate = null;
 
        private static IntPtr _asyncDisconnectNotificationDelegatePointer = IntPtr.Zero;
        private static AsyncDisconnectNotificationDelegate _asyncDisconnectNotificationDelegate = null;
 
        private static IntPtr _executeDelegatePointer = IntPtr.Zero;
        private static ExecuteFunctionDelegate _executeDelegate = null;
 
        private static IntPtr _disposeDelegatePointer = IntPtr.Zero;
        private static DisposeFunctionDelegate _disposeDelegate = null;
 
        private static IntPtr _roleDelegatePointer = IntPtr.Zero;
        private static RoleFunctionDelegate _roleDelegate = null;
 
        private static IntPtr _principalDelegatePointer = IntPtr.Zero;
        private static PrincipalFunctionDelegate _principalDelegate = null;
 
        public IntPtr GetAsyncCompletionDelegate() {
            if (IntPtr.Zero == _asyncCompletionDelegatePointer) {
                lock (_delegatelock) {
                    if (IntPtr.Zero == _asyncCompletionDelegatePointer) {
                        AsyncCompletionDelegate d = new AsyncCompletionDelegate(AsyncCompletionHandler);
                        if (null != d) {
                            IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
                            if (IntPtr.Zero != p) {
                                _asyncCompletionDelegate = d;
                                _asyncCompletionDelegatePointer = p;
                            }
                        }
                    }
                }
            }
            return _asyncCompletionDelegatePointer;
        }
 
        public IntPtr GetAsyncDisconnectNotificationDelegate() {
            if (IntPtr.Zero == _asyncDisconnectNotificationDelegatePointer) {
                lock (_delegatelock) {
                    if (IntPtr.Zero == _asyncDisconnectNotificationDelegatePointer) {
                        AsyncDisconnectNotificationDelegate d = new AsyncDisconnectNotificationDelegate(AsyncDisconnectNotificationHandler);
                        if (null != d) {
                            IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
                            if (IntPtr.Zero != p) {
                                _asyncDisconnectNotificationDelegate = d;
                                _asyncDisconnectNotificationDelegatePointer = p;
                            }
                        }
                    }
                }
            }
            return _asyncDisconnectNotificationDelegatePointer;
        }
 
        public IntPtr GetExecuteDelegate() {
            if (IntPtr.Zero == _executeDelegatePointer) {
                lock (_delegatelock) {
                    if (IntPtr.Zero == _executeDelegatePointer) {
                        ExecuteFunctionDelegate d = new ExecuteFunctionDelegate(ProcessRequestNotification);
                        if (null != d) {
                            IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
                            if (IntPtr.Zero != p) {
                                Thread.MemoryBarrier();
                                _executeDelegate = d;
                                _executeDelegatePointer = p;
                            }
                        }
                    }
                }
            }
 
            return _executeDelegatePointer;
        }
 
        public IntPtr GetDisposeDelegate() {
            if (IntPtr.Zero == _disposeDelegatePointer) {
                lock (_delegatelock) {
                    if (IntPtr.Zero == _disposeDelegatePointer) {
                        DisposeFunctionDelegate d = new DisposeFunctionDelegate(DisposeHandler);
                        if (null != d) {
                            IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
                            if (IntPtr.Zero != p) {
                                Thread.MemoryBarrier();
                                _disposeDelegate = d;
                                _disposeDelegatePointer = p;
                            }
                        }
                    }
                }
            }
 
            return _disposeDelegatePointer;
        }
 
        public IntPtr GetRoleDelegate() {
            if (IntPtr.Zero == _roleDelegatePointer) {
                lock (_delegatelock) {
                    if (IntPtr.Zero == _roleDelegatePointer) {
                        RoleFunctionDelegate d = new RoleFunctionDelegate(RoleHandler);
                        if (null != d) {
                            IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
                            if (IntPtr.Zero != p) {
                                Thread.MemoryBarrier();
                                _roleDelegate = d;
                                _roleDelegatePointer = p;
                            }
                        }
                    }
                }
            }
 
            return _roleDelegatePointer;
        }
 
        public IntPtr GetPrincipalDelegate() {
            if (IntPtr.Zero == _principalDelegatePointer) {
                lock (_delegatelock) {
                    if (IntPtr.Zero == _principalDelegatePointer) {
                        PrincipalFunctionDelegate d = new PrincipalFunctionDelegate(GetManagedPrincipalHandler);
                        if (null != d) {
                            IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
                            if (IntPtr.Zero != p) {
                                Thread.MemoryBarrier();
                                _principalDelegate = d;
                                _principalDelegatePointer = p;
                            }
                        }
                    }
                }
            }
 
            return _principalDelegatePointer;
        }
 
 
 
        [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
        public PipelineRuntime() {
            HostingEnvironment.RegisterObject(this);
            Debug.Trace("PipelineDomain", "RegisterObject(this) called");
        }
 
        [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)]
        public override Object InitializeLifetimeService() {
            return null; // never expire lease
        }
 
        public void StartProcessing() {
            Debug.Trace("PipelineDomain", "StartProcessing AppId = " + s_thisAppDomainsIsapiAppId);
            HostingEnvironment.SetupStopListeningHandler();
        }
 
        [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)]
        public void StopProcessing() {
            Debug.Trace("PipelineDomain", "StopProcessing with stack = " + Environment.StackTrace
                        + " for AppId= " +  s_thisAppDomainsIsapiAppId);
 
            if (!HostingEnvironment.StopListeningWasCalled && !HostingEnvironment.ShutdownInitiated) {
                // If GL_STOP_LISTENING wasn't triggered, the reset is likely due to a configuration change.
                HttpRuntime.SetShutdownReason(ApplicationShutdownReason.ConfigurationChange, "IIS configuration change");
            }
 
            s_StopProcessingCalled = true;
            // inititate shutdown and
            // require the native callback for Stop
            HostingEnvironment.InitiateShutdownWithoutDemand();
        }
 
        internal static void WaitForRequestsToDrain() {
            if (s_ApplicationContext == IntPtr.Zero) {
                // If InitializeApplication was never called, then no requests ever came in and StopProcessing will never be called.
                // We can just short-circuit this method.
                return;
            }
 
            while (!s_StopProcessingCalled || _inIndicateCompletionCount > 0) {
                Thread.Sleep(250);
            }
        }
 
        private StringBuilder FormatExceptionMessage(Exception e, string[] strings) {
            StringBuilder sb = new StringBuilder(4096);
 
            if (null != strings) {
                for (int i = 0; i < strings.Length; i++) {
                    sb.Append(strings[i]);
                }
            }
            for (Exception current = e; current != null; current = current.InnerException) {
                if (current == e)
                    sb.Append("\r\n\r\nException: ");
                else
                    sb.Append("\r\n\r\nInnerException: ");
                sb.Append(current.GetType().FullName);
                sb.Append("\r\nMessage: ");
                sb.Append(current.Message);
                sb.Append("\r\nStackTrace: ");
                sb.Append(current.StackTrace);
            }
 
            return sb;
        }
 
        public void InitializeApplication(IntPtr appContext)
        {
            s_ApplicationContext = appContext;
 
            // DevDiv #381425 - webengine4!RegisterModule runs *after* HostingEnvironment.Initialize (and thus the
            // HttpRuntime static ctor) when application preload is active. This means that any global state set
            // by RegisterModule (like the IIS version information, whether we're in integrated mode, misc server
            // info, etc.) will be unavailable to PreAppStart / preload code when the preload feature is active.
            // But since RegisterModule runs before InitializeApplication, we have one last chance here to collect
            // the information before the main part of the application starts, and the pipeline can depend on it
            // to be accurate.
            HttpRuntime.PopulateIISVersionInformation();
 
            HttpApplication app = null;
 
            try {
                // if HttpRuntime.HostingInit failed, do not attempt to create the application (WOS #1653963)
                if (!HttpRuntime.HostingInitFailed) {
                    //
                    //  On IIS7, application initialization does not provide an http context.  Theoretically,
                    //  no one should be using the context during application initialization, but people do.
                    //  Create a dummy context that is used during application initialization
                    //  to prevent breakage (ISAPI mode always provides a context)
                    //
                    HttpWorkerRequest initWorkerRequest = new SimpleWorkerRequest("" /*page*/,
                                                                                  "" /*query*/,
                                                                                  new StringWriter(CultureInfo.InvariantCulture));
                    MimeMapping.SetIntegratedApplicationContext(appContext);
                    HttpContext initHttpContext = new HttpContext(initWorkerRequest);
                    app = HttpApplicationFactory.GetPipelineApplicationInstance(appContext, initHttpContext);
                }
            }
            catch(Exception e)
            {
                if (HttpRuntime.InitializationException == null) {
                    HttpRuntime.InitializationException = e;
                }
            }
            finally {
                s_InitializationCompleted = true;
 
                if (HttpRuntime.InitializationException != null) {
 
                    // at least one module must be registered so that we
                    // call ProcessRequestNotification later and send the formatted
                    // InitializationException to the client.
                    int hresult = UnsafeIISMethods.MgdRegisterEventSubscription(
                        appContext,
                        InitExceptionModuleName,
                        RequestNotification.BeginRequest,
                        0 /*postRequestNotifications*/,
                        InitExceptionModuleName,
                        s_InitExceptionModulePrecondition,
                        new IntPtr(-1),
                        false /*useHighPriority*/);
 
                    if (hresult < 0) {
                        throw new COMException( SR.GetString(SR.Failed_Pipeline_Subscription, InitExceptionModuleName),
                                                hresult );
                    }
 
                    // Always register a managed handler:
                    // WOS 1990290: VS F5 Debugging: "AspNetInitializationExceptionModule" is registered for RQ_BEGIN_REQUEST, 
                    // but the DEBUG verb skips notifications until post RQ_AUTHENTICATE_REQUEST.  
                    hresult = UnsafeIISMethods.MgdRegisterEventSubscription(
                        appContext,
                        HttpApplication.IMPLICIT_HANDLER,
                        RequestNotification.ExecuteRequestHandler /*requestNotifications*/,
                        0 /*postRequestNotifications*/,
                        String.Empty /*type*/, 
                        HttpApplication.MANAGED_PRECONDITION /*precondition*/,
                        new IntPtr(-1),
                        false /*useHighPriority*/);
 
                    if (hresult < 0) {
                        throw new COMException( SR.GetString(SR.Failed_Pipeline_Subscription, HttpApplication.IMPLICIT_HANDLER),
                                                hresult );
                    }
                }
 
                if (app != null) {
                    HttpApplicationFactory.RecyclePipelineApplicationInstance(app);
                }
            }
        }
 
        private static HttpContext UnwrapContext(IntPtr rootedObjectsPointer) {
            RootedObjects objects = RootedObjects.FromPointer(rootedObjectsPointer);
            return objects.HttpContext;
        }
 
        internal bool HostingShutdownInitiated {
            get {
                return HostingEnvironment.ShutdownInitiated;
            }
        }
 
        // called from native code when the IHttpContext is disposed
        internal static void AsyncCompletionHandler(IntPtr rootedObjectsPointer, int bytesCompleted, int hresult, IntPtr pAsyncCompletionContext) {
            HttpContext context = UnwrapContext(rootedObjectsPointer);
            IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest;
            wr.OnAsyncCompletion(bytesCompleted, hresult, pAsyncCompletionContext);
        }
 
        // called from native code when the IHttpConnection is disconnected
        internal static void AsyncDisconnectNotificationHandler(IntPtr pManagedRootedObjects) {
            // Every object we're about to call into should be live / non-disposed,
            // but since we're paranoid we should put guard clauses everywhere.
 
            Debug.Assert(pManagedRootedObjects != IntPtr.Zero);
            if (pManagedRootedObjects != IntPtr.Zero) {
                RootedObjects rootObj = RootedObjects.FromPointer(pManagedRootedObjects);
                Debug.Assert(rootObj != null);
                if (rootObj != null) {
                    IIS7WorkerRequest workerRequest = rootObj.WorkerRequest;
                    Debug.Assert(workerRequest != null);
                    if (workerRequest != null) {
                        workerRequest.NotifyOfAsyncDisconnect();
                    }
                }
            }
        }
 
        // Called from native code to see if a principal is in a given role
        internal static int RoleHandler(IntPtr pRootedObjects, IntPtr pszRole, int cchRole, out bool isInRole) {
            isInRole = false;
            IPrincipal principal = RootedObjects.FromPointer(pRootedObjects).Principal;
            if (principal != null) {
                try {
                    isInRole = principal.IsInRole(StringUtil.StringFromWCharPtr(pszRole, cchRole));
                }
                catch (Exception e) {
                    return Marshal.GetHRForException(e);
                }
            }
            return HResults.S_OK;
        }
 
        // Called from native code to get the managed principal for a given request
        // If the return value is non-zero, the caller must free the returned GCHandle
        internal static IntPtr GetManagedPrincipalHandler(IntPtr pRootedObjects, int requestingAppDomainId) {
            // DevDiv 375079: Server.TransferRequest can be used to transfer requests to different applications,
            // which means that we might be trying to pass a GCHandle to the IPrincipal object to a different
            // AppDomain, which is disallowed. If this happens, we just tell our caller that we can't give him
            // a managed IPrincipal object.
            if (requestingAppDomainId != AppDomain.CurrentDomain.Id) {
                return IntPtr.Zero;
            }
 
            IPrincipal principal = RootedObjects.FromPointer(pRootedObjects).Principal;
            return GCUtil.RootObject(principal);
        }
 
        // called from native code when the IHttpContext is disposed
        internal static void DisposeHandler(IntPtr rootedObjectsPointer) {
            RootedObjects root = RootedObjects.FromPointer(rootedObjectsPointer);
            root.Destroy();
        }
        
        // called from managed code as a perf optimization to avoid calling back later
        internal static void DisposeHandler(HttpContext context, IntPtr nativeRequestContext, RequestNotificationStatus status) {
            if (IIS.MgdCanDisposeManagedContext(nativeRequestContext, status)) {
                context.RootedObjects.Destroy();
            }
        }
 
        //
        // This is the managed entry point for processing request notifications.
        // Although this method is wrapped in try/catch, it is not supposed to
        // cause an exception. If it does throw, the application, httpwriter, etc
        // may not be initialized, and it might not be possible to process the rest
        // of the request. I would prefer to let this method throw and crash the
        // process, but for now we will consume the exception, report the error to
        // IIS, and continue.
        //
        // Code that might throw belongs in HttpRuntime::ProcessRequestNotificationPrivate.
        //
        internal static int ProcessRequestNotification(
                IntPtr rootedObjectsPointer,
                IntPtr nativeRequestContext,
                IntPtr moduleData,
                int flags)
        {
            try {
                return ProcessRequestNotificationHelper(rootedObjectsPointer, nativeRequestContext, moduleData, flags);
            }
            catch(Exception e) {
                ApplicationManager.RecordFatalException(e);
                throw;
            }
        }
 
        internal static int ProcessRequestNotificationHelper(
                IntPtr rootedObjectsPointer,
                IntPtr nativeRequestContext,
                IntPtr moduleData,
                int flags)
        {
            IIS7WorkerRequest wr = null;
            HttpContext context = null;
            RequestNotificationStatus status = RequestNotificationStatus.Continue;
            RootedObjects root;
            bool workerRequestWasJustCreated = false;
 
            if (rootedObjectsPointer == IntPtr.Zero) {
                InitializeRequestContext(nativeRequestContext, flags, out wr, out context);
                workerRequestWasJustCreated = true;
                if (context == null) {
                    return (int)RequestNotificationStatus.FinishRequest;
                }
 
                root = RootedObjects.Create();
                root.HttpContext = context;
                root.WorkerRequest = wr;
                root.WriteTransferEventIfNecessary();
                context.RootedObjects = root;
 
                IIS.MgdSetManagedHttpContext(nativeRequestContext, root.Pointer);
            }
            else {
                root = RootedObjects.FromPointer(rootedObjectsPointer);
                context = root.HttpContext;
                wr = root.WorkerRequest as IIS7WorkerRequest;
            }
 
            Debug.Assert(root != null, "We should have a RootedObjects instance by this point.");
            Debug.Assert(wr != null, "We should have an IIS7WorkerRequest instance by this point.");
 
            using (root.WithinTraceBlock()) {
                if (workerRequestWasJustCreated) {
                    AspNetEventSource.Instance.RequestStarted(wr);
                }
 
                int currentModuleIndex;
                bool isPostNotification;
                int currentNotification;
                IIS.MgdGetCurrentNotificationInfo(nativeRequestContext, out currentModuleIndex, out isPostNotification, out currentNotification);
 
                // If the HttpContext is null at this point, then we've already transitioned this request to a WebSockets request.
                // The WebSockets module should already be running, and asynchronous module-level events (like SendResponse) are
                // ineligible to be hooked by managed code.
                if (context == null || context.HasWebSocketRequestTransitionStarted) {
                    return (int)RequestNotificationStatus.Continue;
                }
 
                // It is possible for a notification to complete asynchronously while we're in
                // a call to IndicateCompletion, in which case a new IIS thread might enter before 
                // the call to IndicateCompletion returns.  If this happens, block the thread until
                // IndicateCompletion returns.  But never block a SendResponse notification, because
                // that can cause the request to hang (DevDiv Bugs 187441).
                if (context.InIndicateCompletion
                    && context.ThreadInsideIndicateCompletion != Thread.CurrentThread 
                    && RequestNotification.SendResponse != (RequestNotification)currentNotification) {
                    while (context.InIndicateCompletion) {
                        Thread.Sleep(10);
                    }
                }
            
                // RQ_SEND_RESPONSE fires out of band and completes synchronously only.
                // The pipeline must be reentrant to support this, so the notification 
                // context for the previous notification must be saved and restored.
                NotificationContext savedNotificationContext = context.NotificationContext;
                bool cancellable = context.IsInCancellablePeriod;
                bool locked = false;
                try {
                    if (cancellable) {
                        context.EndCancellablePeriod();
                    }
                    bool isReEntry = (savedNotificationContext != null);
                    if (isReEntry) {
                        context.ApplicationInstance.AcquireNotifcationContextLock(ref locked);
                    }
                    context.NotificationContext = new NotificationContext(flags /*CurrentNotificationFlags*/, 
                                                                          isReEntry);
 
                    Action<RequestNotificationStatus> verifierCheck = null;
                    if (AppVerifier.IsAppVerifierEnabled) {
                        verifierCheck = AppVerifier.GetRequestNotificationStatusCheckDelegate(context, (RequestNotification)currentNotification, isPostNotification);
                    }
 
                    status = HttpRuntime.ProcessRequestNotification(wr, context);
 
                    if (verifierCheck != null) {
                        AppVerifier.InvokeVerifierCheck(verifierCheck, status);
                    }
                }
                finally {
                    if (status != RequestNotificationStatus.Pending) {
                        // if we completed the notification, pop the notification context stack
                        // if this is an asynchronous unwind, then the completion will clear the context
                        context.NotificationContext = savedNotificationContext;
 
                        // DevDiv 112755 restore cancellable state if its changed
                        if (cancellable && !context.IsInCancellablePeriod) {
                            context.BeginCancellablePeriod();
                        } else if (!cancellable && context.IsInCancellablePeriod) {
                            context.EndCancellablePeriod();
                        }
                    }
                    if (locked) {
                        context.ApplicationInstance.ReleaseNotifcationContextLock();
                    }
                }
 
                if (status != RequestNotificationStatus.Pending) {
                    // The current notification may have changed due to the HttpApplication progressing the IIS state machine, so retrieve the info again.
                    IIS.MgdGetCurrentNotificationInfo(nativeRequestContext, out currentModuleIndex, out isPostNotification, out currentNotification);
 
                    // WOS 1785741: (Perf) In profiles, 8% of HelloWorld is transitioning from native to managed.
                    // The fix is to keep managed code on the stack so that the AppDomain context remains on the
                    // thread, and we can re-enter managed code without setting up the AppDomain context.
                    // If this optimization is possible, MgdIndicateCompletion will execute one or more notifications
                    // and return PENDING as the status.
                    ThreadContext threadContext = context.IndicateCompletionContext;
                    // DevDiv 482614:
                    // Don't use local copy to detect if we can call MgdIndicateCompletion because another thread 
                    // unwinding from MgdIndicateCompletion may be changing context.IndicateCompletionContext at the same time.
                    if (!context.InIndicateCompletion && context.IndicateCompletionContext != null) {
                        if (status == RequestNotificationStatus.Continue) {
                            try {
                                context.InIndicateCompletion = true;
                                Interlocked.Increment(ref _inIndicateCompletionCount);
                                context.ThreadInsideIndicateCompletion = Thread.CurrentThread;
                                IIS.MgdIndicateCompletion(nativeRequestContext, ref status);
                            }
                            finally {
                                context.ThreadInsideIndicateCompletion = null;
                                Interlocked.Decrement(ref _inIndicateCompletionCount);
 
                                // Leave will have been called already if the last notification is returning pending
                                // DTS267762: Make sure InIndicateCompletion is released, not based on the thread context state
                                // Otherwise the next request notification may deadlock
                                if (!threadContext.HasBeenDisassociatedFromThread || context.InIndicateCompletion) {
                                    lock (threadContext) {
                                        if (!threadContext.HasBeenDisassociatedFromThread) {
                                            threadContext.DisassociateFromCurrentThread();
                                        }
 
                                        context.IndicateCompletionContext = null;
                                        context.InIndicateCompletion = false;
                                    }
                                }
                            }
                        }
                        else {
                            if (!threadContext.HasBeenDisassociatedFromThread || context.InIndicateCompletion) {
                                lock (threadContext) {
                                    if (!threadContext.HasBeenDisassociatedFromThread) {
                                        threadContext.DisassociateFromCurrentThread();
                                    }
 
                                    context.IndicateCompletionContext = null;
                                    context.InIndicateCompletion = false;
                                }
                            }
                        }
                    }
                }
 
                if (context.HasWebSocketRequestTransitionStarted && status == RequestNotificationStatus.Pending) {
                    // At this point, the WebSocket module event (PostEndRequest) has executed and set up the appropriate contexts for us.
                    // However, there is a race condition that we need to avoid. It is possible that one thread has kicked off some async
                    // work, e.g. via an IHttpAsyncHandler, and that thread is unwinding and has reached this line of execution.
                    // Meanwhile, the IHttpAsyncHandler completed quickly (but asynchronously) and invoked MgdPostCompletion, which
                    // resulted in a new thread calling ProcessRequestNotification. If this second thread starts the WebSocket transition,
                    // then there's the risk that *both* threads might attempt to call WebSocketPipeline.ProcessRequest, which could AV
                    // the process.
                    //
                    // We protect against this by allowing only the thread which started the transition to complete the transition, so in
                    // the above scenario the original thread (which invoked the IHttpAsyncHandler) no-ops at this point and just returns
                    // Pending to its caller.
 
                    if (context.DidCurrentThreadStartWebSocketTransition) {
                        // We'll mark the HttpContext as complete, call the continuation to kick off the socket send / receive loop, and return
                        // Pending to IIS so that it doesn't advance the state machine until the WebSocket loop completes.
                        root.ReleaseHttpContext();
                        root.WebSocketPipeline.ProcessRequest();
                    }
                }
 
                return (int)status;
            }
        }
 
        private static void InitializeRequestContext(IntPtr nativeRequestContext, int flags, out IIS7WorkerRequest wr, out HttpContext context) {
            wr = null;
            context = null;
            try {
                bool etwEnabled = ((flags & HttpContext.FLAG_ETW_PROVIDER_ENABLED) == HttpContext.FLAG_ETW_PROVIDER_ENABLED);
 
                // this may throw, e.g. if the request Content-Length header has a value greater than Int32.MaxValue
                wr = IIS7WorkerRequest.CreateWorkerRequest(nativeRequestContext, etwEnabled);
 
                // this may throw, e.g. see WOS 1724573: ASP.Net v2.0: wrong error code returned when ? is used in the URL
                context = new HttpContext(wr, false);
            }
            catch {
                // treat as "400 Bad Request" since that's the only reason the HttpContext.ctor should throw
                IIS.MgdSetBadRequestStatus(nativeRequestContext);
            }
        }
 
        /// <include file='doc\ISAPIRuntime.uex' path='docs/doc[@for="ISAPIRuntime.IRegisteredObject.Stop"]/*' />
        /// <internalonly/>
        void IRegisteredObject.Stop(bool immediate) {
            Debug.Trace("PipelineDomain", "IRegisteredObject.Stop appId = " +
                        s_thisAppDomainsIsapiAppId);
 
            while (!s_InitializationCompleted && !s_StopProcessingCalled) {
                // the native W3_MGD_APP_CONTEXT is not ready for us to unload
                Thread.Sleep(250);
            }
 
            RemoveThisAppDomainFromUnmanagedTable();
            HostingEnvironment.UnregisterObject(this);
        }
 
        internal void SetThisAppDomainsIsapiAppId(String appId) {
            Debug.Trace("PipelineDomain", "SetThisAppDomainsPipelineAppId appId=" + appId);
            s_thisAppDomainsIsapiAppId = appId;
        }
 
        internal static void RemoveThisAppDomainFromUnmanagedTable() {
            if (Interlocked.Exchange(ref s_isThisAppDomainRemovedFromUnmanagedTable, 1) != 0) {
                return;
            }
 
            //
            // only notify mgdeng of this shutdown if we went through
            // Initialize from the there
            // We can also have PipelineRuntime in app domains with only
            // other protocols
            //
            try {
                if (s_thisAppDomainsIsapiAppId != null  && s_ApplicationContext != IntPtr.Zero) {
                    Debug.Trace("PipelineDomain", "Calling MgdAppDomainShutdown appId=" +
                        s_thisAppDomainsIsapiAppId + " (AppDomainAppId=" + HttpRuntime.AppDomainAppId + ")");
 
                    UnsafeIISMethods.MgdAppDomainShutdown(s_ApplicationContext);
                }
 
                HttpRuntime.AddAppDomainTraceMessage(SR.GetString(SR.App_Domain_Restart));
            }
            catch(Exception e) {
                if (ShouldRethrowException(e)) {
                    throw;
                }
            }
        }
 
        internal static bool ShouldRethrowException(Exception ex) {
            return     ex is NullReferenceException
                    || ex is AccessViolationException
                    || ex is StackOverflowException
                    || ex is OutOfMemoryException
                    || ex is System.Threading.ThreadAbortException;
        }
 
    }
}