Wednesday, September 29, 2010

IIS 7.5 – different handling of errors for local vs non-local requests

There are few issues which can be discovered during the migration from IIS 6 to 7.5 (example). One of the issues we’ve been unable to identify for quite some time is connected with “virtual instances” built on top of the url rewriting.

The problem stems from the fact that by default IIS does not handle non-ASP.NET requests using ASP.NET pipeline. This means that while all requests to virtual ASP.NET resources (like http://myhost.com/virtualinstance/theresource.aspx) are handled by ASP.NET (and thus can be rewritten to http://myhost.com/theresource.aspx), all requests to virtual non-ASP.NET resources (like http://myhost.com/virtualinstance/thestyle.css) are not handled by ASP.NET (IIS happily returns 404 since there’s no physical resource under such virtual address).

As I’ve pointed out in the article on virtual instances, our way to overcome this problem was to attach an *.aspx page as a 404 page in the IIS (screenshot taken on IIS 6.0).

This way all requests for non-ASP.NET resources were passed to ASP.NET pipeline as http://myhost.com/ErrorHandler.aspx?404;http://myhost.com/virtualinstance/thestyle.css. This only changes slightly the regular expression you can use to handle “virtual” requests but it’s not a big issue.

Unfortunately, it’s been this trick which turned out to not to work on IIS 7.5. Or, at least, work partially. It turned out that all remote requests were handled correctly, while local requests were handled incorrectly – all “virtual” requests to non-ASP.NET resources were still completely ignored by ASP.NET pipeline. We bravely accepted such behavior for quite some time - business clients send only remote requests so there’s been only an inconvenience for our IT guys who were deploying apps onto a server and trying to quickly verify whether or not the app is running by launching a local browser and pointing it to an app.

But since developers also run & test their applications on their local IISes, they needed a simple solution.

Yes, they could install a virtual machine and send requests between their host machine and the virtual machine (so that all requests would be recognized as remote by IIS).

Another option was to force IIS 7.5 to handle all requests with ASP.NET pipeline:

<system.webServer>
  <modules runAllManagedModulesForAllRequests="true">
     ...
  </modules>
</system.webServer>

but this changes the way ASP.NET sees all virtual requests to non-ASP.NET resources – instead of http://myhost.com/ErrorHandler.aspx?404;http://myhost.com/virtualinstance/thestyle.css you just get http://myhost.com/virtualinstance/thestyle.css. Should IIS be responsible for handling all requests, even to static resources? The question is open but surely, handling all requests with ASP.NET would change the semantics when comparing to IIS 6 so there’s always a chance of some subtle issues and extensive testing would be needed.

Luckily, yet another solution exists. It turns out that the errorMode attribute of the httpErrors section of system.webServer in the web.config which is responsible for storing the mapping of 404 to /ErrorHandler.aspx in IIS 7.5 can be configured in one of three ways:

  • DetailedLocalOnly
  • Detailed
  • Custom

And the problem with IIS 7.5 is that the DetailedLocalOnly is the default setting. This changes the way IIS 7.5 handles errors regarding local requests.

So, to conclude, setting the errorMode to Custom makes the semantics equal for local and remote requests and – to us – also means that the error handling under IIS 7.5 works exactly the same as it did under IIS 6.0.

<system.webServer>
  <httpErrors errorMode="Custom">
    <remove statusCode="404" subStatusCode="-1" />
    <error statusCode="404" prefixLanguageFilePath="" path="/ErrorHandler.aspx" responseMode="ExecuteURL" />
  </httpErrors>
</system.webServer>

No comments: