Skip to content

Commit 33392e5

Browse files
committed
Merge branch 'master' of https://github.com/trilobyte/RestSharp
2 parents 0f466b6 + 150e5fe commit 33392e5

File tree

2 files changed

+43
-2
lines changed

2 files changed

+43
-2
lines changed

RestSharp.IntegrationTests/oAuth1Tests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,5 +171,20 @@ public void Properly_Encodes_Parameter_Names()
171171

172172
Assert.Equal("name%5Bfirst%5D", sortedParams[0].Name);
173173
}
174+
175+
[Fact]
176+
public void Use_RFC_3986_Encoding_For_Auth_Signature_Base()
177+
{
178+
// reserved characters for 2396 and 3986
179+
var reserved2396Characters = new[] { ";", "/", "?", ":", "@", "&", "=", "+", "$", "," }; // http://www.ietf.org/rfc/rfc2396.txt
180+
var additionalReserved3986Characters = new[] { "!", "*", "'", "(", ")" }; // http://www.ietf.org/rfc/rfc3986.txt
181+
var reservedCharacterString = string.Join( string.Empty, reserved2396Characters.Union( additionalReserved3986Characters ) );
182+
183+
// act
184+
var escapedString = OAuthTools.UrlEncodeRelaxed( reservedCharacterString );
185+
186+
// assert
187+
Assert.Equal( "%3B%2F%3F%3A%40%26%3D%2B%24%2C%21%2A%27%28%29", escapedString );
188+
}
174189
}
175190
}

RestSharp/Authenticators/OAuth/OAuthTools.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,42 @@ public static string GetTimestamp(DateTime dateTime)
8383
return timestamp.ToString();
8484
}
8585

86+
/// <summary>
87+
/// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986.
88+
/// </summary>
89+
/// <seealso cref="http://stackoverflow.com/questions/846487/how-to-get-uri-escapedatastring-to-comply-with-rfc-3986" />
90+
private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
91+
8692
/// <summary>
8793
/// URL encodes a string based on section 5.1 of the OAuth spec.
8894
/// Namely, percent encoding with [RFC3986], avoiding unreserved characters,
8995
/// upper-casing hexadecimal characters, and UTF-8 encoding for text value pairs.
9096
/// </summary>
91-
/// <param name="value"></param>
97+
/// <param name="value">The value to escape.</param>
98+
/// <returns>The escaped value.</returns>
99+
/// <remarks>
100+
/// The <see cref="Uri.EscapeDataString"/> method is <i>supposed</i> to take on
101+
/// RFC 3986 behavior if certain elements are present in a .config file. Even if this
102+
/// actually worked (which in my experiments it <i>doesn't</i>), we can't rely on every
103+
/// host actually having this configuration element present.
104+
/// </remarks>
92105
/// <seealso cref="http://oauth.net/core/1.0#encoding_parameters" />
106+
/// <seealso cref="http://stackoverflow.com/questions/846487/how-to-get-uri-escapedatastring-to-comply-with-rfc-3986" />
93107
public static string UrlEncodeRelaxed(string value)
94108
{
95-
return Uri.EscapeDataString(value);
109+
// Start with RFC 2396 escaping by calling the .NET method to do the work.
110+
// This MAY sometimes exhibit RFC 3986 behavior (according to the documentation).
111+
// If it does, the escaping we do that follows it will be a no-op since the
112+
// characters we search for to replace can't possibly exist in the string.
113+
StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));
114+
115+
// Upgrade the escaping to RFC 3986, if necessary.
116+
for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++) {
117+
escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
118+
}
119+
120+
// Return the fully-RFC3986-escaped string.
121+
return escaped.ToString();
96122
}
97123

98124
/// <summary>

0 commit comments

Comments
 (0)