3
3
4
4
using System . Net . Http . HPack ;
5
5
using System . Net . Http . QPack ;
6
- using System . Runtime . InteropServices ;
6
+ using System . Numerics ;
7
+ using System . Runtime . CompilerServices ;
7
8
using System . Text ;
8
9
9
10
namespace System . Net . Http . Headers
@@ -114,63 +115,29 @@ internal static class KnownHeaders
114
115
private static AltSvcHeaderParser ? GetAltSvcHeaderParser ( ) => AltSvcHeaderParser . Parser ;
115
116
#endif
116
117
117
- // Helper interface for making GetCandidate generic over strings, utf8, etc
118
- private interface IHeaderNameAccessor
119
- {
120
- int Length { get ; }
121
- char this [ int index ] { get ; }
122
- }
123
-
124
- private readonly struct StringAccessor : IHeaderNameAccessor
125
- {
126
- private readonly string _string ;
127
-
128
- public StringAccessor ( string s )
129
- {
130
- _string = s ;
131
- }
132
-
133
- public int Length => _string . Length ;
134
- public char this [ int index ] => _string [ index ] ;
135
- }
136
-
137
- // Can't use Span here as it's unsupported.
138
- private readonly unsafe struct BytePtrAccessor : IHeaderNameAccessor
139
- {
140
- private readonly byte * _p ;
141
- private readonly int _length ;
142
-
143
- public BytePtrAccessor ( byte * p , int length )
144
- {
145
- _p = p ;
146
- _length = length ;
147
- }
148
-
149
- public int Length => _length ;
150
- public char this [ int index ] => ( char ) _p [ index ] ;
151
- }
152
-
153
118
/// <summary>
154
119
/// Find possible known header match via lookup on length and a distinguishing char for that length.
155
120
/// </summary>
156
121
/// <remarks>
157
122
/// Matching is case-insensitive. Because of this, we do not preserve the case of the original header,
158
123
/// whether from the wire or from the user explicitly setting a known header using a header name string.
159
124
/// </remarks>
160
- private static KnownHeader ? GetCandidate < T > ( T key )
161
- where T : struct , IHeaderNameAccessor // Enforce struct for performance
125
+ private static KnownHeader ? GetCandidate < T > ( ReadOnlySpan < T > key )
126
+ where T : struct , INumberBase < T >
162
127
{
163
128
// Lookup is performed by first switching on the header name's length, and then switching
164
129
// on the most unique position in that length's string.
165
130
166
- int length = key . Length ;
167
- switch ( length )
131
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
132
+ static int GetLower ( T value ) => int . CreateTruncating ( value ) | 0x20 ;
133
+
134
+ switch ( key . Length )
168
135
{
169
136
case 2 :
170
137
return TE ; // TE
171
138
172
139
case 3 :
173
- switch ( key [ 0 ] | 0x20 )
140
+ switch ( GetLower ( key [ 0 ] ) )
174
141
{
175
142
case 'a' : return Age ; // [A]ge
176
143
case 'p' : return P3P ; // [P]3P
@@ -180,7 +147,7 @@ public BytePtrAccessor(byte* p, int length)
180
147
break ;
181
148
182
149
case 4 :
183
- switch ( key [ 0 ] | 0x20 )
150
+ switch ( GetLower ( key [ 0 ] ) )
184
151
{
185
152
case 'd' : return Date ; // [D]ate
186
153
case 'e' : return ETag ; // [E]Tag
@@ -192,15 +159,15 @@ public BytePtrAccessor(byte* p, int length)
192
159
break ;
193
160
194
161
case 5 :
195
- switch ( key [ 0 ] | 0x20 )
162
+ switch ( GetLower ( key [ 0 ] ) )
196
163
{
197
164
case 'a' : return Allow ; // [A]llow
198
165
case 'r' : return Range ; // [R]ange
199
166
}
200
167
break ;
201
168
202
169
case 6 :
203
- switch ( key [ 0 ] | 0x20 )
170
+ switch ( GetLower ( key [ 0 ] ) )
204
171
{
205
172
case 'a' : return Accept ; // [A]ccept
206
173
case 'c' : return Cookie ; // [C]ookie
@@ -212,14 +179,14 @@ public BytePtrAccessor(byte* p, int length)
212
179
break ;
213
180
214
181
case 7 :
215
- switch ( key [ 0 ] | 0x20 )
182
+ switch ( GetLower ( key [ 0 ] ) )
216
183
{
217
184
case ':' : return PseudoStatus ; // [:]status
218
185
case 'a' : return AltSvc ; // [A]lt-Svc
219
186
case 'c' : return Cookie2 ; // [C]ookie2
220
187
case 'e' : return Expires ; // [E]xpires
221
188
case 'r' :
222
- switch ( key [ 3 ] | 0x20 )
189
+ switch ( GetLower ( key [ 3 ] ) )
223
190
{
224
191
case 'e' : return Referer ; // [R]ef[e]rer
225
192
case 'r' : return Refresh ; // [R]ef[r]esh
@@ -233,7 +200,7 @@ public BytePtrAccessor(byte* p, int length)
233
200
break ;
234
201
235
202
case 8 :
236
- switch ( key [ 3 ] | 0x20 )
203
+ switch ( GetLower ( key [ 3 ] ) )
237
204
{
238
205
case '-' : return AltUsed ; // Alt[-]Used
239
206
case 'a' : return Location ; // Loc[a]tion
@@ -246,7 +213,7 @@ public BytePtrAccessor(byte* p, int length)
246
213
return ExpectCT ; // Expect-CT
247
214
248
215
case 10 :
249
- switch ( key [ 0 ] | 0x20 )
216
+ switch ( GetLower ( key [ 0 ] ) )
250
217
{
251
218
case 'c' : return Connection ; // [C]onnection
252
219
case 'k' : return KeepAlive ; // [K]eep-Alive
@@ -256,7 +223,7 @@ public BytePtrAccessor(byte* p, int length)
256
223
break ;
257
224
258
225
case 11 :
259
- switch ( key [ 0 ] | 0x20 )
226
+ switch ( GetLower ( key [ 0 ] ) )
260
227
{
261
228
case 'c' : return ContentMD5 ; // [C]ontent-MD5
262
229
case 'g' : return GrpcStatus ; // [g]rpc-status
@@ -266,7 +233,7 @@ public BytePtrAccessor(byte* p, int length)
266
233
break ;
267
234
268
235
case 12 :
269
- switch ( key [ 5 ] | 0x20 )
236
+ switch ( GetLower ( key [ 5 ] ) )
270
237
{
271
238
case 'd' : return XMSEdgeRef ; // X-MSE[d]ge-Ref
272
239
case 'e' : return XPoweredBy ; // X-Pow[e]red-By
@@ -279,12 +246,12 @@ public BytePtrAccessor(byte* p, int length)
279
246
break ;
280
247
281
248
case 13 :
282
- switch ( key [ 12 ] | 0x20 )
249
+ switch ( GetLower ( key [ 12 ] ) )
283
250
{
284
251
case 'd' : return LastModified ; // Last-Modifie[d]
285
252
case 'e' : return ContentRange ; // Content-Rang[e]
286
253
case 'g' :
287
- switch ( key [ 0 ] | 0x20 )
254
+ switch ( GetLower ( key [ 0 ] ) )
288
255
{
289
256
case 's' : return ServerTiming ; // [S]erver-Timin[g]
290
257
case 'g' : return GrpcEncoding ; // [g]rpc-encodin[g]
@@ -299,15 +266,15 @@ public BytePtrAccessor(byte* p, int length)
299
266
break ;
300
267
301
268
case 14 :
302
- switch ( key [ 0 ] | 0x20 )
269
+ switch ( GetLower ( key [ 0 ] ) )
303
270
{
304
271
case 'a' : return AcceptCharset ; // [A]ccept-Charset
305
272
case 'c' : return ContentLength ; // [C]ontent-Length
306
273
}
307
274
break ;
308
275
309
276
case 15 :
310
- switch ( key [ 7 ] | 0x20 )
277
+ switch ( GetLower ( key [ 7 ] ) )
311
278
{
312
279
case '-' : return XFrameOptions ; // X-Frame[-]Options
313
280
case 'e' : return AcceptEncoding ; // Accept-[E]ncoding
@@ -319,11 +286,11 @@ public BytePtrAccessor(byte* p, int length)
319
286
break ;
320
287
321
288
case 16 :
322
- switch ( key [ 11 ] | 0x20 )
289
+ switch ( GetLower ( key [ 11 ] ) )
323
290
{
324
291
case 'a' : return ContentLocation ; // Content-Loc[a]tion
325
292
case 'c' :
326
- switch ( key [ 0 ] | 0x20 )
293
+ switch ( GetLower ( key [ 0 ] ) )
327
294
{
328
295
case 'p' : return ProxyConnection ; // [P]roxy-Conne[c]tion
329
296
case 'x' : return XXssProtection ; // [X]-XSS-Prote[c]tion
@@ -337,7 +304,7 @@ public BytePtrAccessor(byte* p, int length)
337
304
break ;
338
305
339
306
case 17 :
340
- switch ( key [ 0 ] | 0x20 )
307
+ switch ( GetLower ( key [ 0 ] ) )
341
308
{
342
309
case 'i' : return IfModifiedSince ; // [I]f-Modified-Since
343
310
case 's' : return SecWebSocketKey ; // [S]ec-WebSocket-Key
@@ -346,15 +313,15 @@ public BytePtrAccessor(byte* p, int length)
346
313
break ;
347
314
348
315
case 18 :
349
- switch ( key [ 0 ] | 0x20 )
316
+ switch ( GetLower ( key [ 0 ] ) )
350
317
{
351
318
case 'p' : return ProxyAuthenticate ; // [P]roxy-Authenticate
352
319
case 'x' : return XContentDuration ; // [X]-Content-Duration
353
320
}
354
321
break ;
355
322
356
323
case 19 :
357
- switch ( key [ 0 ] | 0x20 )
324
+ switch ( GetLower ( key [ 0 ] ) )
358
325
{
359
326
case 'c' : return ContentDisposition ; // [C]ontent-Disposition
360
327
case 'i' : return IfUnmodifiedSince ; // [I]f-Unmodified-Since
@@ -369,7 +336,7 @@ public BytePtrAccessor(byte* p, int length)
369
336
return SecWebSocketVersion ; // Sec-WebSocket-Version
370
337
371
338
case 22 :
372
- switch ( key [ 0 ] | 0x20 )
339
+ switch ( GetLower ( key [ 0 ] ) )
373
340
{
374
341
case 'a' : return AccessControlMaxAge ; // [A]ccess-Control-Max-Age
375
342
case 's' : return SecWebSocketProtocol ; // [S]ec-WebSocket-Protocol
@@ -384,7 +351,7 @@ public BytePtrAccessor(byte* p, int length)
384
351
return SecWebSocketExtensions ; // Sec-WebSocket-Extensions
385
352
386
353
case 25 :
387
- switch ( key [ 0 ] | 0x20 )
354
+ switch ( GetLower ( key [ 0 ] ) )
388
355
{
389
356
case 's' : return StrictTransportSecurity ; // [S]trict-Transport-Security
390
357
case 'u' : return UpgradeInsecureRequests ; // [U]pgrade-Insecure-Requests
@@ -395,7 +362,7 @@ public BytePtrAccessor(byte* p, int length)
395
362
return AccessControlAllowOrigin ; // Access-Control-Allow-Origin
396
363
397
364
case 28 :
398
- switch ( key [ 21 ] | 0x20 )
365
+ switch ( GetLower ( key [ 21 ] ) )
399
366
{
400
367
case 'h' : return AccessControlAllowHeaders ; // Access-Control-Allow-[H]eaders
401
368
case 'm' : return AccessControlAllowMethods ; // Access-Control-Allow-[M]ethods
@@ -412,9 +379,9 @@ public BytePtrAccessor(byte* p, int length)
412
379
return null ;
413
380
}
414
381
415
- internal static KnownHeader ? TryGetKnownHeader ( string name )
382
+ public static KnownHeader ? TryGetKnownHeader ( string name )
416
383
{
417
- KnownHeader ? candidate = GetCandidate ( new StringAccessor ( name ) ) ;
384
+ KnownHeader ? candidate = GetCandidate < char > ( name ) ;
418
385
if ( candidate != null && StringComparer . OrdinalIgnoreCase . Equals ( name , candidate . Name ) )
419
386
{
420
387
return candidate ;
@@ -423,15 +390,12 @@ public BytePtrAccessor(byte* p, int length)
423
390
return null ;
424
391
}
425
392
426
- internal static unsafe KnownHeader ? TryGetKnownHeader ( ReadOnlySpan < byte > name )
393
+ public static KnownHeader ? TryGetKnownHeader ( ReadOnlySpan < byte > name )
427
394
{
428
- fixed ( byte * p = & MemoryMarshal . GetReference ( name ) )
395
+ KnownHeader ? candidate = GetCandidate ( name ) ;
396
+ if ( candidate != null && Ascii . EqualsIgnoreCase ( name , candidate . Name ) )
429
397
{
430
- KnownHeader ? candidate = GetCandidate ( new BytePtrAccessor ( p , name . Length ) ) ;
431
- if ( candidate != null && Ascii . EqualsIgnoreCase ( name , candidate . Name ) )
432
- {
433
- return candidate ;
434
- }
398
+ return candidate ;
435
399
}
436
400
437
401
return null ;
0 commit comments