2
2
// Licensed under the MIT License.
3
3
4
4
using System ;
5
+ using System . Collections . Generic ;
6
+ using System . Globalization ;
5
7
using System . Net ;
6
8
using System . Net . Http ;
7
9
using System . Threading ;
8
10
using System . Threading . Tasks ;
11
+ using System . Web ;
12
+ using Azure ;
9
13
using Xunit ;
10
14
11
15
namespace Microsoft . Identity . Web . Test . Common . Mocks
@@ -32,8 +36,9 @@ public MockHttpMessageHandler()
32
36
/// Once the http message is executed, this property holds the request message.
33
37
/// </summary>
34
38
public HttpRequestMessage ActualRequestMessage { get ; private set ; }
39
+ public Dictionary < string , string > ActualRequestPostData { get ; private set ; }
35
40
36
- protected override Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
41
+ protected override async Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
37
42
{
38
43
ActualRequestMessage = request ;
39
44
@@ -62,7 +67,7 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
62
67
Content = new StringContent ( TestConstants . DiscoveryJsonResponse ) ,
63
68
} ;
64
69
65
- return Task . FromResult ( responseMessage ) ;
70
+ return responseMessage ;
66
71
}
67
72
}
68
73
@@ -81,10 +86,109 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
81
86
82
87
if ( request . Method != HttpMethod . Get && request . Content != null )
83
88
{
84
- string postData = request . Content . ReadAsStringAsync ( ) . Result ;
89
+ string postData = await request . Content . ReadAsStringAsync ( ) ;
90
+ ActualRequestPostData = QueryStringParser . ParseKeyValueList ( postData , '&' , true , false ) ;
91
+
92
+ }
93
+
94
+ return ResponseMessage ;
95
+ }
96
+ }
97
+
98
+ public static class QueryStringParser
99
+ {
100
+ public static Dictionary < string , string > ParseKeyValueList ( string input , char delimiter , bool urlDecode ,
101
+ bool lowercaseKeys )
102
+ {
103
+ var response = new Dictionary < string , string > ( ) ;
104
+
105
+ var queryPairs = SplitWithQuotes ( input , delimiter ) ;
106
+
107
+ foreach ( string queryPair in queryPairs )
108
+ {
109
+ var pair = SplitWithQuotes ( queryPair , '=' ) ;
110
+
111
+ if ( pair . Count == 2 && ! string . IsNullOrWhiteSpace ( pair [ 0 ] ) && ! string . IsNullOrWhiteSpace ( pair [ 1 ] ) )
112
+ {
113
+ string key = pair [ 0 ] ;
114
+ string value = pair [ 1 ] ;
115
+
116
+ // Url decoding is needed for parsing OAuth response, but not for parsing WWW-Authenticate header in 401 challenge
117
+ if ( urlDecode )
118
+ {
119
+ key = UrlDecode ( key ) ;
120
+ value = UrlDecode ( value ) ;
121
+ }
122
+
123
+ if ( lowercaseKeys )
124
+ {
125
+ key = key . Trim ( ) . ToLowerInvariant ( ) ;
126
+ }
127
+
128
+ value = value . Trim ( ) . Trim ( '\" ' ) . Trim ( ) ;
129
+
130
+ response [ key ] = value ;
131
+ }
132
+ }
133
+
134
+ return response ;
135
+ }
136
+
137
+ public static Dictionary < string , string > ParseKeyValueList ( string input , char delimiter , bool urlDecode )
138
+ {
139
+ return ParseKeyValueList ( input , delimiter , urlDecode , true ) ;
140
+ }
141
+
142
+ private static string UrlDecode ( string message )
143
+ {
144
+ if ( string . IsNullOrEmpty ( message ) )
145
+ {
146
+ return message ;
147
+ }
148
+
149
+ message = message . Replace ( "+" , "%20" ) ;
150
+ message = Uri . UnescapeDataString ( message ) ;
151
+
152
+ return message ;
153
+ }
154
+
155
+ internal static IReadOnlyList < string > SplitWithQuotes ( string input , char delimiter )
156
+ {
157
+ if ( string . IsNullOrWhiteSpace ( input ) )
158
+ {
159
+ return Array . Empty < string > ( ) ;
160
+ }
161
+
162
+ var items = new List < string > ( ) ;
163
+
164
+ int startIndex = 0 ;
165
+ bool insideString = false ;
166
+ string item ;
167
+ for ( int i = 0 ; i < input . Length ; i ++ )
168
+ {
169
+ if ( input [ i ] == delimiter && ! insideString )
170
+ {
171
+ item = input . Substring ( startIndex , i - startIndex ) ;
172
+ if ( ! string . IsNullOrWhiteSpace ( item . Trim ( ) ) )
173
+ {
174
+ items . Add ( item ) ;
175
+ }
176
+
177
+ startIndex = i + 1 ;
178
+ }
179
+ else if ( input [ i ] == '"' )
180
+ {
181
+ insideString = ! insideString ;
182
+ }
183
+ }
184
+
185
+ item = input . Substring ( startIndex ) ;
186
+ if ( ! string . IsNullOrWhiteSpace ( item . Trim ( ) ) )
187
+ {
188
+ items . Add ( item ) ;
85
189
}
86
190
87
- return Task . FromResult ( ResponseMessage ) ;
191
+ return items ;
88
192
}
89
193
}
90
194
}
0 commit comments