Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uri.TryCreate throws UriFormatException?

Tags:

c#

.net

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?

like image 713
Jim Mischel Avatar asked Jul 13 '09 15:07

Jim Mischel


1 Answers

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.

like image 140
Jim Mischel Avatar answered Nov 13 '22 21:11

Jim Mischel