2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
4
using System ;
5
+ using System . Collections ;
5
6
using System . Collections . Generic ;
7
+ using System . Diagnostics . CodeAnalysis ;
6
8
using Microsoft . Extensions . Primitives ;
7
9
using Microsoft . Net . Http . Headers ;
8
10
@@ -11,14 +13,21 @@ namespace Microsoft.AspNetCore.Http.Features
11
13
/// <summary>
12
14
/// Default implementation for <see cref="IRequestCookiesFeature"/>.
13
15
/// </summary>
14
- public class RequestCookiesFeature : IRequestCookiesFeature
16
+ public class RequestCookiesFeature : IRequestCookiesFeature , IRequestCookieCollection
15
17
{
16
18
// Lambda hoisted to static readonly field to improve inlining https://github.com/dotnet/roslyn/issues/13624
17
19
private readonly static Func < IFeatureCollection , IHttpRequestFeature ? > _nullRequestFeature = f => null ;
18
20
19
21
private FeatureReferences < IHttpRequestFeature > _features ;
20
22
private StringValues _original ;
21
- private IRequestCookieCollection ? _parsedValues ;
23
+ private Dictionary < string , string > ? _parsedValues ;
24
+ private IRequestCookieCollection ? _userPassed ;
25
+
26
+ private static readonly string [ ] EmptyKeys = Array . Empty < string > ( ) ;
27
+ private static readonly Enumerator EmptyEnumerator = new Enumerator ( ) ;
28
+ // Pre-box
29
+ private static readonly IEnumerator < KeyValuePair < string , string > > EmptyIEnumeratorType = EmptyEnumerator ;
30
+ private static readonly IEnumerator EmptyIEnumerator = EmptyEnumerator ;
22
31
23
32
/// <summary>
24
33
/// Initializes a new instance of <see cref="RequestCookiesFeature"/>.
@@ -31,7 +40,7 @@ public RequestCookiesFeature(IRequestCookieCollection cookies)
31
40
throw new ArgumentNullException ( nameof ( cookies ) ) ;
32
41
}
33
42
34
- _parsedValues = cookies ;
43
+ _userPassed = cookies ;
35
44
}
36
45
37
46
/// <summary>
@@ -48,6 +57,8 @@ public RequestCookiesFeature(IFeatureCollection features)
48
57
_features . Initalize ( features ) ;
49
58
}
50
59
60
+ private Dictionary < string , string > ? Store { get ; set ; }
61
+
51
62
private IHttpRequestFeature HttpRequestFeature =>
52
63
_features . Fetch ( ref _features . Cache , _nullRequestFeature ) ! ;
53
64
@@ -60,9 +71,10 @@ public IRequestCookieCollection Cookies
60
71
{
61
72
if ( _parsedValues == null )
62
73
{
63
- _parsedValues = RequestCookieCollection . Empty ;
74
+ _parsedValues = new Dictionary < string , string > ( ) ;
64
75
}
65
- return _parsedValues ;
76
+
77
+ return this ;
66
78
}
67
79
68
80
var headers = HttpRequestFeature . Headers ;
@@ -75,25 +87,25 @@ public IRequestCookieCollection Cookies
75
87
if ( _parsedValues == null || _original != current )
76
88
{
77
89
_original = current ;
78
- _parsedValues = RequestCookieCollection . Parse ( current ) ;
90
+ _parsedValues = Parse ( current ) ;
79
91
}
80
92
81
- return _parsedValues ;
93
+ return this ;
82
94
}
83
95
set
84
96
{
85
- _parsedValues = value ;
97
+ _userPassed = value ;
86
98
_original = StringValues . Empty ;
87
99
if ( _features . Collection != null )
88
100
{
89
- if ( _parsedValues == null || _parsedValues . Count == 0 )
101
+ if ( _userPassed == null || _userPassed . Count == 0 )
90
102
{
91
103
HttpRequestFeature . Headers . Remove ( HeaderNames . Cookie ) ;
92
104
}
93
105
else
94
106
{
95
- var headers = new List < string > ( _parsedValues . Count ) ;
96
- foreach ( var pair in _parsedValues )
107
+ var headers = new List < string > ( _userPassed . Count ) ;
108
+ foreach ( var pair in _userPassed )
97
109
{
98
110
headers . Add ( new CookieHeaderValue ( pair . Key , pair . Value ) . ToString ( ) ) ;
99
111
}
@@ -103,5 +115,235 @@ public IRequestCookieCollection Cookies
103
115
}
104
116
}
105
117
}
118
+
119
+ /// <inheritdoc />
120
+ public string ? this [ string key ]
121
+ {
122
+ get
123
+ {
124
+ if ( key == null )
125
+ {
126
+ throw new ArgumentNullException ( nameof ( key ) ) ;
127
+ }
128
+
129
+ if ( _userPassed != null )
130
+ {
131
+ return _userPassed [ key ] ;
132
+ }
133
+
134
+ if ( Store == null )
135
+ {
136
+ return null ;
137
+ }
138
+
139
+ if ( TryGetValue ( key , out var value ) )
140
+ {
141
+ return value ;
142
+ }
143
+ return null ;
144
+ }
145
+ }
146
+
147
+ internal static Dictionary < string , string > ? Parse ( StringValues values )
148
+ => ParseInternal ( values , AppContext . TryGetSwitch ( ResponseCookies . EnableCookieNameEncoding , out var enabled ) && enabled ) ;
149
+
150
+ internal static Dictionary < string , string > ? ParseInternal ( StringValues values , bool enableCookieNameEncoding )
151
+ {
152
+ if ( values . Count == 0 )
153
+ {
154
+ return null ;
155
+ }
156
+
157
+ var store = new Dictionary < string , string > ( values . Count ) ;
158
+
159
+ if ( CookieHeaderValue . TryParseIntoDictionary ( values , store , enableCookieNameEncoding ) )
160
+ {
161
+ if ( store . Count == 0 )
162
+ {
163
+ return null ;
164
+ }
165
+
166
+ return store ;
167
+ }
168
+
169
+ return null ;
170
+ }
171
+
172
+ /// <inheritdoc />
173
+ public int Count
174
+ {
175
+ get
176
+ {
177
+ if ( _userPassed != null )
178
+ {
179
+ return _userPassed . Count ;
180
+ }
181
+
182
+ if ( Store == null )
183
+ {
184
+ return 0 ;
185
+ }
186
+ return Store . Count ;
187
+ }
188
+ }
189
+
190
+ /// <inheritdoc />
191
+ public ICollection < string > Keys
192
+ {
193
+ get
194
+ {
195
+ if ( _userPassed != null )
196
+ {
197
+ return _userPassed . Keys ;
198
+ }
199
+
200
+ if ( Store == null )
201
+ {
202
+ return EmptyKeys ;
203
+ }
204
+ return Store . Keys ;
205
+ }
206
+ }
207
+
208
+ /// <inheritdoc />
209
+ public bool ContainsKey ( string key )
210
+ {
211
+ if ( _userPassed != null )
212
+ {
213
+ return _userPassed . ContainsKey ( key ) ;
214
+ }
215
+
216
+ if ( Store == null )
217
+ {
218
+ return false ;
219
+ }
220
+ return Store . ContainsKey ( key ) ;
221
+ }
222
+
223
+ /// <inheritdoc />
224
+ public bool TryGetValue ( string key , [ MaybeNullWhen ( false ) ] out string ? value )
225
+ {
226
+ if ( _userPassed != null )
227
+ {
228
+ return _userPassed . TryGetValue ( key , out value ) ;
229
+ }
230
+
231
+ if ( Store == null )
232
+ {
233
+ value = null ;
234
+ return false ;
235
+ }
236
+ return Store . TryGetValue ( key , out value ) ;
237
+ }
238
+
239
+ /// <summary>
240
+ /// Returns an struct enumerator that iterates through a collection without boxing.
241
+ /// </summary>
242
+ /// <returns>An <see cref="Enumerator" /> object that can be used to iterate through the collection.</returns>
243
+ internal Enumerator GetEnumerator ( )
244
+ {
245
+ if ( Store == null || Store . Count == 0 )
246
+ {
247
+ // Non-boxed Enumerator
248
+ return EmptyEnumerator ;
249
+ }
250
+ // Non-boxed Enumerator
251
+ return new Enumerator ( Store . GetEnumerator ( ) ) ;
252
+ }
253
+
254
+ /// <summary>
255
+ /// Returns an enumerator that iterates through a collection, boxes in non-empty path.
256
+ /// </summary>
257
+ /// <returns>An <see cref="IEnumerator{T}" /> object that can be used to iterate through the collection.</returns>
258
+ IEnumerator < KeyValuePair < string , string > > IEnumerable < KeyValuePair < string , string > > . GetEnumerator ( )
259
+ {
260
+ if ( _userPassed != null )
261
+ {
262
+ return _userPassed . GetEnumerator ( ) ;
263
+ }
264
+
265
+ if ( Store == null || Store . Count == 0 )
266
+ {
267
+ // Non-boxed Enumerator
268
+ return EmptyIEnumeratorType ;
269
+ }
270
+ // Boxed Enumerator
271
+ return GetEnumerator ( ) ;
272
+ }
273
+
274
+ /// <summary>
275
+ /// Returns an enumerator that iterates through a collection, boxes in non-empty path.
276
+ /// </summary>
277
+ /// <returns>An <see cref="IEnumerator" /> object that can be used to iterate through the collection.</returns>
278
+ IEnumerator IEnumerable . GetEnumerator ( )
279
+ {
280
+ if ( _userPassed != null )
281
+ {
282
+ return _userPassed . GetEnumerator ( ) ;
283
+ }
284
+
285
+ if ( Store == null || Store . Count == 0 )
286
+ {
287
+ // Non-boxed Enumerator
288
+ return EmptyIEnumerator ;
289
+ }
290
+ // Boxed Enumerator
291
+ return GetEnumerator ( ) ;
292
+ }
293
+
294
+ internal struct Enumerator : IEnumerator < KeyValuePair < string , string > >
295
+ {
296
+ // Do NOT make this readonly, or MoveNext will not work
297
+ private Dictionary < string , string > . Enumerator _dictionaryEnumerator ;
298
+ private bool _notEmpty ;
299
+
300
+ internal Enumerator ( Dictionary < string , string > . Enumerator dictionaryEnumerator )
301
+ {
302
+ _dictionaryEnumerator = dictionaryEnumerator ;
303
+ _notEmpty = true ;
304
+ }
305
+
306
+ public bool MoveNext ( )
307
+ {
308
+ if ( _notEmpty )
309
+ {
310
+ return _dictionaryEnumerator . MoveNext ( ) ;
311
+ }
312
+ return false ;
313
+ }
314
+
315
+ public KeyValuePair < string , string > Current
316
+ {
317
+ get
318
+ {
319
+ if ( _notEmpty )
320
+ {
321
+ var current = _dictionaryEnumerator . Current ;
322
+ return new KeyValuePair < string , string > ( current . Key , current . Value ) ;
323
+ }
324
+ return default ( KeyValuePair < string , string > ) ;
325
+ }
326
+ }
327
+
328
+ object IEnumerator . Current
329
+ {
330
+ get
331
+ {
332
+ return Current ;
333
+ }
334
+ }
335
+
336
+ public void Dispose ( )
337
+ {
338
+ }
339
+
340
+ public void Reset ( )
341
+ {
342
+ if ( _notEmpty )
343
+ {
344
+ ( ( IEnumerator ) _dictionaryEnumerator ) . Reset ( ) ;
345
+ }
346
+ }
347
+ }
106
348
}
107
349
}
0 commit comments