.nethttpruntime

Why does HttpWorkerRequest fail during HttpRuntime.ProcessRequest after a .NET 2.0 to .NET 4.0 upgrade?


I am upgrading our application, which has an internal webserver, from .NET 2.0 to .NET 4.0.

I am handling a request with an object HttpListenerWorkerRequest, that extends the HttpWorkerRequest class, and creates a request which GetRawUrl() returns a Url in the format of http://localhost:82/Default.aspx.

In .NET 2.0, sending this to HttpRuntime.ProcessRequest(httpListenerWorkerRequest) works without issue, however in .NET 4.0, I get a web page with the nothing but the text "Bad Request" on it.

Cracking open HttpRuntime, I can see that Bad Requests are thrown from ProcessRequestInternal(HttpWorkerRequest wr), a private method that tries to build an HttpContext.

I tried this myself:

try
{
    //what's going on?
    hcontext = new HttpContext(workerRequest);
}
catch(Exception e)
{
    //Debugging break point here
}

Pre-update (.NET 2.0), it builds fine, post-update (.NET 4.0), I get a System.ArgumentException stating that

The relative virtual path 'http:/localhost:82/Default.aspx' is not allowed here, thrown at

at System.Web.VirtualPath.Create(String virtualPath, VirtualPathOptions options)
   at System.Web.HttpRequest.get_ClientFilePath()
   at System.Web.Security.CookielessHelperClass.RemoveCookielessValuesFromPath()
   at System.Web.HttpContext.Init(HttpRequest request, HttpResponse response)
   at System.Web.HttpContext..ctor(HttpWorkerRequest wr)
   at Talcasoft.Web.Hosting.HttpWorkerThread.Run(Object request) in      
   C:\[OurLibrary].Web\Hosting\HttpWorkerThread.cs:line 51

What has changed in .NET to cause this, and what can I do to get around it?

EDIT I have just noticed that the disallowed http: is followed by a single slash, not a double, although the GetRawUrl() in the request certainly returns a double.


Solution

  • I'm not a 100% certain that this is the 'exact answer' but looks pretty close to me - and there is some more to write...

    There seems to be a breaking change of a sort inside the VirtualPath class - and it's substantiated with how they are checking for illegal characters. (btw. you can google the 'VirtualPath source' for what seems a .NET 4 version of it).

    Inside VirtualPath.Create a check is invoked for 'illegal virtual path characters'.

    It first goes into registry ("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET", "VerificationCompatibility") - to see if the compatibility mode 'illegal chars' should be used.

    Based on that - I'm guessing (I don't have a way of checking this right now) - that if you set the above registry value (int) to 1 - you should get your methods working the old way and w/o any additional effort. Note: a IIS (or host process) restart may be required as suggested in one of the links

    And then based on that registry flag it used either of these two...

    ':', '?', '*', '\0' // .NET 4.0 - the default
    '\0' // the 'compatibility' mode  
    

    That seems to actually describe your story quite well as your path with the 'port' designation is actually illegal per the new default.


    FINAL EDIT / EXPLANATION:

    (update based on comments and the solution that solved it)
    This is my understanding of what's going on inside:

    1) Solution for prior to .NET 4.0 was the VerificationCompatibility key (see above).

    2) With .NET 4.0 internal handling and fixing of url paths is made more robust. And works fine in most cases. In short, all paths are fixed and normalized before entering the VirtualPath.Create - and your http://... becomes an expected absolute path /Default.aspx.

    However when you supply the HttpWorkerRequest (instead of Request/Response etc.) - the raw Url is taken directly from the worker - and the responsibility for supplying the correct and normalized paths is down to your worker request. (this is still a bit iffy, and looks like a bug or bad handling internally).

    To reproduce the issue:

    internal class MyRequest : SimpleWorkerRequest
    {
        public MyRequest() : base("", "", String.Empty, String.Empty, null) { }
        public override String GetRawUrl() { return "http://localhost:82/Default.aspx"; }
    }
    // ...
    var wr = new MyRequest();
    var context1 = new HttpContext(wr);
    

    Gives the error The relative virtual path 'http:/localhost:82/Default.aspx' is not allowed here.

    The FIX:

    public override String GetRawUrl() 
    { return new Uri(url, UriKind.RelativeOrAbsolute).AbsolutePath; }
    


    And some of the research on the subject based on that VerificationCompatibility keyword in the registry which seems to be the key to it.

    Ampersand in URL filename = bad request

    Configure IIS to accept URL with Special Characters...

    For 32 vs 64 difference in Registry

    And here is a similar thing from Microsoft - but what seems to be a 'hotfix' for '2.0', i.e. doesn't apply to you - but just attaching it as something official in this line.

    FIX: "HTTP 400 - Bad request" error message in the .NET Framework 1.1
    FIX: Error message when you try to visit an ASP.NET 2.0-based Web page: "HttpException (0x80004005): '/HandlerTest/WebForm1.aspx/a:b' is not a valid virtual path"
    ASP.NET 2.0 x64 – You may get HTTP 400 Bad Request or Error as mentioned in KB 932552 or 826437

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET

    DWord Value Name: VerificationCompatibility  Value Data: 1