diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs index 0ec137186e..8474b2b0f6 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Internal/Infrastructure/HttpUtilities.cs @@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure SetKnownMethod(_mask7Chars, _httpDeleteMethodLong, HttpMethod.Delete, 6); SetKnownMethod(_mask8Chars, _httpConnectMethodLong, HttpMethod.Connect, 7); SetKnownMethod(_mask8Chars, _httpOptionsMethodLong, HttpMethod.Options, 7); - FillEmptyKnownMethods(); + FillKnownMethodsGaps(); _methodNames[(byte)HttpMethod.Get] = HttpMethods.Get; _methodNames[(byte)HttpMethod.Put] = HttpMethods.Put; _methodNames[(byte)HttpMethod.Delete] = HttpMethods.Delete; @@ -64,9 +64,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int GetKnownMethodIndex(ulong value) { - var tmp = (int)value & 0x100604; + const int MagicNumer = 0x0600000C; + var tmp = (int)value & MagicNumer; - return ((tmp >> 2) | (tmp >> 8) | (tmp >> 17)) & 0x0F; + return ((tmp >> 2) | (tmp >> 23)) & 0x0F; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -75,15 +76,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure _knownMethods[GetKnownMethodIndex(knownMethodUlong)] = new Tuple(mask, knownMethodUlong, knownMethod, length, true); } - private static void FillEmptyKnownMethods() + + private static void FillKnownMethodsGaps() { var knownMethods = _knownMethods; var length = knownMethods.Length; + var invalidHttpMethod = new Tuple(_mask8Chars, 0ul, HttpMethod.Custom, 0, false); for (int i = 0; i < length; i++) { if (knownMethods[i] == null) { - knownMethods[i] = new Tuple(_mask8Chars, 0ul, HttpMethod.Custom, 0, false); + knownMethods[i] = invalidHttpMethod; } } } diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/KnownStrings.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/KnownStrings.cs index 95bb6a87ef..99b20dc8c5 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/KnownStrings.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/KnownStrings.cs @@ -11,15 +11,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance { public class KnownStrings { - static byte[] _methodGet = Encoding.UTF8.GetBytes("GET "); - static byte[] _methodConnect = Encoding.UTF8.GetBytes("CONNECT "); - static byte[] _methodDelete = Encoding.UTF8.GetBytes("DELETE "); - static byte[] _methodHead = Encoding.UTF8.GetBytes("HEAD "); - static byte[] _methodPatch = Encoding.UTF8.GetBytes("PATCH "); - static byte[] _methodPost = Encoding.UTF8.GetBytes("POST "); - static byte[] _methodPut = Encoding.UTF8.GetBytes("PUT "); - static byte[] _methodOptions = Encoding.UTF8.GetBytes("OPTIONS "); - static byte[] _methodTrace = Encoding.UTF8.GetBytes("TRACE "); + static byte[] _methodConnect = Encoding.ASCII.GetBytes("CONNECT "); + static byte[] _methodDelete = Encoding.ASCII.GetBytes("DELETE \0"); + static byte[] _methodGet = Encoding.ASCII.GetBytes("GET "); + static byte[] _methodHead = Encoding.ASCII.GetBytes("HEAD \0\0\0"); + static byte[] _methodPatch = Encoding.ASCII.GetBytes("PATCH \0\0"); + static byte[] _methodPost = Encoding.ASCII.GetBytes("POST \0\0\0"); + static byte[] _methodPut = Encoding.ASCII.GetBytes("PUT \0\0\0\0"); + static byte[] _methodOptions = Encoding.ASCII.GetBytes("OPTIONS "); + static byte[] _methodTrace = Encoding.ASCII.GetBytes("TRACE \0\0"); diff --git a/test/Microsoft.AspNetCore.Server.KestrelTests/KnownStringsTests.cs b/test/Microsoft.AspNetCore.Server.KestrelTests/KnownStringsTests.cs new file mode 100644 index 0000000000..14dba4859c --- /dev/null +++ b/test/Microsoft.AspNetCore.Server.KestrelTests/KnownStringsTests.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; +using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; +using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure; +using Xunit; + +namespace Microsoft.AspNetCore.Server.KestrelTests +{ + public class KnownStringsTests + { + static byte[] _methodConnect = Encoding.ASCII.GetBytes("CONNECT "); + static byte[] _methodDelete = Encoding.ASCII.GetBytes("DELETE \0"); + static byte[] _methodGet = Encoding.ASCII.GetBytes("GET "); + static byte[] _methodHead = Encoding.ASCII.GetBytes("HEAD \0\0\0"); + static byte[] _methodPatch = Encoding.ASCII.GetBytes("PATCH \0\0"); + static byte[] _methodPost = Encoding.ASCII.GetBytes("POST \0\0\0"); + static byte[] _methodPut = Encoding.ASCII.GetBytes("PUT \0\0\0\0"); + static byte[] _methodOptions = Encoding.ASCII.GetBytes("OPTIONS "); + static byte[] _methodTrace = Encoding.ASCII.GetBytes("TRACE \0\0"); + + + const int MagicNumer = 0x0600000C; + static byte[] _invalidMethod1 = BitConverter.GetBytes((ulong)MagicNumer); + static byte[] _invalidMethod2 = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + static byte[] _invalidMethod3 = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + static byte[] _invalidMethod4 = Encoding.ASCII.GetBytes("CONNECT "); + static byte[] _invalidMethod5 = Encoding.ASCII.GetBytes("DELETE \0"); + static byte[] _invalidMethod6 = Encoding.ASCII.GetBytes("GET "); + static byte[] _invalidMethod7 = Encoding.ASCII.GetBytes("HEAD \0\0\0"); + static byte[] _invalidMethod8 = Encoding.ASCII.GetBytes("PATCH \0\0"); + static byte[] _invalidMethod9 = Encoding.ASCII.GetBytes("POST \0\0\0"); + static byte[] _invalidMethod10 = Encoding.ASCII.GetBytes("PUT \0\0\0\0"); + static byte[] _invalidMethod11 = Encoding.ASCII.GetBytes("OPTIONS "); + static byte[] _invalidMethod12 = Encoding.ASCII.GetBytes("TRACE \0\0"); + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static object[] CreateTestDataEntry(byte[] methodData, HttpMethod expectedMethod, int expectedLength, bool expectedResult) + { + return new object[] { methodData, expectedMethod, expectedLength, expectedResult }; + } + + private static readonly List _testData + = new List + { + CreateTestDataEntry(_methodGet, HttpMethod.Get, 3,true), + CreateTestDataEntry(_methodPut, HttpMethod.Put, 3,true), + CreateTestDataEntry(_methodPost, HttpMethod.Post, 4,true), + CreateTestDataEntry(_methodHead, HttpMethod.Head, 4,true), + CreateTestDataEntry(_methodTrace, HttpMethod.Trace, 5,true), + CreateTestDataEntry(_methodPatch, HttpMethod.Patch, 5,true), + CreateTestDataEntry(_methodDelete, HttpMethod.Delete, 6,true), + CreateTestDataEntry(_methodConnect, HttpMethod.Connect, 7,true), + CreateTestDataEntry(_methodOptions, HttpMethod.Options, 7,true), + CreateTestDataEntry(_invalidMethod1, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod2, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod3, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod4, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod5, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod6, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod7, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod8, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod9, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod10, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod11, HttpMethod.Custom, 0,false), + CreateTestDataEntry(_invalidMethod12, HttpMethod.Custom, 0,false), + }; + + public static IEnumerable TestData => _testData; + + + [Theory] + [MemberData(nameof(TestData), MemberType = typeof(KnownStringsTests))] + public void GetsKnownMethod(byte[] methodData, HttpMethod expectedMethod, int expectedLength, bool expectedResult) + { + var data = new Span(methodData); + + HttpMethod method; + int length; + + bool result = data.GetKnownMethod(out method, out length); + + Assert.Equal(expectedResult, result); + Assert.Equal(expectedMethod, method); + Assert.Equal(expectedLength, length); + + } + } +}