From 48524edbe063f1b80578157c1f1775aa865ae5ad Mon Sep 17 00:00:00 2001 From: Bert Willems Date: Mon, 16 Apr 2012 11:59:49 +0200 Subject: [PATCH 1/2] #231 Support for LinkedIn Field-Selector Notation Fixes the broken .NET RFC 3986 encoding for Uri.EscapeDataString --- RestSharp/Authenticators/OAuth/OAuthTools.cs | 30 ++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/RestSharp/Authenticators/OAuth/OAuthTools.cs b/RestSharp/Authenticators/OAuth/OAuthTools.cs index 27e71ab61..d8c83dd36 100644 --- a/RestSharp/Authenticators/OAuth/OAuthTools.cs +++ b/RestSharp/Authenticators/OAuth/OAuthTools.cs @@ -83,16 +83,42 @@ public static string GetTimestamp(DateTime dateTime) return timestamp.ToString(); } + /// + /// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986. + /// + /// + private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" }; + /// /// URL encodes a string based on section 5.1 of the OAuth spec. /// Namely, percent encoding with [RFC3986], avoiding unreserved characters, /// upper-casing hexadecimal characters, and UTF-8 encoding for text value pairs. /// - /// + /// The value to escape. + /// The escaped value. + /// + /// The method is supposed to take on + /// RFC 3986 behavior if certain elements are present in a .config file. Even if this + /// actually worked (which in my experiments it doesn't), we can't rely on every + /// host actually having this configuration element present. + /// /// + /// public static string UrlEncodeRelaxed(string value) { - return Uri.EscapeDataString(value); + // Start with RFC 2396 escaping by calling the .NET method to do the work. + // This MAY sometimes exhibit RFC 3986 behavior (according to the documentation). + // If it does, the escaping we do that follows it will be a no-op since the + // characters we search for to replace can't possibly exist in the string. + StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value)); + + // Upgrade the escaping to RFC 3986, if necessary. + for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++) { + escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0])); + } + + // Return the fully-RFC3986-escaped string. + return escaped.ToString(); } /// From 150e5fe2fceed126f561292b1bfc7e6eff7921cc Mon Sep 17 00:00:00 2001 From: Bert Willems Date: Tue, 8 May 2012 09:00:46 +0200 Subject: [PATCH 2/2] + added test for #231 --- RestSharp.IntegrationTests/oAuth1Tests.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/RestSharp.IntegrationTests/oAuth1Tests.cs b/RestSharp.IntegrationTests/oAuth1Tests.cs index 9387514c5..d9002c680 100644 --- a/RestSharp.IntegrationTests/oAuth1Tests.cs +++ b/RestSharp.IntegrationTests/oAuth1Tests.cs @@ -171,5 +171,20 @@ public void Properly_Encodes_Parameter_Names() Assert.Equal("name%5Bfirst%5D", sortedParams[0].Name); } + + [Fact] + public void Use_RFC_3986_Encoding_For_Auth_Signature_Base() + { + // reserved characters for 2396 and 3986 + var reserved2396Characters = new[] { ";", "/", "?", ":", "@", "&", "=", "+", "$", "," }; // http://www.ietf.org/rfc/rfc2396.txt + var additionalReserved3986Characters = new[] { "!", "*", "'", "(", ")" }; // http://www.ietf.org/rfc/rfc3986.txt + var reservedCharacterString = string.Join( string.Empty, reserved2396Characters.Union( additionalReserved3986Characters ) ); + + // act + var escapedString = OAuthTools.UrlEncodeRelaxed( reservedCharacterString ); + + // assert + Assert.Equal( "%3B%2F%3F%3A%40%26%3D%2B%24%2C%21%2A%27%28%29", escapedString ); + } } }