About escaping slashes in .NET

Andrey Akinshin

A puzzle for today: what will this code get?
 

var uri = new Uri("http://localhost/%2F1");
Console.WriteLine(uri.OriginalString);
Console.WriteLine(uri.AbsoluteUri);

The right answer is “it depends”. Let’s sort it out.
 
So, what will it get?
 
The result depends on the .NET version
 

// .NET 4.0
http://localhost/%2F1
http://localhost//1


// .NET 4.5
http://localhost/%2F1
http://localhost/%2F1

 
Why is that?
 
Unfortunately, before .NET 4.0 there was an unpleasant bug with escaping slashes (aka %2F). In .NET 4.5 they decided to fix this bug to address RFC 3986. It seems they did the right thing, but they created some more headaches for developers who don’t know about this tiny nuance: now escaping mechanism depends on the .NET version. It would be better to use the right mechanism from .NET 4.5. But what if you don’t have .NET 4.5? There is a way to fix the behavior in .NET 4.0. You just need to add the following magic lines to the .config file of your project:
 

<configuration>
  <uri>
    <schemeSettings>
      <add name="http"
           genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
    </schemeSettings>
  </uri>
</configuration>

 
This trick works starting from .NET 4.0 beta 2. In other words it won’t work in .NET 3.5. You will have to look for the other options. For example, you can find the following magic “hack” on the Internet:
 

void ForceCanonicalPathAndQuery(Uri uri)
{
  string paq = uri.PathAndQuery; // need to access PathAndQuery
  FieldInfo flagsFieldInfo = typeof(Uri).GetField("m_Flags",
    BindingFlags.Instance | BindingFlags.NonPublic);
  ulong flags = (ulong) flagsFieldInfo.GetValue(uri);
  flags &= ~((ulong) 0x30); // Flags.PathNotCanonical|Flags.QueryNotCanonical
  flagsFieldInfo.SetValue(uri, flags);
}

 
And what will happen in Mono?
 
In Mono, they have the very same bug. The fix became available in Mono 3.10.0 in October 2014. If you have the latest version, everything should be ok for you. How the rest of developers would switch between the old and new behavior? System.Uri class has the IriParsing property for this purpose. Let’s review the code:
 

private static bool s_IriParsing;

internal static bool IriParsing {
    get { return s_IriParsing; }
    set { s_IriParsing = value; }
}

 
The property is set in the following way:
 

static Uri ()
{
#if NET_4_5
    IriParsing = true;
#endif

    var iriparsingVar =
        Environment.GetEnvironmentVariable ("MONO_URI_IRIPARSING");
    if (iriparsingVar == "true")
        IriParsing = true;
    else if (iriparsingVar == "false")
        IriParsing = false;
}

 
In other words, the easiest way to do it is the MONO_URI_IRIPARSING environment variable.
 
Summary
 
The bug is very unpleasant and might cost you many hours of peace of mind, if you accidentally face it. That is why I decided to write this article to make more people aware of this issue. Remember about the ambiguity of escaping of some URI and write stable code.
 
Related links:

 

February 5th, 2015

Leave a Comment