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 );
+ }
}
}
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();
}
///