I'm at DevTeach this week and yesterday I ran my Low Level ASP.NET Session here. I like doing this session – it's one of those that I don't get to do often and it's one of those deep topics that are fun to get wrapped up in for an hour or so <g>.
Anyway, it talks about low level aspects of ASP.NET up to the HttpHandler level. In discussion of the ASP.NET pipeline firing off the HttpApplication events, I ran into a somewhat embarrassing snag that I apparently hadn't checked out recently in one of my demos. The events off the HttpApplication object are essentially what drives the pipeline with both direct event hookups and HttpModules that hook into these events via Web.Config and the IHttpModule interface.
In the past I've always stressed one thing to be aware of in that ASP.NET can shortcircuit the pipeline if HttpApplication.CompleteRequest() and Response.End() (which used to implicitly call HttpApplication.CompleteRequest). So I have one demo that shows some page logic that calls Response.End(), while I have an HTTP Module hooked up that adds a footer to every page. It was supposed to short circuit and NOT show the footer, but in good Murphy's Law tradition it of course showed the footer… Ooops. <g>
The trivial code I use for the sample is something like this:
protected void Page_Load(object sender, System.EventArgs e)
{
Response.Write("<h1>Hello Cruel World</h1>");
if (this.chkResponseEnd.Checked)
{
Response.End();
Response.Write("<h1>Goodbye Cruel World</h1>");
}
}
There's a checkbox on the form and when checked Response.End() is called.
But alas in ASP.NET 2.0 when I call it, the HTML content added by the module that adds a footer to the page continues to display just fine.
Now I can still force the pipeline to short-circuit, but I have to explicitly call on the HttpApplicationInstance and CompleteRequest:
HttpContext.Current.ApplicationInstance.CompleteRequest();
Response.End();
Now the short circuiting does happen as before.
It looks like there's a definite (and welcome) behavior change in ASP.NET that causes Response.End() no longer to short circuit the Http Pipeline. This is a good thing because in most situations you don't actually want the short circuiting to happen. As in this scenario I would much prefer to have the pipeline continue processing and put my footer on the page even though I issued a Response.End(). There maybe other things happening too - compression of output, special formatting - who knows what that should still happen even though the Response has effectively been killed.
It looks like this is what Response.End() does now. But, curious person that I am I wanted to check out what ASP.NET is doing differently here so I dug out Reflector and checked out Response.End:
public void End()
{
if (this._context.IsInCancellablePeriod)
{
InternalSecurityPermissions.ControlThread.Assert();
Thread.CurrentThread.Abort(new HttpApplication.CancelModuleException(false));
}
else if (!this._flushing)
{
this.Flush();
this._ended = true;
if (this._context.ApplicationInstance != null)
{
this._context.ApplicationInstance.CompleteRequest();
}
}
}
Looking at this code I'm actually not sure what ASP.NET is doing to keep the processing of the Pipeline alive. In both of the conditional paths it seems like the Pipeline is in fact short circuited – Thread.Abort() certainly will cause an exception in the application (unless the CancelModuleException holds some significance here) and terminate the request. And the second path calls HttpApplication.CompleteRequest() which as shown above does bypass the pipeline (except for the HttpApplication.EndRequest event which is guaranteed to fire). So it seems odd that the Response.End() call is in fact firing the rest of the events.
Moral of the story for me: I better double check my demos more carefully under 2.0 <g>. Demonstrating this particular demo will have to include the CompleteRequest() call, which is still important to 'warn' about. If you have criticical behavior in a handler that does things like validation or licensing/limiting of some sort the abillity to short circuit with CompleteRequest() puts a crimp in that scenario as pages can potentially bypass this behavior with some strategically placed code.
Other Posts you might also like