2

Update: A fix has now made it's way into mono. This is good news!

Updated: Added logic to fix fragment handling.

I am trying to send a request with an encoded slash on Mono using the Uri class. This is basically the Mono equivalent of this question: GETting a URL with an url-encoded slash

The issue is that Mono similar to .NET will unescape any slashes it finds in the Uri when it is constructed. This logic was originally put in in place in order to remove vulnerabilities that could occur if paths were escape encoded and not detected.

In the previous post there is a hack which shows setting flags on the underlying Uri class via reflection which force the escaped slashes to be left alone. This behavior has been fixed in .NET 4.5 and by default the escaped slashes are allowed (as I mentioned in the comments).

I tried to do the same on Mono, but it fails as the internals of the Uri class are different. I came up with this approach to achieve what I want, which works but it is TERRIBLY hacky.

class Program { static void Main(string[] args) { var uri = new Uri("http://www.yahoo.com/%2F?Foo=Bar%2F#frag"); UriHelper.ForceCanonicalPathAndQuery(uri); Console.WriteLine ("uri.ToString() - " + uri.ToString ()); Console.WriteLine ("uri.AbsoluteUri - " + uri.AbsoluteUri); Console.WriteLine ("uri.Host - " + uri.Host); Console.WriteLine ("uri.Query - " + uri.Query); Console.WriteLine ("uri.PathAndQuery - " + uri.PathAndQuery); Console.WriteLine ("uri.AbsolutePath - " + uri.AbsolutePath); Console.WriteLine ("uri.Fragment - " + uri.Fragment); }

public class UriHelper {
  private static Type uriType = typeof(Uri);
  private static FieldInfo sourceField;
  private static FieldInfo queryField;
  private static FieldInfo pathField;
  private static FieldInfo cachedToStringField;
  private static FieldInfo cachedAbsoluteUriField;

  static UriHelper ()
  {
    sourceField = uriType.GetField ("source", BindingFlags.NonPublic | BindingFlags.Instance);
    queryField = uriType.GetField ("query", BindingFlags.NonPublic | BindingFlags.Instance);
    pathField = uriType.GetField ("path", BindingFlags.NonPublic | BindingFlags.Instance);
    cachedToStringField = uriType.GetField ("cachedToString", BindingFlags.NonPublic | BindingFlags.Instance);
    cachedAbsoluteUriField = uriType.GetField ("cachedAbsoluteUri", BindingFlags.NonPublic | BindingFlags.Instance);
  }

  public static void ForceCanonicalPathAndQuery(Uri uri)
  {
    var source = (string) sourceField.GetValue (uri);
    cachedToStringField.SetValue (uri, source);
    cachedAbsoluteUriField.SetValue (uri, source);
    var fragPos = source.IndexOf ("#");
    var queryPos = source.IndexOf ("?");
    var start = source.IndexOf (uri.Host) + uri.Host.Length;
    var pathEnd = queryPos == -1 ? fragPos : queryPos;
    if (pathEnd == -1)
      pathEnd = source.Length+1;
    var path = queryPos > -1 ? source.Substring (start, pathEnd - start) : source.Substring (start);
    pathField.SetValue (uri, path);
    queryField.SetValue(uri, fragPos > -1 ? source.Substring(queryPos, fragPos - queryPos) : source.Substring(queryPos));
  }
}

}

When you run this, it outputs the following:

uri.ToString() - http://www.yahoo.com/%2F?Foo=Bar%2F#frag
uri.AbsoluteUri - http://www.yahoo.com/%2F?Foo=Bar%2F#frag
uri.Host - www.yahoo.com
uri.Query - ?Foo=Bar%2F
uri.PathAndQuery - /%2F?Foo=Bar%2F
uri.AbsolutePath - /%2F
uri.Fragment - #frag

I don't at all feel good about it, but it does work, at least for the basic scenario of taking a Uri and issuing a query.

I might be missing something in the Uri class, so if you have a better / less hacky way to do what I am doing here, I'd really appreciate it.

Community
  • 1
  • 1
Glenn Block
  • 8,463
  • 1
  • 32
  • 34

1 Answers1

0

From the original question, it looks like the behaviour of MS.NET changed in .NET 4.5 to fix the bug.

Indeed, then, it is a bug in mono for not following the behaviour change in the .NET 4.5 profile. And it seems someone already fixed the bug and proposed a pull request, the problem is that nobody in the Mono team seems to have found the time to review it: https://github.com/mono/mono/pull/619

knocte
  • 16,941
  • 11
  • 79
  • 125
  • I didn't file the original issue, I just found out that it was fixed in 4.5 and added a comment :-) I did file a bug with xamarin (https://bugzilla.xamarin.com/show_bug.cgi?id=16960) and have talked to Miguel. I think the issue is that it wouldn't be patched for earlier versions of mono as that would be inconsistent with .NET, but hopefully it can be fixed for Mono 3.x. – Glenn Block Dec 25 '13 at 17:54
  • I never proposed to fix it for all Mono profiles, you can embed the code of your bugfix under a NET_4_5 define on its class libraries in order to fix the bug only in the .NET 4.5 profile – knocte Dec 25 '13 at 18:47
  • Hi Knocte, agreed, thanks! The bug I filed was to fix it for the 4.5 profile. Do you have any idea on how to do what I am proposing above in a less hacky way? – Glenn Block Dec 25 '13 at 18:51
  • the less hacky way is to obviously fix the bug in mono instead of resorting to reflection – knocte Dec 25 '13 at 23:37
  • Ha ha, well it's not considered a bug pre 4.5 ;-) It was by design. I mean if there's another way barring a fix. – Glenn Block Dec 26 '13 at 02:48
  • why complicate your life? evolution of the versions of the .NET profile are not only to introduce fancy C# or CLR features, it can also mean fixing bugs, and depending on a new version of .NET because of a fixed bug is a perfectly reasonable motivation – knocte Dec 26 '13 at 07:13
  • @GlennBlock turns out someone already fixed the bug (updated my answer). So now you just have to pester people to do a review :-) – knocte Jan 02 '14 at 14:28
  • Hi @knocte. It looks like based on the comments the PR was reviewed and a bunch of feedback was given though not addressed. Regardless I forwarded it on and looks like Miguel agrees it should be fixed: https://bugzilla.xamarin.com/show_bug.cgi?id=16960 – Glenn Block Jan 03 '14 at 19:35
  • Oh right, I didn't notice the feedback as I read the thread very quickly. BTW I think you should mark your bug as duplicate of bug 9576 – knocte Jan 03 '14 at 19:42