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.
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 workingthe 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.
(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; }
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
Here is my take on it. It still contains some guesswork but I'll include a test that you can make to prove or disprove this hypothesis.
The stack trace shows ClientFilePath.get
as the origin of the exception. It looks like this:
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;
It creates a VirtualPath
and allows only absolute values. http://localhost:82/Default.aspx
is a relative path because it does not start with a slash. It is not a URL in this context because it is not being interpreted as such.
So VirtualPath.Create
understandably denies this path. I don't know why .NET 2.0 allowed this, but .NET 4.0 requires an absolute path and according to the code this is non-configurable.
Actually, I have never seen HttpRequest.RawUrl
return a URL before. In my experience it should return an absolute path like /Default.aspx
. If you want to set the host and port you have to find other means to do that.
So the fix is to not use http://localhost:82/Default.aspx
but /Default.aspx
. Would that work for you?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With