// 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 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 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(); } } } }
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); status = HttpRuntime.ProcessRequestNotification(wr, context); } 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); } }
// called from native code when the IHttpContext is disposed internal static void DisposeHandler(IntPtr rootedObjectsPointer) { RootedObjects root = RootedObjects.FromPointer(rootedObjectsPointer); root.Destroy(); }
private static HttpContext UnwrapContext(IntPtr rootedObjectsPointer) { RootedObjects objects = RootedObjects.FromPointer(rootedObjectsPointer); return(objects.HttpContext); }