I have a method that tries to create a Uri and then clean it up (removes fragments, excludes some domains and query string patterns, etc.). The method looks like this:
static public bool TryCreateCleanUri(Uri baseUri, string relstr, out Uri result)
{
if (!Uri.TryCreate(baseUri, relstr, out result))
{
return false;
}
return CleanupUri(result, out result);
}
This method has been working fine for months. But last night it failed. Uri.TryCreate() threw an exception! Here's the stack trace:
ERROR: Unhandled exception caught. Program terminating.
System.UriFormatException: Invalid URI: The hostname could not be parsed.
at System.Uri.CreateHostStringHelper(String str, UInt16 idx, UInt16 end, Flags& flags, String& scopeId)
at System.Uri.CreateHostString()
at System.Uri.GetComponentsHelper(UriComponents uriComponents, UriFormat uriFormat)
at System.Uri.CombineUri(Uri basePart, String relativePart, UriFormat uriFormat)
at System.Uri.GetCombinedString(Uri baseUri, String relativeStr, Boolean dontEscape, String& result)
at System.Uri.ResolveHelper(Uri baseUri, Uri relativeUri, String& newUriString, Boolean& userEscaped, UriFormatException& e)
at System.Uri.TryCreate(Uri baseUri, Uri relativeUri, Uri& result)
at System.Uri.TryCreate(Uri baseUri, String relativeUri, Uri& result)
Documentation for Uri.TryCreate(Uri, String, out Uri)
says that the return value is True
if successful, False
otherwise, but it's silent about exceptions. However, documentation for Uri.TryCreate(Uri, Uri, out Uri)
says:
This method constructs the URI, puts it in canonical form, and validates it. If an unhandled exception occurs, this method catches it. If you want to create a Uri and get exceptions use one of the Uri constructors.
The stack trace shows that the exception was thrown in Uri.TryCreate(Uri, Uri, out Uri)
, which, according to the documentation, shouldn't happen.
This is a very rare occurrence. I've been using that code for months, running literally billions of urls through it, and haven't encountered a problem until now. Unfortunately I don't know what combination of things caused the problem. I'm hoping to construct a test case that shows the error.
Is this a known bug in Uri.TryCreate
, or am I missing something?
Unwilling to wait potentially several months for my code to encounter this situation again, I spent some time with ILDASM to figure out what TryCreate
is doing, and then a little more time coming up with a way to reproduce the error.
The reason for the crash in Uri.TryCreate(Uri baseUri, Uri relativeUri, out Uri result)
appears to be a badly formatted baseUri
. For example, the Uri
constructor allows the following:
Uri badUri = new Uri("mailto:[email protected]@mischel.com");
According to the RFC for mailto: URIs, that shouldn't be allowed. And although the constructor creates and returns a Uri
object, trying to access (some of) its properties throws UriFormatException
. For example, given the above code, this line will throw an exception:
string badUriString = badUri.AbsoluteUri;
I find it rather interesting that the Uri
class appears to use two different parsing algorithms: one used during construction, and one used internally for getting the individual components.
Passing this invalid Uri
to TryCreate
will result in the exception that I described in the original question. The TryCreate
method checks the baseUri
parameter for null
, but doesn't (can't, I would imagine) validate it otherwise. It has to assume that, if the parameter is non-null, the passed object is a fully initialized and valid Uri
instance. But at some point in constructing the result, TryCreate
attempts to obtain the components of baseUri
and an exception is thrown.
I can't say that my program actually encountered a mailto: URL that was formatted this way. I can say with some degree of certainty, though, that an invalid Uri
object was the cause of the crash in my program, simply because the exception stack trace from my program matches the stack trace from the test program. Simply put, the bug is in the Uri
constructor (and also in the TryCreate
methods) which allow the invalid Uri
to be created.
You can follow the bug report on Microsoft Connect.
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