Partager via


System.Uri doesn’t allow embedded escaped slashes

I use C# a lot to write small utilities and sometimes find that it is annoying to have to dig into the source code to figure out why .Net framework doesn’t work as I expected. This happens again when I am using LinkedIn API (BTW, this is the first occurence of such experience).

LinkedIn API allows you to get the details of user’s connection using url like the following:

https://api.linkedin.com/v1/people/url=http%3A%2F%2Fwww.linkedin.com%2Fin%2Flbeebe/connections

However, if you create a url like the above in C# and send the request to LinkedIn, the server will reject the request and complain about the wrong signature.

I hit this kind of issue several times before (due to slightly different escaping rules between the requirement of oAuth 1.0 3.6 / 2 (RFC3986) and Uri.EscapeUriString in .Net 2.0 (RFC2396)). But this time, the root cause is different.

After spending lots of time debugging by myself and searching the web later, I reached the following post: https://connect.microsoft.com/VisualStudio/feedback/details/94109/

So it is clear that .Net framework explicitly alters the input url for ‘security’ reason. But it doesn’t expose the interface to allow the user to change this behavior (even if it is simply a parser flag called ‘GenericUriParserOptions.DontUnescapePathDotsAndSlashes’ which can be set internally).

Well, I’m on my own to hack it. The workaround posted in the bug changes the parser flag for all Uri objects with the same url scheme, so I have to write a more isolated version:

      public static class HackedUri
    {
        private const GenericUriParserOptions c_Options =
            GenericUriParserOptions.Default |
            GenericUriParserOptions.DontUnescapePathDotsAndSlashes |
            GenericUriParserOptions.Idn |
            GenericUriParserOptions.IriParsing;
        private static readonly GenericUriParser s_SyntaxHttp = new GenericUriParser(c_Options);
        private static readonly GenericUriParser s_SyntaxHttps = new GenericUriParser(c_Options);

        static HackedUri()
        {
            // Initialize the scheme
            FieldInfo fieldInfoSchemeName = typeof(UriParser).GetField("m_Scheme", BindingFlags.Instance | BindingFlags.NonPublic);
            if (fieldInfoSchemeName == null)
            {
                throw new MissingFieldException("'m_Scheme' field not found");
            }
            fieldInfoSchemeName.SetValue(s_SyntaxHttp, "http");
            fieldInfoSchemeName.SetValue(s_SyntaxHttps, "https");

            FieldInfo fieldInfoPort = typeof(UriParser).GetField("m_Port", BindingFlags.Instance | BindingFlags.NonPublic);
            if (fieldInfoPort == null)
            {
                throw new MissingFieldException("'m_Port' field not found");
            }
            fieldInfoPort.SetValue(s_SyntaxHttp, 80);
            fieldInfoPort.SetValue(s_SyntaxHttps, 443);
        }

        public static Uri Create(string url)
        {
            Uri result = new Uri(url);

            if (url.IndexOf("%2F", StringComparison.OrdinalIgnoreCase) != -1)
            {
                UriParser parser = null;
                switch (result.Scheme.ToLowerInvariant())
                {
                    case "http":
                        parser = s_SyntaxHttp;
                        break;
                    case "https":
                        parser = s_SyntaxHttps;
                        break;
                }

                if (parser != null)
                {
                    // Associate the parser
                    FieldInfo fieldInfo = typeof(Uri).GetField("m_Syntax", BindingFlags.Instance | BindingFlags.NonPublic);
                    if (fieldInfo == null)
                    {
                        throw new MissingFieldException("'m_Syntax' field not found");
                    }
                    fieldInfo.SetValue(result, parser);
                }
            }

            return result;
        }
    }

However, I feel guilty for hacking.

Comments

  • Anonymous
    January 16, 2012
    With .NET 4.0, you can change this setting in the config file: msdn.microsoft.com/.../bb882619.aspx msdn.microsoft.com/.../ee656539.aspx <configuration>  <uri>    <schemeSettings>      <clear/>      <add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes"/>    </schemeSettings>  </uri> </configuration>

  • Anonymous
    January 16, 2012
    The comment has been removed

  • Anonymous
    January 17, 2012
    You'll need to add the settings to the configuration file for your application - either YourAppName.exe.config or web.config - and it will affect all Uri parsing in the application.

  • Anonymous
    January 17, 2012
    The comment has been removed

  • Anonymous
    January 24, 2012
    This does not work on Windows Phone -> FieldAccessException

  • Anonymous
    January 25, 2012
    The comment has been removed

  • Anonymous
    March 23, 2014
    xiang, I am one such phone developer stuck without a solution in sight! how do phone developers accomplish this simple call after all?