8

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.

4

2 回答 2

7

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

于 2013-04-22T01:36:41.797 回答
2

这是我的看法。它仍然包含一些猜测,但我将包含一个测试,您可以进行测试以证明或反驳这个假设。

堆栈跟踪显示ClientFilePath.get为异常的起源。它看起来像这样:

    if (this._clientFilePath == null)
    {
        string rawUrl = this.RawUrl;
        int index = rawUrl.IndexOf('?');
        if (index > -1)
        {
            rawUrl = rawUrl.Substring(0, index);
        }
        this._clientFilePath
     = VirtualPath.Create(rawUrl, VirtualPathOptions.AllowAbsolutePath); //here!
    }
    return this._clientFilePath;

它创建一个VirtualPath并且只允许绝对值。http://localhost:82/Default.aspx是相对路径,因为它不以斜杠开头。在这种情况下,它不是一个 URL,因为它没有被解释为这样。

所以VirtualPath.Create可以理解地否认这条道路。我不知道为什么 .NET 2.0 允许这样做,但 .NET 4.0 需要绝对路径,并且根据代码,这是不可配置的。

实际上,我以前从未见过HttpRequest.RawUrl返回 URL。以我的经验,它应该返回一个绝对路径,例如/Default.aspx. 如果你想设置主机和端口,你必须找到其他方法来做到这一点。

所以解决方法是不使用http://localhost:82/Default.aspxbut /Default.aspx。这对你有用吗?

于 2013-04-22T09:37:54.737 回答