diff --git a/.gitignore b/.gitignore
index 6acc284439..af0898e29a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,3 +30,5 @@ runtimes/
.build/
.testPublish/
launchSettings.json
+BenchmarkDotNet.Artifacts/
+BDN.Generated/
diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln
index 1a5cda8a81..e6dca6eac9 100644
--- a/KestrelHttpServer.sln
+++ b/KestrelHttpServer.sln
@@ -45,6 +45,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{0EF2AC
test\shared\TestServiceContext.cs = test\shared\TestServiceContext.cs
EndProjectSection
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perf", "perf", "{21B17FBB-5A58-42A8-8473-43160509A9FF}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Server.Kestrel.Performance", "perf\Microsoft.AspNetCore.Server.Kestrel.Performance\Microsoft.AspNetCore.Server.Kestrel.Performance.xproj", "{70567566-524C-4B67-9B59-E5C206D6C2EB}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -79,6 +83,10 @@ Global
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9559A5F1-080C-4909-B6CF-7E4B3DC55748}.Release|Any CPU.Build.0 = Release|Any CPU
+ {70567566-524C-4B67-9B59-E5C206D6C2EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {70567566-524C-4B67-9B59-E5C206D6C2EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {70567566-524C-4B67-9B59-E5C206D6C2EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {70567566-524C-4B67-9B59-E5C206D6C2EB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -92,5 +100,6 @@ Global
{5F64B3C3-0C2E-431A-B820-A81BBFC863DA} = {2D5D5227-4DBD-499A-96B1-76A36B03B750}
{9559A5F1-080C-4909-B6CF-7E4B3DC55748} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
{0EF2ACDF-012F-4472-A13A-4272419E2903} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
+ {70567566-524C-4B67-9B59-E5C206D6C2EB} = {21B17FBB-5A58-42A8-8473-43160509A9FF}
EndGlobalSection
EndGlobal
diff --git a/global.json b/global.json
index 983ba0401e..c0415c19af 100644
--- a/global.json
+++ b/global.json
@@ -1,3 +1,3 @@
{
- "projects": ["src"]
+ "projects": ["src", "perf"]
}
diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.xproj b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.xproj
new file mode 100644
index 0000000000..3bbc5601e0
--- /dev/null
+++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.xproj
@@ -0,0 +1,22 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ 70567566-524c-4b67-9b59-e5c206d6c2eb
+ Microsoft.AspNetCore.Server.Kestrel.Performance
+ .\obj
+ .\bin\
+ v4.6.2
+
+
+ 2.0
+
+
+
+
+
+
\ No newline at end of file
diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs
new file mode 100644
index 0000000000..db29b584db
--- /dev/null
+++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Program.cs
@@ -0,0 +1,48 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using BenchmarkDotNet.Running;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Performance
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ var options = (uint[])Enum.GetValues(typeof(BenchmarkType));
+ BenchmarkType type;
+ if (args.Length != 1 || !Enum.TryParse(args[0], out type))
+ {
+ Console.WriteLine($"Please add benchmark to run as parameter:");
+ for (var i = 0; i < options.Length; i++)
+ {
+ Console.WriteLine($" {((BenchmarkType)options[i]).ToString()}");
+ }
+
+ return;
+ }
+
+ RunSelectedBenchmarks(type);
+ }
+
+ private static void RunSelectedBenchmarks(BenchmarkType type)
+ {
+ if (type.HasFlag(BenchmarkType.RequestParsing))
+ {
+ BenchmarkRunner.Run();
+ }
+ }
+ }
+
+ [Flags]
+ public enum BenchmarkType : uint
+ {
+ RequestParsing = 1,
+ // add new ones in powers of two - e.g. 2,4,8,16...
+
+ All = uint.MaxValue
+ }
+
+
+}
diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md
new file mode 100644
index 0000000000..4088c38007
--- /dev/null
+++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/Readme.md
@@ -0,0 +1,11 @@
+Compile the solution in Release mode (so Kestrel is available in release)
+
+To run a specific benchmark add it as parameter
+```
+dotnet run -c Release RequestParsing
+```
+To run all use `All` as parameter
+```
+dotnet run -c Release All
+```
+Using no parameter will list all available benchmarks
\ No newline at end of file
diff --git a/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs
new file mode 100644
index 0000000000..b1bdbe16f9
--- /dev/null
+++ b/perf/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsing.cs
@@ -0,0 +1,200 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Text;
+using BenchmarkDotNet.Attributes;
+using Microsoft.AspNetCore.Server.Kestrel.Internal;
+using Microsoft.AspNetCore.Server.Kestrel.Internal.Http;
+using Microsoft.AspNetCore.Server.Kestrel.Internal.Infrastructure;
+using RequestLineStatus = Microsoft.AspNetCore.Server.Kestrel.Internal.Http.Frame.RequestLineStatus;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Performance
+{
+ [Config(typeof(CoreConfig))]
+ public class RequestParsing
+ {
+ private const int InnerLoopCount = 512;
+ private const int Pipelining = 16;
+
+ private const string plaintextRequest = "GET /plaintext HTTP/1.1\r\nHost: www.example.com\r\n\r\n";
+
+ private const string liveaspnetRequest = "GET https://live.asp.net/ HTTP/1.1\r\n" +
+ "Host: live.asp.net\r\n" +
+ "Connection: keep-alive\r\n" +
+ "Upgrade-Insecure-Requests: 1\r\n" +
+ "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\r\n" +
+ "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" +
+ "DNT: 1\r\n" +
+ "Accept-Encoding: gzip, deflate, sdch, br\r\n" +
+ "Accept-Language: en-US,en;q=0.8\r\n" +
+ "Cookie: __unam=7a67379-1s65dc575c4-6d778abe-1; omniID=9519gfde_3347_4762_8762_df51458c8ec2\r\n\r\n";
+
+ private const string unicodeRequest =
+ "GET http://stackoverflow.com/questions/40148683/why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric HTTP/1.1\r\n" +
+ "Accept: text/html, application/xhtml+xml, image/jxr, */*\r\n" +
+ "Accept-Language: en-US,en-GB;q=0.7,en;q=0.3\r\n" +
+ "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.14965\r\n" +
+ "Accept-Encoding: gzip, deflate\r\n" +
+ "Host: stackoverflow.com\r\n" +
+ "Connection: Keep-Alive\r\n" +
+ "Cache-Control: max-age=0\r\n" +
+ "Upgrade-Insecure-Requests: 1\r\n" +
+ "DNT: 1\r\n" +
+ "Referer: http://stackoverflow.com/?tab=month\r\n" +
+ "Pragma: no-cache\r\n" +
+ "Cookie: prov=20629ccd-8b0f-e8ef-2935-cd26609fc0bc; __qca=P0-1591065732-1479167353442; _ga=GA1.2.1298898376.1479167354; _gat=1; sgt=id=9519gfde_3347_4762_8762_df51458c8ec2; acct=t=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric&s=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric\r\n\r\n";
+
+ private static readonly byte[] _plaintextPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(plaintextRequest, Pipelining)));
+ private static readonly byte[] _plaintextRequest = Encoding.ASCII.GetBytes(plaintextRequest);
+
+ private static readonly byte[] _liveaspnentPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(liveaspnetRequest, Pipelining)));
+ private static readonly byte[] _liveaspnentRequest = Encoding.ASCII.GetBytes(liveaspnetRequest);
+
+ private static readonly byte[] _unicodePipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(unicodeRequest, Pipelining)));
+ private static readonly byte[] _unicodeRequest = Encoding.ASCII.GetBytes(unicodeRequest);
+
+ private KestrelTrace Trace;
+ private LoggingThreadPool ThreadPool;
+ private MemoryPool MemoryPool;
+ private SocketInput SocketInput;
+ private Frame