Getting a Uri with escaped slashes on mono

2019-02-19 09:43发布

问题:

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.

回答1:

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