I spent some time today to convert an ASP.NET app to run in Integrated Pipeline mode in IIS 7. The process was painless, but of course I wanted to check out some of the new integration features by applying some custom generated content to a static HTML file. In concept very simple, but as it turns out there are few tricks you need to use to manipulate the content correctly.
I set all of this up by taking an existing ASP.NET application and switching it into an Application Pool configured for Integrated Pipeline operation. The Integrated Pipeline essentially merges IIS functionality with ASP.NET’s pipeline so that HttpApplication events fire against every request that is directed at the current application or even the entire Web Server.
I've been using Vista Beta 2 for a couple of weeks now and have been doing all my Web development with IIS 7 and it works great. But I've been running my apps in ISAPI mode rather than the new Integrated Pipeline mode. Integrated Pipeline operation or ISAPI are the two modes supported and they are configured at the Application Pool level in IIS. Switching is easy - it's simply a matter of selecting the application pool and switching the pipeline mode in the basic settings.
Once switched over it’s possible that your app won’t run if you have any custom handlers or modules defined. Specifically you have to move out the <httpHandlers> and <httpModules> section of your Web.config into the a separate and new <system.webserver> general section:
<configuration>
<system.webServer>
<handlers>
<add name="*.wws" path="*.wws" verb="*" type="Westwind.WebStore.PageRedirectorHTTPHandler" preCondition="integratedMode,runtimeVersionv2.0,bitness32" />
</handlers>
<modules>
<add name= "SharewareModule" type="Westwind.WebStore.SharewareModule" preCondition="managedHandler"/>
</modules>
<validation validateIntegratedModeConfiguration="false" />
</system.webServer>
</configuration>
Actually the above was auto-updated by using a command line call to:
%systemroot%\system32\inetsrv\APPCMD.EXE migrate config "West Wind Dev Site/wwstore"
When I originally switched my ASP.NET application’s Application Pool into Integrated mode I got a long and surprisingly detailed error message and the message directed me to the above command line to update my web.config file. Now these are error messages that are actually useful!
What this command does is upgrade your web.config by leaving the old handlers and module section intact and also adding the section above on the bottom. In Integrated mode IIS uses the lower section – if you switch back to ISAPI mode in the Pipeline the application still works with the old settings. Note however, that on older IIS versions the file will fail since the system.webServer section is not legal for Web.config.
With that in place both my handlers and modules run without any problems providing the same functionality as before in plain ASP.NET using the ISAPI or IIS 6 pipeline.
Modules are the fun part
One of the new features of the integrated pipeline is that you can fire your module code against ANY request against the server at the level where the module is defined (in my case at the virtual level for an Application). This means I can apply module code not only against ASP.NET pages (anything that is mapped to the ASP.NET Handler) but against anything including say static HTML pages.
I have one very simple module in my application that pops up a demo message on the bottom of the page when the app is in compiled in demo mode. So I wanted to take advantage of these new features. I ran into some problems with this particular implementation and static content, but it actually provided some good insight in just how IIS 7 works in feeding all this content.
The module above is actually a little silly thing I use to brand the demo versions of the West Wind Web Store, which basically looks at the output and then appends a message to the bottom of the output (if it’s a text/html document that is). It sure would be nice if that also worked against a static page of which there are a few in the local application. Well, now I should be able to.
In operation against ASP.NET manged pages the module worked without any changes, so the original behavior worked just fine. The module is pretty trivial and it turns out that indeed IIS (not I say IIS not ASP.NET – think of system.webServer as IIS) fires the module just fine, but unfortunately I got no banner output for hitting a static HTML page.
Here’s the code:
private void application_PostRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication) sender;
// *** Must make sure we don't add this to data responses!!!
if (app.Context.Response.ContentType.ToLower() == "text/html")
app.Context.Response.Write(SharewareMessage);
}
First shot above – doesn’t work. For a couple of reasons. The first is a simple HTTP issue. Static Pages are served by a built-in handler (StaticFile Handler) so the handler generates the output, creates headers etc. The static file handler adds a Content-Length header to the generated page, and my code that gets added to the page doesn’t affect this content length. Solution to that particular problem:
private void application_PostRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication) sender;
// *** Must make sure we don't add this to data responses!!!
if (app.Context.Response.ContentType.ToLower() == "text/html")
{
app.Context.Response.Headers.Remove("Content-Length");
app.Context.Response.Write(SharewareMessage);
}
}
Remove the Content-Length which removes the originally generated content-length and causes IIS to write the length when output generation is completed.
Running the code with this fix shows that IIS is indeed generating the right content length now, but unfortunately the added text is still not displaying. Looking at the generated HTTP headers shows why:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Encoding: gzip
Last-Modified: Sun, 18 Jun 2006 06:23:28 GMT
Accept-Ranges: bytes
ETag: "07025b69f92c61:0"
Vary: Accept-Encoding
Server: Microsoft-IIS/7.0
X-Powered-By: ASP.NET
Date: Sun, 18 Jun 2006 06:57:53 GMT
Content-Length: 889
Note the GZIP compression. IIS 7 has a static file compression module that is active by default, so my HTML page gets GZIP’d as part of the HTTP Handler operation that ‘creates’ the page output. Once I add my non-GZipped string to this output this output is ignored. The client reads the GZip header and from that header gets the content size which won’t include my generated output.
The brute force solution is to remove the StaticCompression Module:
<system.webServer>
<modules>
<remove name="StaticCompressionModule" />
<add name= "SharewareModule" type="Westwind.WebStore.SharewareModule" />
</modules>
<validation validateIntegratedModeConfiguration="false" />
</system.webServer>
This is truly a brute force approach and there might be a better way to filter this. GZip compression is certainly a useful feature and is applied to images, style sheets etc. so turning it off for all of these just for getting this mechanism to work is a bit much. But at the moment I can’t see how to do that. Notice that these changes are made at the virtual level’s web.config file so they apply only to my Application, not to the whole server (although you can set this up to run at the Web Root or the whole Web Server as well!)
I’m thinking that it would be nice if Modules in general had some sort of filter that allowed to specify what file types they are applied to. In most cases this is not necessary as modules can internally decide what content to run against. But in some scenarios where complex modules like the Compression Module runs it might be nice to exercise some fine grained control and just tell the module to only execute against a certain path or not execute against a certain path structure.
It’s going to take getting used to all this new functionality, but it’s pretty exciting to see this integration and have it work so smoothly using existing concepts that we're familiar with in ASP.NET. The really cool thing about this is that you can write HttpHandlers and HttpModules that fully participate in the IIS pipeline and can run against ANY request, not just ASP.NET requests.
Good stuff.
Other Posts you might also like