When an HttpApplication processes a client request it uses Execution Steps (IExecutionStep). An array of execution steps is constructed to represent the Http Pipeline. Each Execution Step defines an Execute() method that gets called to process its part of the pipeline. For example, calls to BeginRequest, AuthenticateRequest, AuthorizeRequest, BeginProcessRequest, EndProcessRequest, ect are built and wrapped as steps and then executed by the HttpApplication. Once all steps have completed execution the response is sent back to IIS.
The Handler in Figure 1 sets its IAsyncResult to complete inside the BeginProcessRequest. Figure 2 is the code for the IAsyncResult object and a typical implementation will execute the AsyncCallback method when the operation is complete. This callback is to the CallHandlerExecutionStep class’s method OnAsyncHandlerCompletion. This method first checks the IAsyncResult’s CompletedSynchronously property and if true just exits which allows the current thread to complete and sends the response to the client. Otherwise it calls the Async Handler’s EndProcessRequest method and releases its reference to the handler.
public bool CompletedSynchronously { get { return false; } } public bool IsCompleted { get { return _isCompleted; } }}
If the current thread is a Thread Pool Thread (Thread.CurrentThread.IsThreadPoolThread) the HttpApplication will continue processing the remaining steps and send the response to IIS and then back to the client via a call to ISAPIWorkerRequest.EndOfRequest().
When the stack unwinds back down to where the steps are being executed things start to go bad. Because the CompletedSynchronously of the AsynResult object was not set the HttpApplication halts its execution of the steps as the steps should be completed when the OnAsyncHandlerCompletion callback is invoked. At this point the HttpApplication breaks out of the step execution and attempts to return the thread to its proper culture and security context however the HttpContext has already been released due to the request being previously completed which causes a null reference exception to be thrown. The exception is caught and we attempt to finish the request with an error and we attempt to either build an error page or use a custom error page if one exists. The ISAPIWorkerRequest.EndOfRequest() method is now called which ends up trying to send the Http Error 500 using an ISAPI context/Request that has already sent and causes IIS (w3wp) to AV/crash/exit.