@@ -83,16 +83,42 @@ public static string GetTimestamp(DateTime dateTime)
83
83
return timestamp . ToString ( ) ;
84
84
}
85
85
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
+
86
92
/// <summary>
87
93
/// URL encodes a string based on section 5.1 of the OAuth spec.
88
94
/// Namely, percent encoding with [RFC3986], avoiding unreserved characters,
89
95
/// upper-casing hexadecimal characters, and UTF-8 encoding for text value pairs.
90
96
/// </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>
92
105
/// <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" />
93
107
public static string UrlEncodeRelaxed ( string value )
94
108
{
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 ( ) ;
96
122
}
97
123
98
124
/// <summary>
0 commit comments