1 //------------------------------------------------------------------------------
2 // <copyright file="IIS7Runtime.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 /*
8  * The ASP.NET/IIS 7 integrated pipeline runtime service host
9  *
10  * Copyright (c) 2004 Microsoft Corporation
11  */
12 
13 namespace System.Web.Hosting {
14     using System.Collections;
15     using System.Globalization;
16     using System.Reflection;
17     using System.Runtime.InteropServices;
18     using System.Security.Permissions;
19     using System.Security.Principal;
20     using System.Text;
21     using System.Threading;
22     using System.Web.Util;
23     using System.Web;
24     using System.Web.Management;
25     using System.IO;
26 
27     using IIS = UnsafeIISMethods;
28 
AsyncCompletionDelegate( IntPtr rootedObjectsPointer, int bytesRead, int hresult, IntPtr pAsyncCompletionContext)29     delegate void AsyncCompletionDelegate(
30         IntPtr rootedObjectsPointer,
31         int bytesRead,
32         int hresult,
33         IntPtr pAsyncCompletionContext);
34 
AsyncDisconnectNotificationDelegate( IntPtr pManagedRootedObjects)35     delegate void AsyncDisconnectNotificationDelegate(
36         IntPtr pManagedRootedObjects);
37 
38     // this delegate is called from native code
39     // each time a native-managed
40     // transition is made to process a request state
ExecuteFunctionDelegate( IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, int flags)41     delegate int ExecuteFunctionDelegate(
42             IntPtr rootedObjectsPointer,
43             IntPtr nativeRequestContext,
44             IntPtr moduleData,
45             int flags);
46 
PrincipalFunctionDelegate( IntPtr rootedObjectsPointer, int requestingAppDomainId)47     delegate IntPtr PrincipalFunctionDelegate(
48             IntPtr rootedObjectsPointer,
49             int requestingAppDomainId);
50 
RoleFunctionDelegate( IntPtr pRootedObjects, IntPtr pszRole, int cchRole, out bool isInRole)51     delegate int RoleFunctionDelegate(
52             IntPtr pRootedObjects,
53             IntPtr pszRole,
54             int cchRole,
55             out bool isInRole);
56 
57     // this delegate is called from native code when the request is complete
58     // to free any managed resources associated with the request
DisposeFunctionDelegate( [In] IntPtr rootedObjectsPointer )59     delegate void DisposeFunctionDelegate( [In] IntPtr rootedObjectsPointer );
60 
61     [ComImport, Guid("c96cb854-aec2-4208-9ada-a86a96860cb6"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
62     internal interface IPipelineRuntime {
StartProcessing()63         void StartProcessing();
StopProcessing()64         void StopProcessing();
InitializeApplication([In] IntPtr appContext)65         void InitializeApplication([In] IntPtr appContext);
GetAsyncCompletionDelegate()66         IntPtr GetAsyncCompletionDelegate();
GetAsyncDisconnectNotificationDelegate()67         IntPtr GetAsyncDisconnectNotificationDelegate();
GetExecuteDelegate()68         IntPtr GetExecuteDelegate();
GetDisposeDelegate()69         IntPtr GetDisposeDelegate();
GetRoleDelegate()70         IntPtr GetRoleDelegate();
GetPrincipalDelegate()71         IntPtr GetPrincipalDelegate();
72     }
73 
74     /// <include file='doc\ISAPIRuntime.uex' path='docs/doc[@for="ISAPIRuntime"]/*' />
75     /// <devdoc>
76     ///    <para>[To be supplied.]</para>
77     /// </devdoc>
78     /// <internalonly/>
79     internal sealed class PipelineRuntime : MarshalByRefObject, IPipelineRuntime, IRegisteredObject {
80 
81         // initialization error handling
82         internal const string InitExceptionModuleName = "AspNetInitializationExceptionModule";
83         private const string s_InitExceptionModulePrecondition = "";
84 
85         // to control removal from unmanaged table (to it only once)
86         private static int s_isThisAppDomainRemovedFromUnmanagedTable;
87         private static IntPtr s_ApplicationContext;
88         private static string s_thisAppDomainsIsapiAppId;
89 
90         // when GL_APPLICATION_STOP fires, this is set to true to indicate that we can unload the AppDomain
91         private static bool s_StopProcessingCalled;
92         private static bool s_InitializationCompleted;
93 
94         // keep rooted through the app domain lifetime
95         private static object _delegatelock = new object();
96 
97         private static int _inIndicateCompletionCount;
98 
99         private static IntPtr _asyncCompletionDelegatePointer = IntPtr.Zero;
100         private static AsyncCompletionDelegate _asyncCompletionDelegate = null;
101 
102         private static IntPtr _asyncDisconnectNotificationDelegatePointer = IntPtr.Zero;
103         private static AsyncDisconnectNotificationDelegate _asyncDisconnectNotificationDelegate = null;
104 
105         private static IntPtr _executeDelegatePointer = IntPtr.Zero;
106         private static ExecuteFunctionDelegate _executeDelegate = null;
107 
108         private static IntPtr _disposeDelegatePointer = IntPtr.Zero;
109         private static DisposeFunctionDelegate _disposeDelegate = null;
110 
111         private static IntPtr _roleDelegatePointer = IntPtr.Zero;
112         private static RoleFunctionDelegate _roleDelegate = null;
113 
114         private static IntPtr _principalDelegatePointer = IntPtr.Zero;
115         private static PrincipalFunctionDelegate _principalDelegate = null;
116 
GetAsyncCompletionDelegate()117         public IntPtr GetAsyncCompletionDelegate() {
118             if (IntPtr.Zero == _asyncCompletionDelegatePointer) {
119                 lock (_delegatelock) {
120                     if (IntPtr.Zero == _asyncCompletionDelegatePointer) {
121                         AsyncCompletionDelegate d = new AsyncCompletionDelegate(AsyncCompletionHandler);
122                         if (null != d) {
123                             IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
124                             if (IntPtr.Zero != p) {
125                                 _asyncCompletionDelegate = d;
126                                 _asyncCompletionDelegatePointer = p;
127                             }
128                         }
129                     }
130                 }
131             }
132             return _asyncCompletionDelegatePointer;
133         }
134 
GetAsyncDisconnectNotificationDelegate()135         public IntPtr GetAsyncDisconnectNotificationDelegate() {
136             if (IntPtr.Zero == _asyncDisconnectNotificationDelegatePointer) {
137                 lock (_delegatelock) {
138                     if (IntPtr.Zero == _asyncDisconnectNotificationDelegatePointer) {
139                         AsyncDisconnectNotificationDelegate d = new AsyncDisconnectNotificationDelegate(AsyncDisconnectNotificationHandler);
140                         if (null != d) {
141                             IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
142                             if (IntPtr.Zero != p) {
143                                 _asyncDisconnectNotificationDelegate = d;
144                                 _asyncDisconnectNotificationDelegatePointer = p;
145                             }
146                         }
147                     }
148                 }
149             }
150             return _asyncDisconnectNotificationDelegatePointer;
151         }
152 
GetExecuteDelegate()153         public IntPtr GetExecuteDelegate() {
154             if (IntPtr.Zero == _executeDelegatePointer) {
155                 lock (_delegatelock) {
156                     if (IntPtr.Zero == _executeDelegatePointer) {
157                         ExecuteFunctionDelegate d = new ExecuteFunctionDelegate(ProcessRequestNotification);
158                         if (null != d) {
159                             IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
160                             if (IntPtr.Zero != p) {
161                                 Thread.MemoryBarrier();
162                                 _executeDelegate = d;
163                                 _executeDelegatePointer = p;
164                             }
165                         }
166                     }
167                 }
168             }
169 
170             return _executeDelegatePointer;
171         }
172 
GetDisposeDelegate()173         public IntPtr GetDisposeDelegate() {
174             if (IntPtr.Zero == _disposeDelegatePointer) {
175                 lock (_delegatelock) {
176                     if (IntPtr.Zero == _disposeDelegatePointer) {
177                         DisposeFunctionDelegate d = new DisposeFunctionDelegate(DisposeHandler);
178                         if (null != d) {
179                             IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
180                             if (IntPtr.Zero != p) {
181                                 Thread.MemoryBarrier();
182                                 _disposeDelegate = d;
183                                 _disposeDelegatePointer = p;
184                             }
185                         }
186                     }
187                 }
188             }
189 
190             return _disposeDelegatePointer;
191         }
192 
GetRoleDelegate()193         public IntPtr GetRoleDelegate() {
194             if (IntPtr.Zero == _roleDelegatePointer) {
195                 lock (_delegatelock) {
196                     if (IntPtr.Zero == _roleDelegatePointer) {
197                         RoleFunctionDelegate d = new RoleFunctionDelegate(RoleHandler);
198                         if (null != d) {
199                             IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
200                             if (IntPtr.Zero != p) {
201                                 Thread.MemoryBarrier();
202                                 _roleDelegate = d;
203                                 _roleDelegatePointer = p;
204                             }
205                         }
206                     }
207                 }
208             }
209 
210             return _roleDelegatePointer;
211         }
212 
GetPrincipalDelegate()213         public IntPtr GetPrincipalDelegate() {
214             if (IntPtr.Zero == _principalDelegatePointer) {
215                 lock (_delegatelock) {
216                     if (IntPtr.Zero == _principalDelegatePointer) {
217                         PrincipalFunctionDelegate d = new PrincipalFunctionDelegate(GetManagedPrincipalHandler);
218                         if (null != d) {
219                             IntPtr p = Marshal.GetFunctionPointerForDelegate(d);
220                             if (IntPtr.Zero != p) {
221                                 Thread.MemoryBarrier();
222                                 _principalDelegate = d;
223                                 _principalDelegatePointer = p;
224                             }
225                         }
226                     }
227                 }
228             }
229 
230             return _principalDelegatePointer;
231         }
232 
233 
234 
235         [SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
PipelineRuntime()236         public PipelineRuntime() {
237             HostingEnvironment.RegisterObject(this);
238             Debug.Trace("PipelineDomain", "RegisterObject(this) called");
239         }
240 
241         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)]
InitializeLifetimeService()242         public override Object InitializeLifetimeService() {
243             return null; // never expire lease
244         }
245 
StartProcessing()246         public void StartProcessing() {
247             Debug.Trace("PipelineDomain", "StartProcessing AppId = " + s_thisAppDomainsIsapiAppId);
248             HostingEnvironment.SetupStopListeningHandler();
249         }
250 
251         [EnvironmentPermission(SecurityAction.Assert, Unrestricted = true)]
StopProcessing()252         public void StopProcessing() {
253             Debug.Trace("PipelineDomain", "StopProcessing with stack = " + Environment.StackTrace
254                         + " for AppId= " +  s_thisAppDomainsIsapiAppId);
255 
256             if (!HostingEnvironment.StopListeningWasCalled && !HostingEnvironment.ShutdownInitiated) {
257                 // If GL_STOP_LISTENING wasn't triggered, the reset is likely due to a configuration change.
258                 HttpRuntime.SetShutdownReason(ApplicationShutdownReason.ConfigurationChange, "IIS configuration change");
259             }
260 
261             s_StopProcessingCalled = true;
262             // inititate shutdown and
263             // require the native callback for Stop
264             HostingEnvironment.InitiateShutdownWithoutDemand();
265         }
266 
WaitForRequestsToDrain()267         internal static void WaitForRequestsToDrain() {
268             if (s_ApplicationContext == IntPtr.Zero) {
269                 // If InitializeApplication was never called, then no requests ever came in and StopProcessing will never be called.
270                 // We can just short-circuit this method.
271                 return;
272             }
273 
274             while (!s_StopProcessingCalled || _inIndicateCompletionCount > 0) {
275                 Thread.Sleep(250);
276             }
277         }
278 
FormatExceptionMessage(Exception e, string[] strings)279         private StringBuilder FormatExceptionMessage(Exception e, string[] strings) {
280             StringBuilder sb = new StringBuilder(4096);
281 
282             if (null != strings) {
283                 for (int i = 0; i < strings.Length; i++) {
284                     sb.Append(strings[i]);
285                 }
286             }
287             for (Exception current = e; current != null; current = current.InnerException) {
288                 if (current == e)
289                     sb.Append("\r\n\r\nException: ");
290                 else
291                     sb.Append("\r\n\r\nInnerException: ");
292                 sb.Append(current.GetType().FullName);
293                 sb.Append("\r\nMessage: ");
294                 sb.Append(current.Message);
295                 sb.Append("\r\nStackTrace: ");
296                 sb.Append(current.StackTrace);
297             }
298 
299             return sb;
300         }
301 
InitializeApplication(IntPtr appContext)302         public void InitializeApplication(IntPtr appContext)
303         {
304             s_ApplicationContext = appContext;
305 
306             // DevDiv #381425 - webengine4!RegisterModule runs *after* HostingEnvironment.Initialize (and thus the
307             // HttpRuntime static ctor) when application preload is active. This means that any global state set
308             // by RegisterModule (like the IIS version information, whether we're in integrated mode, misc server
309             // info, etc.) will be unavailable to PreAppStart / preload code when the preload feature is active.
310             // But since RegisterModule runs before InitializeApplication, we have one last chance here to collect
311             // the information before the main part of the application starts, and the pipeline can depend on it
312             // to be accurate.
313             HttpRuntime.PopulateIISVersionInformation();
314 
315             HttpApplication app = null;
316 
317             try {
318                 // if HttpRuntime.HostingInit failed, do not attempt to create the application (WOS #1653963)
319                 if (!HttpRuntime.HostingInitFailed) {
320                     //
321                     //  On IIS7, application initialization does not provide an http context.  Theoretically,
322                     //  no one should be using the context during application initialization, but people do.
323                     //  Create a dummy context that is used during application initialization
324                     //  to prevent breakage (ISAPI mode always provides a context)
325                     //
326                     HttpWorkerRequest initWorkerRequest = new SimpleWorkerRequest("" /*page*/,
327                                                                                   "" /*query*/,
328                                                                                   new StringWriter(CultureInfo.InvariantCulture));
329                     MimeMapping.SetIntegratedApplicationContext(appContext);
330                     HttpContext initHttpContext = new HttpContext(initWorkerRequest);
331                     app = HttpApplicationFactory.GetPipelineApplicationInstance(appContext, initHttpContext);
332                 }
333             }
334             catch(Exception e)
335             {
336                 if (HttpRuntime.InitializationException == null) {
337                     HttpRuntime.InitializationException = e;
338                 }
339             }
340             finally {
341                 s_InitializationCompleted = true;
342 
343                 if (HttpRuntime.InitializationException != null) {
344 
345                     // at least one module must be registered so that we
346                     // call ProcessRequestNotification later and send the formatted
347                     // InitializationException to the client.
348                     int hresult = UnsafeIISMethods.MgdRegisterEventSubscription(
349                         appContext,
350                         InitExceptionModuleName,
351                         RequestNotification.BeginRequest,
352                         0 /*postRequestNotifications*/,
353                         InitExceptionModuleName,
354                         s_InitExceptionModulePrecondition,
355                         new IntPtr(-1),
356                         false /*useHighPriority*/);
357 
358                     if (hresult < 0) {
359                         throw new COMException( SR.GetString(SR.Failed_Pipeline_Subscription, InitExceptionModuleName),
360                                                 hresult );
361                     }
362 
363                     // Always register a managed handler:
364                     // WOS 1990290: VS F5 Debugging: "AspNetInitializationExceptionModule" is registered for RQ_BEGIN_REQUEST,
365                     // but the DEBUG verb skips notifications until post RQ_AUTHENTICATE_REQUEST.
366                     hresult = UnsafeIISMethods.MgdRegisterEventSubscription(
367                         appContext,
368                         HttpApplication.IMPLICIT_HANDLER,
369                         RequestNotification.ExecuteRequestHandler /*requestNotifications*/,
370                         0 /*postRequestNotifications*/,
371                         String.Empty /*type*/,
372                         HttpApplication.MANAGED_PRECONDITION /*precondition*/,
373                         new IntPtr(-1),
374                         false /*useHighPriority*/);
375 
376                     if (hresult < 0) {
377                         throw new COMException( SR.GetString(SR.Failed_Pipeline_Subscription, HttpApplication.IMPLICIT_HANDLER),
378                                                 hresult );
379                     }
380                 }
381 
382                 if (app != null) {
383                     HttpApplicationFactory.RecyclePipelineApplicationInstance(app);
384                 }
385             }
386         }
387 
UnwrapContext(IntPtr rootedObjectsPointer)388         private static HttpContext UnwrapContext(IntPtr rootedObjectsPointer) {
389             RootedObjects objects = RootedObjects.FromPointer(rootedObjectsPointer);
390             return objects.HttpContext;
391         }
392 
393         internal bool HostingShutdownInitiated {
394             get {
395                 return HostingEnvironment.ShutdownInitiated;
396             }
397         }
398 
399         // called from native code when the IHttpContext is disposed
AsyncCompletionHandler(IntPtr rootedObjectsPointer, int bytesCompleted, int hresult, IntPtr pAsyncCompletionContext)400         internal static void AsyncCompletionHandler(IntPtr rootedObjectsPointer, int bytesCompleted, int hresult, IntPtr pAsyncCompletionContext) {
401             HttpContext context = UnwrapContext(rootedObjectsPointer);
402             IIS7WorkerRequest wr = context.WorkerRequest as IIS7WorkerRequest;
403             wr.OnAsyncCompletion(bytesCompleted, hresult, pAsyncCompletionContext);
404         }
405 
406         // called from native code when the IHttpConnection is disconnected
AsyncDisconnectNotificationHandler(IntPtr pManagedRootedObjects)407         internal static void AsyncDisconnectNotificationHandler(IntPtr pManagedRootedObjects) {
408             // Every object we're about to call into should be live / non-disposed,
409             // but since we're paranoid we should put guard clauses everywhere.
410 
411             Debug.Assert(pManagedRootedObjects != IntPtr.Zero);
412             if (pManagedRootedObjects != IntPtr.Zero) {
413                 RootedObjects rootObj = RootedObjects.FromPointer(pManagedRootedObjects);
414                 Debug.Assert(rootObj != null);
415                 if (rootObj != null) {
416                     IIS7WorkerRequest workerRequest = rootObj.WorkerRequest;
417                     Debug.Assert(workerRequest != null);
418                     if (workerRequest != null) {
419                         workerRequest.NotifyOfAsyncDisconnect();
420                     }
421                 }
422             }
423         }
424 
425         // Called from native code to see if a principal is in a given role
RoleHandler(IntPtr pRootedObjects, IntPtr pszRole, int cchRole, out bool isInRole)426         internal static int RoleHandler(IntPtr pRootedObjects, IntPtr pszRole, int cchRole, out bool isInRole) {
427             isInRole = false;
428             IPrincipal principal = RootedObjects.FromPointer(pRootedObjects).Principal;
429             if (principal != null) {
430                 try {
431                     isInRole = principal.IsInRole(StringUtil.StringFromWCharPtr(pszRole, cchRole));
432                 }
433                 catch (Exception e) {
434                     return Marshal.GetHRForException(e);
435                 }
436             }
437             return HResults.S_OK;
438         }
439 
440         // Called from native code to get the managed principal for a given request
441         // If the return value is non-zero, the caller must free the returned GCHandle
GetManagedPrincipalHandler(IntPtr pRootedObjects, int requestingAppDomainId)442         internal static IntPtr GetManagedPrincipalHandler(IntPtr pRootedObjects, int requestingAppDomainId) {
443             // DevDiv 375079: Server.TransferRequest can be used to transfer requests to different applications,
444             // which means that we might be trying to pass a GCHandle to the IPrincipal object to a different
445             // AppDomain, which is disallowed. If this happens, we just tell our caller that we can't give him
446             // a managed IPrincipal object.
447             if (requestingAppDomainId != AppDomain.CurrentDomain.Id) {
448                 return IntPtr.Zero;
449             }
450 
451             IPrincipal principal = RootedObjects.FromPointer(pRootedObjects).Principal;
452             return GCUtil.RootObject(principal);
453         }
454 
455         // called from native code when the IHttpContext is disposed
DisposeHandler(IntPtr rootedObjectsPointer)456         internal static void DisposeHandler(IntPtr rootedObjectsPointer) {
457             RootedObjects root = RootedObjects.FromPointer(rootedObjectsPointer);
458             root.Destroy();
459         }
460 
461         // called from managed code as a perf optimization to avoid calling back later
DisposeHandler(HttpContext context, IntPtr nativeRequestContext, RequestNotificationStatus status)462         internal static void DisposeHandler(HttpContext context, IntPtr nativeRequestContext, RequestNotificationStatus status) {
463             if (IIS.MgdCanDisposeManagedContext(nativeRequestContext, status)) {
464                 context.RootedObjects.Destroy();
465             }
466         }
467 
468         //
469         // This is the managed entry point for processing request notifications.
470         // Although this method is wrapped in try/catch, it is not supposed to
471         // cause an exception. If it does throw, the application, httpwriter, etc
472         // may not be initialized, and it might not be possible to process the rest
473         // of the request. I would prefer to let this method throw and crash the
474         // process, but for now we will consume the exception, report the error to
475         // IIS, and continue.
476         //
477         // Code that might throw belongs in HttpRuntime::ProcessRequestNotificationPrivate.
478         //
ProcessRequestNotification( IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, int flags)479         internal static int ProcessRequestNotification(
480                 IntPtr rootedObjectsPointer,
481                 IntPtr nativeRequestContext,
482                 IntPtr moduleData,
483                 int flags)
484         {
485             try {
486                 return ProcessRequestNotificationHelper(rootedObjectsPointer, nativeRequestContext, moduleData, flags);
487             }
488             catch(Exception e) {
489                 ApplicationManager.RecordFatalException(e);
490                 throw;
491             }
492         }
493 
ProcessRequestNotificationHelper( IntPtr rootedObjectsPointer, IntPtr nativeRequestContext, IntPtr moduleData, int flags)494         internal static int ProcessRequestNotificationHelper(
495                 IntPtr rootedObjectsPointer,
496                 IntPtr nativeRequestContext,
497                 IntPtr moduleData,
498                 int flags)
499         {
500             IIS7WorkerRequest wr = null;
501             HttpContext context = null;
502             RequestNotificationStatus status = RequestNotificationStatus.Continue;
503             RootedObjects root;
504             bool workerRequestWasJustCreated = false;
505 
506             if (rootedObjectsPointer == IntPtr.Zero) {
507                 InitializeRequestContext(nativeRequestContext, flags, out wr, out context);
508                 workerRequestWasJustCreated = true;
509                 if (context == null) {
510                     return (int)RequestNotificationStatus.FinishRequest;
511                 }
512 
513                 root = RootedObjects.Create();
514                 root.HttpContext = context;
515                 root.WorkerRequest = wr;
516                 root.WriteTransferEventIfNecessary();
517                 context.RootedObjects = root;
518 
519                 IIS.MgdSetManagedHttpContext(nativeRequestContext, root.Pointer);
520             }
521             else {
522                 root = RootedObjects.FromPointer(rootedObjectsPointer);
523                 context = root.HttpContext;
524                 wr = root.WorkerRequest as IIS7WorkerRequest;
525             }
526 
527             Debug.Assert(root != null, "We should have a RootedObjects instance by this point.");
528             Debug.Assert(wr != null, "We should have an IIS7WorkerRequest instance by this point.");
529 
530             using (root.WithinTraceBlock()) {
531                 if (workerRequestWasJustCreated) {
532                     AspNetEventSource.Instance.RequestStarted(wr);
533                 }
534 
535                 int currentModuleIndex;
536                 bool isPostNotification;
537                 int currentNotification;
538                 IIS.MgdGetCurrentNotificationInfo(nativeRequestContext, out currentModuleIndex, out isPostNotification, out currentNotification);
539 
540                 // If the HttpContext is null at this point, then we've already transitioned this request to a WebSockets request.
541                 // The WebSockets module should already be running, and asynchronous module-level events (like SendResponse) are
542                 // ineligible to be hooked by managed code.
543                 if (context == null || context.HasWebSocketRequestTransitionStarted) {
544                     return (int)RequestNotificationStatus.Continue;
545                 }
546 
547                 // It is possible for a notification to complete asynchronously while we're in
548                 // a call to IndicateCompletion, in which case a new IIS thread might enter before
549                 // the call to IndicateCompletion returns.  If this happens, block the thread until
550                 // IndicateCompletion returns.  But never block a SendResponse notification, because
551                 // that can cause the request to hang (DevDiv Bugs 187441).
552                 if (context.InIndicateCompletion
553                     && context.ThreadInsideIndicateCompletion != Thread.CurrentThread
554                     && RequestNotification.SendResponse != (RequestNotification)currentNotification) {
555                     while (context.InIndicateCompletion) {
556                         Thread.Sleep(10);
557                     }
558                 }
559 
560                 // RQ_SEND_RESPONSE fires out of band and completes synchronously only.
561                 // The pipeline must be reentrant to support this, so the notification
562                 // context for the previous notification must be saved and restored.
563                 NotificationContext savedNotificationContext = context.NotificationContext;
564                 bool cancellable = context.IsInCancellablePeriod;
565                 bool locked = false;
566                 try {
567                     if (cancellable) {
568                         context.EndCancellablePeriod();
569                     }
570                     bool isReEntry = (savedNotificationContext != null);
571                     if (isReEntry) {
572                         context.ApplicationInstance.AcquireNotifcationContextLock(ref locked);
573                     }
574                     context.NotificationContext = new NotificationContext(flags /*CurrentNotificationFlags*/,
575                                                                           isReEntry);
576 
577                     Action<RequestNotificationStatus> verifierCheck = null;
578                     if (AppVerifier.IsAppVerifierEnabled) {
579                         verifierCheck = AppVerifier.GetRequestNotificationStatusCheckDelegate(context, (RequestNotification)currentNotification, isPostNotification);
580                     }
581 
582                     status = HttpRuntime.ProcessRequestNotification(wr, context);
583 
584                     if (verifierCheck != null) {
585                         AppVerifier.InvokeVerifierCheck(verifierCheck, status);
586                     }
587                 }
588                 finally {
589                     if (status != RequestNotificationStatus.Pending) {
590                         // if we completed the notification, pop the notification context stack
591                         // if this is an asynchronous unwind, then the completion will clear the context
592                         context.NotificationContext = savedNotificationContext;
593 
594                         // DevDiv 112755 restore cancellable state if its changed
595                         if (cancellable && !context.IsInCancellablePeriod) {
596                             context.BeginCancellablePeriod();
597                         } else if (!cancellable && context.IsInCancellablePeriod) {
598                             context.EndCancellablePeriod();
599                         }
600                     }
601                     if (locked) {
602                         context.ApplicationInstance.ReleaseNotifcationContextLock();
603                     }
604                 }
605 
606                 if (status != RequestNotificationStatus.Pending) {
607                     // The current notification may have changed due to the HttpApplication progressing the IIS state machine, so retrieve the info again.
608                     IIS.MgdGetCurrentNotificationInfo(nativeRequestContext, out currentModuleIndex, out isPostNotification, out currentNotification);
609 
610                     // WOS 1785741: (Perf) In profiles, 8% of HelloWorld is transitioning from native to managed.
611                     // The fix is to keep managed code on the stack so that the AppDomain context remains on the
612                     // thread, and we can re-enter managed code without setting up the AppDomain context.
613                     // If this optimization is possible, MgdIndicateCompletion will execute one or more notifications
614                     // and return PENDING as the status.
615                     ThreadContext threadContext = context.IndicateCompletionContext;
616                     // DevDiv 482614:
617                     // Don't use local copy to detect if we can call MgdIndicateCompletion because another thread
618                     // unwinding from MgdIndicateCompletion may be changing context.IndicateCompletionContext at the same time.
619                     if (!context.InIndicateCompletion && context.IndicateCompletionContext != null) {
620                         if (status == RequestNotificationStatus.Continue) {
621                             try {
622                                 context.InIndicateCompletion = true;
623                                 Interlocked.Increment(ref _inIndicateCompletionCount);
624                                 context.ThreadInsideIndicateCompletion = Thread.CurrentThread;
625                                 IIS.MgdIndicateCompletion(nativeRequestContext, ref status);
626                             }
627                             finally {
628                                 context.ThreadInsideIndicateCompletion = null;
629                                 Interlocked.Decrement(ref _inIndicateCompletionCount);
630 
631                                 // Leave will have been called already if the last notification is returning pending
632                                 // DTS267762: Make sure InIndicateCompletion is released, not based on the thread context state
633                                 // Otherwise the next request notification may deadlock
634                                 if (!threadContext.HasBeenDisassociatedFromThread || context.InIndicateCompletion) {
635                                     lock (threadContext) {
636                                         if (!threadContext.HasBeenDisassociatedFromThread) {
637                                             threadContext.DisassociateFromCurrentThread();
638                                         }
639 
640                                         context.IndicateCompletionContext = null;
641                                         context.InIndicateCompletion = false;
642                                     }
643                                 }
644                             }
645                         }
646                         else {
647                             if (!threadContext.HasBeenDisassociatedFromThread || context.InIndicateCompletion) {
648                                 lock (threadContext) {
649                                     if (!threadContext.HasBeenDisassociatedFromThread) {
650                                         threadContext.DisassociateFromCurrentThread();
651                                     }
652 
653                                     context.IndicateCompletionContext = null;
654                                     context.InIndicateCompletion = false;
655                                 }
656                             }
657                         }
658                     }
659                 }
660 
661                 if (context.HasWebSocketRequestTransitionStarted && status == RequestNotificationStatus.Pending) {
662                     // At this point, the WebSocket module event (PostEndRequest) has executed and set up the appropriate contexts for us.
663                     // However, there is a race condition that we need to avoid. It is possible that one thread has kicked off some async
664                     // work, e.g. via an IHttpAsyncHandler, and that thread is unwinding and has reached this line of execution.
665                     // Meanwhile, the IHttpAsyncHandler completed quickly (but asynchronously) and invoked MgdPostCompletion, which
666                     // resulted in a new thread calling ProcessRequestNotification. If this second thread starts the WebSocket transition,
667                     // then there's the risk that *both* threads might attempt to call WebSocketPipeline.ProcessRequest, which could AV
668                     // the process.
669                     //
670                     // We protect against this by allowing only the thread which started the transition to complete the transition, so in
671                     // the above scenario the original thread (which invoked the IHttpAsyncHandler) no-ops at this point and just returns
672                     // Pending to its caller.
673 
674                     if (context.DidCurrentThreadStartWebSocketTransition) {
675                         // We'll mark the HttpContext as complete, call the continuation to kick off the socket send / receive loop, and return
676                         // Pending to IIS so that it doesn't advance the state machine until the WebSocket loop completes.
677                         root.ReleaseHttpContext();
678                         root.WebSocketPipeline.ProcessRequest();
679                     }
680                 }
681 
682                 return (int)status;
683             }
684         }
685 
InitializeRequestContext(IntPtr nativeRequestContext, int flags, out IIS7WorkerRequest wr, out HttpContext context)686         private static void InitializeRequestContext(IntPtr nativeRequestContext, int flags, out IIS7WorkerRequest wr, out HttpContext context) {
687             wr = null;
688             context = null;
689             try {
690                 bool etwEnabled = ((flags & HttpContext.FLAG_ETW_PROVIDER_ENABLED) == HttpContext.FLAG_ETW_PROVIDER_ENABLED);
691 
692                 // this may throw, e.g. if the request Content-Length header has a value greater than Int32.MaxValue
693                 wr = IIS7WorkerRequest.CreateWorkerRequest(nativeRequestContext, etwEnabled);
694 
695                 // this may throw, e.g. see WOS 1724573: ASP.Net v2.0: wrong error code returned when ? is used in the URL
696                 context = new HttpContext(wr, false);
697             }
698             catch {
699                 // treat as "400 Bad Request" since that's the only reason the HttpContext.ctor should throw
700                 IIS.MgdSetBadRequestStatus(nativeRequestContext);
701             }
702         }
703 
704         /// <include file='doc\ISAPIRuntime.uex' path='docs/doc[@for="ISAPIRuntime.IRegisteredObject.Stop"]/*' />
705         /// <internalonly/>
IRegisteredObject.Stop(bool immediate)706         void IRegisteredObject.Stop(bool immediate) {
707             Debug.Trace("PipelineDomain", "IRegisteredObject.Stop appId = " +
708                         s_thisAppDomainsIsapiAppId);
709 
710             while (!s_InitializationCompleted && !s_StopProcessingCalled) {
711                 // the native W3_MGD_APP_CONTEXT is not ready for us to unload
712                 Thread.Sleep(250);
713             }
714 
715             RemoveThisAppDomainFromUnmanagedTable();
716             HostingEnvironment.UnregisterObject(this);
717         }
718 
SetThisAppDomainsIsapiAppId(String appId)719         internal void SetThisAppDomainsIsapiAppId(String appId) {
720             Debug.Trace("PipelineDomain", "SetThisAppDomainsPipelineAppId appId=" + appId);
721             s_thisAppDomainsIsapiAppId = appId;
722         }
723 
RemoveThisAppDomainFromUnmanagedTable()724         internal static void RemoveThisAppDomainFromUnmanagedTable() {
725             if (Interlocked.Exchange(ref s_isThisAppDomainRemovedFromUnmanagedTable, 1) != 0) {
726                 return;
727             }
728 
729             //
730             // only notify mgdeng of this shutdown if we went through
731             // Initialize from the there
732             // We can also have PipelineRuntime in app domains with only
733             // other protocols
734             //
735             try {
736                 if (s_thisAppDomainsIsapiAppId != null  && s_ApplicationContext != IntPtr.Zero) {
737                     Debug.Trace("PipelineDomain", "Calling MgdAppDomainShutdown appId=" +
738                         s_thisAppDomainsIsapiAppId + " (AppDomainAppId=" + HttpRuntime.AppDomainAppId + ")");
739 
740                     UnsafeIISMethods.MgdAppDomainShutdown(s_ApplicationContext);
741                 }
742 
743                 HttpRuntime.AddAppDomainTraceMessage(SR.GetString(SR.App_Domain_Restart));
744             }
745             catch(Exception e) {
746                 if (ShouldRethrowException(e)) {
747                     throw;
748                 }
749             }
750         }
751 
ShouldRethrowException(Exception ex)752         internal static bool ShouldRethrowException(Exception ex) {
753             return     ex is NullReferenceException
754                     || ex is AccessViolationException
755                     || ex is StackOverflowException
756                     || ex is OutOfMemoryException
757                     || ex is System.Threading.ThreadAbortException;
758         }
759 
760     }
761 }
762