URL Rewrite problem with POST request

Wednesday, July 31, 2013

Greetings

Hello Everyone! In this blog I would like to share my thoughts and findings on programming, software and a like. I will post here so that I can improve my writing skills and find stuff I was working on previously easier.

URL Rewrite

IIS users had waited a bit for Apache equivalent of mod_rewrite. Finally we've got a decent module from Microsoft - Url Rewrite. Among basic features as rewriting all URLs to extension-less equivalents, blocking unwanted URLs or even more common providing friendly URLs the module provides a nice GUI inside IIS Manager so you can quickly test you ideas without referring to documentation. More importantly rewrite rules of your web site can be configured through Web.config configuration section.

The feature I like the most is the ability to create a reversed proxy inside an IIS web site with a little help of another IIS module - Application Request Routing. I have used it couple of times, mostly to be able to make "cross domain" POSTs. One important thing is to remember to enable proxy in ARR settings inside IIS Manager. Following command line will do it for you:
%windir%\system32\inetsrv\appcmd.exe set config -section:system.webServer/proxy /enabled:"True" /commit:apphost

Interfering with ASP.NET modules

URL Rewrite is a native IIS module and from my understanding it deals with HTTP on a pretty low level. Most of custom IIS modules that I've used or built were written in managed code. This means that they use ASP.NET stack that provides some higher level API to deal with requests and responses. However some of those APIs have important side effects one can easily overlook. Why is that important? Because combining several modules into one request/response pipeline can cause hard to debug bugs.
I've came a cross the same problem twice - as it turned out - it was caused exactly by this interference. Specifically after some happy months of rewriting http POSTs using URL Rewrite suddenly features using it stopped working. In both cases the problem was caused by seemingly not intrusive call:
var request = HttpContext.Current.Request;
var parameter = request["formOrQueryOrServerVariableKey"];
As you may already know this code looks for a value inside QueryString, Form, Cookies, or ServerVariables collection member specified in the key parameter. The important part is a Form which is an abstraction over html form that typically translates to http POST body. ASP.NET reads the body of a http request and decodes form url encoded values into NameValueCollection. This is done by reading input stream of the request which internally is buffered on disk. However since this actually is a network stream it can only be read once. This of course means that depending on the order of modules and at which stage of request pipeline one module decides to read it the other ones may not be able to access it. Exactly this scenario caused URL Rewrited POST request to hang until timeout.

Glimpse

One of the modules affected by this problem is part of a great tool called Glimpse. After an hour of looking through code base of this diagnostic tool I've found what I was looking for inside RequestMetadata class. Specifically RequestIsAjax method that in turn is called by AjaxPolicy during BeginRequest event. I've created a pull request that hopefully will fix the issue.

One thing that I definitely will remember from this investigation is to always be very careful while writing ASP.NET http module.