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 . Diagnostics ;
5
6
using System . Text ;
6
7
7
8
namespace Microsoft . AspNet . Server . Kestrel . Infrastructure
@@ -12,6 +13,62 @@ public static class MemoryPoolIterator2Extensions
12
13
13
14
private static Encoding _utf8 = Encoding . UTF8 ;
14
15
16
+ public const string HttpConnectMethod = "CONNECT" ;
17
+ public const string HttpDeleteMethod = "DELETE" ;
18
+ public const string HttpGetMethod = "GET" ;
19
+ public const string HttpHeadMethod = "HEAD" ;
20
+ public const string HttpPatchMethod = "PATCH" ;
21
+ public const string HttpPostMethod = "POST" ;
22
+ public const string HttpPutMethod = "PUT" ;
23
+ public const string HttpOptionsMethod = "OPTIONS" ;
24
+ public const string HttpTraceMethod = "TRACE" ;
25
+
26
+ public const string Http10Version = "HTTP/1.0" ;
27
+ public const string Http11Version = "HTTP/1.1" ;
28
+
29
+ private static long _httpConnectMethodLong = GetAsciiStringAsLong ( "CONNECT\0 " ) ;
30
+ private static long _httpDeleteMethodLong = GetAsciiStringAsLong ( "DELETE\0 \0 " ) ;
31
+ private static long _httpGetMethodLong = GetAsciiStringAsLong ( "GET\0 \0 \0 \0 \0 " ) ;
32
+ private static long _httpHeadMethodLong = GetAsciiStringAsLong ( "HEAD\0 \0 \0 \0 " ) ;
33
+ private static long _httpPatchMethodLong = GetAsciiStringAsLong ( "PATCH\0 \0 \0 " ) ;
34
+ private static long _httpPostMethodLong = GetAsciiStringAsLong ( "POST\0 \0 \0 \0 " ) ;
35
+ private static long _httpPutMethodLong = GetAsciiStringAsLong ( "PUT\0 \0 \0 \0 \0 " ) ;
36
+ private static long _httpOptionsMethodLong = GetAsciiStringAsLong ( "OPTIONS\0 " ) ;
37
+ private static long _httpTraceMethodLong = GetAsciiStringAsLong ( "TRACE\0 \0 \0 " ) ;
38
+
39
+ private static long _http10VersionLong = GetAsciiStringAsLong ( "HTTP/1.0" ) ;
40
+ private static long _http11VersionLong = GetAsciiStringAsLong ( "HTTP/1.1" ) ;
41
+
42
+ private const int PerfectHashDivisor = 37 ;
43
+ private static Tuple < long , string > [ ] _knownStrings = new Tuple < long , string > [ PerfectHashDivisor ] ;
44
+
45
+ static MemoryPoolIterator2Extensions ( )
46
+ {
47
+ _knownStrings [ _httpConnectMethodLong % PerfectHashDivisor ] = Tuple . Create ( _httpConnectMethodLong , HttpConnectMethod ) ;
48
+ _knownStrings [ _httpDeleteMethodLong % PerfectHashDivisor ] = Tuple . Create ( _httpDeleteMethodLong , HttpDeleteMethod ) ;
49
+ _knownStrings [ _httpGetMethodLong % PerfectHashDivisor ] = Tuple . Create ( _httpGetMethodLong , HttpGetMethod ) ;
50
+ _knownStrings [ _httpHeadMethodLong % PerfectHashDivisor ] = Tuple . Create ( _httpHeadMethodLong , HttpHeadMethod ) ;
51
+ _knownStrings [ _httpPatchMethodLong % PerfectHashDivisor ] = Tuple . Create ( _httpPatchMethodLong , HttpPatchMethod ) ;
52
+ _knownStrings [ _httpPostMethodLong % PerfectHashDivisor ] = Tuple . Create ( _httpPostMethodLong , HttpPostMethod ) ;
53
+ _knownStrings [ _httpPutMethodLong % PerfectHashDivisor ] = Tuple . Create ( _httpPutMethodLong , HttpPutMethod ) ;
54
+ _knownStrings [ _httpOptionsMethodLong % PerfectHashDivisor ] = Tuple . Create ( _httpOptionsMethodLong , HttpOptionsMethod ) ;
55
+ _knownStrings [ _httpTraceMethodLong % PerfectHashDivisor ] = Tuple . Create ( _httpTraceMethodLong , HttpTraceMethod ) ;
56
+ _knownStrings [ _http10VersionLong % PerfectHashDivisor ] = Tuple . Create ( _http10VersionLong , Http10Version ) ;
57
+ _knownStrings [ _http11VersionLong % PerfectHashDivisor ] = Tuple . Create ( _http11VersionLong , Http11Version ) ;
58
+ }
59
+
60
+ private unsafe static long GetAsciiStringAsLong ( string str )
61
+ {
62
+ Debug . Assert ( str . Length == 8 , "String must be exactly 8 (ASCII) characters long." ) ;
63
+
64
+ var bytes = Encoding . ASCII . GetBytes ( str ) ;
65
+
66
+ fixed ( byte * ptr = bytes )
67
+ {
68
+ return * ( long * ) ptr ;
69
+ }
70
+ }
71
+
15
72
private static unsafe string GetAsciiStringStack ( byte [ ] input , int inputOffset , int length )
16
73
{
17
74
// avoid declaring other local vars, or doing work with stackalloc
@@ -20,6 +77,7 @@ private static unsafe string GetAsciiStringStack(byte[] input, int inputOffset,
20
77
21
78
return GetAsciiStringImplementation ( output , input , inputOffset , length ) ;
22
79
}
80
+
23
81
private static unsafe string GetAsciiStringImplementation ( char * output , byte [ ] input , int inputOffset , int length )
24
82
{
25
83
for ( var i = 0 ; i < length ; i ++ )
@@ -203,5 +261,55 @@ public static ArraySegment<byte> GetArraySegment(this MemoryPoolIterator2 start,
203
261
start . CopyTo ( array , 0 , length , out length ) ;
204
262
return new ArraySegment < byte > ( array , 0 , length ) ;
205
263
}
264
+
265
+ /// <summary>
266
+ /// Checks that up to 8 bytes between <paramref name="begin"/> and <paramref name="end"/> correspond to a known HTTP string.
267
+ /// </summary>
268
+ /// <remarks>
269
+ /// A "known HTTP string" can be an HTTP method name defined in the HTTP/1.1 RFC or an HTTP version (HTTP/1.0 or HTTP/1.1).
270
+ /// Since all of those fit in at most 8 bytes, they can be optimally looked up by reading those bytes as a long. Once
271
+ /// in that format, uninteresting bits are cleared and the remaining long modulo 37 is looked up in a table.
272
+ /// The number 37 was chosen because that number allows for a perfect hash of the set of
273
+ /// "known strings" (CONNECT, DELETE, GET, HEAD, PATCH, POST, PUT, OPTIONS, TRACE, HTTP/1.0 and HTTP/1.1, where strings
274
+ /// with less than 8 characters have 0s appended to their ends to fill for the missing bytes).
275
+ /// </remarks>
276
+ /// <param name="begin">The iterator from which to start the known string lookup.</param>
277
+ /// <param name="end">The iterator pointing to the end of the input string.</param>
278
+ /// <param name="knownString">A reference to a pre-allocated known string, if the input matches any.</param>
279
+ /// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns>
280
+ public static bool GetKnownString ( this MemoryPoolIterator2 begin , MemoryPoolIterator2 end , out string knownString )
281
+ {
282
+ knownString = null ;
283
+
284
+ // This optimization only works on little endian environments (for now).
285
+ if ( ! BitConverter . IsLittleEndian )
286
+ {
287
+ return false ;
288
+ }
289
+
290
+ var inputLength = begin . GetLength ( end ) ;
291
+
292
+ if ( inputLength > sizeof ( long ) )
293
+ {
294
+ return false ;
295
+ }
296
+
297
+ var inputLong = begin . PeekLong ( ) ;
298
+
299
+ if ( inputLong == - 1 )
300
+ {
301
+ return false ;
302
+ }
303
+
304
+ inputLong &= ( long ) ( unchecked ( ( ulong ) ~ 0 ) >> ( ( sizeof ( long ) - inputLength ) * 8 ) ) ;
305
+
306
+ var value = _knownStrings [ inputLong % PerfectHashDivisor ] ;
307
+ if ( value != null && value . Item1 == inputLong )
308
+ {
309
+ knownString = value . Item2 ;
310
+ }
311
+
312
+ return knownString != null ;
313
+ }
206
314
}
207
315
}
0 commit comments