diff --git a/eng/Versions.props b/eng/Versions.props
index f11bb92706..567b25138b 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -220,7 +220,7 @@
3.1.3
0.9.9
- 0.10.13
+ 0.12.0
4.2.1
4.2.1
3.8.0
diff --git a/src/Servers/IIS/IIS/benchmarks/IIS.Performance/FirstRequestConfig.cs b/src/Servers/IIS/IIS/benchmarks/IIS.Performance/FirstRequestConfig.cs
index 727746871f..c048b28f8d 100644
--- a/src/Servers/IIS/IIS/benchmarks/IIS.Performance/FirstRequestConfig.cs
+++ b/src/Servers/IIS/IIS/benchmarks/IIS.Performance/FirstRequestConfig.cs
@@ -27,10 +27,10 @@ namespace BenchmarkDotNet.Attributes
Add(JitOptimizationsValidator.FailOnError);
- Add(Job.Core
+ Add(Job.Default
.With(CsProjCoreToolchain.From(NetCoreAppSettings.NetCoreApp21))
.With(new GcMode { Server = true })
- .WithTargetCount(10)
+ .WithIterationCount(10)
.WithInvocationCount(1)
.WithUnrollFactor(1)
.With(RunStrategy.ColdStart));
diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Http2ConnectionBenchmark.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Http2ConnectionBenchmark.cs
new file mode 100644
index 0000000000..1438fb5379
--- /dev/null
+++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Http2ConnectionBenchmark.cs
@@ -0,0 +1,101 @@
+// 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.Buffers;
+using System.Collections.Generic;
+using System.IO.Pipelines;
+using System.Net.Http.HPack;
+using System.Threading.Tasks;
+using BenchmarkDotNet.Attributes;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
+using Microsoft.AspNetCore.Testing;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Primitives;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Performance
+{
+ public class Http2ConnectionBenchmark
+ {
+ private MemoryPool _memoryPool;
+ private Pipe _pipe;
+ private HttpRequestHeaders _httpRequestHeaders;
+ private Http2Connection _connection;
+ private Http2HeadersEnumerator _requestHeadersEnumerator;
+ private int _currentStreamId;
+ private HPackEncoder _hpackEncoder;
+ private byte[] _headersBuffer;
+
+ [GlobalSetup]
+ public void GlobalSetup()
+ {
+ _memoryPool = SlabMemoryPoolFactory.Create();
+
+ var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false);
+ _pipe = new Pipe(options);
+
+ _httpRequestHeaders = new HttpRequestHeaders();
+ _httpRequestHeaders.Append(HeaderNames.Method, new StringValues("GET"));
+ _httpRequestHeaders.Append(HeaderNames.Path, new StringValues("/"));
+ _httpRequestHeaders.Append(HeaderNames.Scheme, new StringValues("http"));
+ _httpRequestHeaders.Append(HeaderNames.Authority, new StringValues("localhost:80"));
+
+ _hpackEncoder = new HPackEncoder();
+ _headersBuffer = new byte[1024 * 16];
+
+ var serviceContext = new ServiceContext
+ {
+ DateHeaderValueManager = new DateHeaderValueManager(),
+ ServerOptions = new KestrelServerOptions(),
+ Log = new KestrelTrace(NullLogger.Instance),
+ SystemClock = new MockSystemClock()
+ };
+ serviceContext.ServerOptions.Limits.Http2.MaxStreamsPerConnection = int.MaxValue;
+ serviceContext.DateHeaderValueManager.OnHeartbeat(default);
+
+ _connection = new Http2Connection(new HttpConnectionContext
+ {
+ MemoryPool = _memoryPool,
+ ConnectionId = "TestConnectionId",
+ Protocols = Core.HttpProtocols.Http2,
+ Transport = new MockDuplexPipe(_pipe.Reader, new NullPipeWriter()),
+ ServiceContext = serviceContext,
+ ConnectionFeatures = new FeatureCollection(),
+ TimeoutControl = new MockTimeoutControl(),
+ });
+
+ _requestHeadersEnumerator = new Http2HeadersEnumerator();
+
+ _currentStreamId = 1;
+
+ _ = _connection.ProcessRequestsAsync(new DummyApplication());
+
+ _pipe.Writer.Write(Http2Connection.ClientPreface);
+ PipeWriterHttp2FrameExtensions.WriteSettings(_pipe.Writer, new Http2PeerSettings());
+ _pipe.Writer.FlushAsync().GetAwaiter().GetResult();
+ }
+
+ [Benchmark]
+ public async Task EmptyRequest()
+ {
+ _requestHeadersEnumerator.Initialize(_httpRequestHeaders);
+ PipeWriterHttp2FrameExtensions.WriteStartStream(_pipe.Writer, streamId: _currentStreamId, _requestHeadersEnumerator, _hpackEncoder, _headersBuffer, endStream: true);
+ _currentStreamId += 2;
+ await _pipe.Writer.FlushAsync();
+ }
+
+ [GlobalCleanup]
+ public void Dispose()
+ {
+ _pipe.Writer.Complete();
+ _memoryPool?.Dispose();
+ }
+ }
+}
diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj b/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj
index 07c9d49f68..ef22ae3002 100644
--- a/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj
+++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Microsoft.AspNetCore.Server.Kestrel.Performance.csproj
@@ -1,4 +1,4 @@
-
+
$(DefaultNetCoreTargetFramework)
@@ -10,6 +10,8 @@
+
+
diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockDuplexPipe.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockDuplexPipe.cs
new file mode 100644
index 0000000000..86f6dc3112
--- /dev/null
+++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockDuplexPipe.cs
@@ -0,0 +1,19 @@
+// 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.IO.Pipelines;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Performance
+{
+ internal class MockDuplexPipe : IDuplexPipe
+ {
+ public MockDuplexPipe(PipeReader input, PipeWriter output)
+ {
+ Input = input;
+ Output = output;
+ }
+
+ public PipeReader Input { get; }
+ public PipeWriter Output { get; }
+ }
+}
diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockSystemClock.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockSystemClock.cs
new file mode 100644
index 0000000000..960d2cba95
--- /dev/null
+++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockSystemClock.cs
@@ -0,0 +1,15 @@
+// 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 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Performance
+{
+ internal class MockSystemClock : ISystemClock
+ {
+ public DateTimeOffset UtcNow { get; }
+ public long UtcNowTicks { get; }
+ public DateTimeOffset UtcNowUnsynchronized { get; }
+ }
+}
diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockTimeoutControl.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockTimeoutControl.cs
new file mode 100644
index 0000000000..54865db3c3
--- /dev/null
+++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockTimeoutControl.cs
@@ -0,0 +1,62 @@
+// 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 Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.FlowControl;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
+
+namespace Microsoft.AspNetCore.Server.Kestrel.Performance
+{
+ internal class MockTimeoutControl : ITimeoutControl
+ {
+ public TimeoutReason TimerReason { get; } = TimeoutReason.KeepAlive;
+
+ public void BytesRead(long count)
+ {
+ }
+
+ public void BytesWrittenToBuffer(MinDataRate minRate, long count)
+ {
+ }
+
+ public void CancelTimeout()
+ {
+ }
+
+ public void InitializeHttp2(InputFlowControl connectionInputFlowControl)
+ {
+ }
+
+ public void ResetTimeout(long ticks, TimeoutReason timeoutReason)
+ {
+ }
+
+ public void SetTimeout(long ticks, TimeoutReason timeoutReason)
+ {
+ }
+
+ public void StartRequestBody(MinDataRate minRate)
+ {
+ }
+
+ public void StartTimingRead()
+ {
+ }
+
+ public void StartTimingWrite()
+ {
+ }
+
+ public void StopRequestBody()
+ {
+ }
+
+ public void StopTimingRead()
+ {
+ }
+
+ public void StopTimingWrite()
+ {
+ }
+ }
+}
diff --git a/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/NullPipeWriter.cs b/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/NullPipeWriter.cs
index 24adcba48a..c982ec4902 100644
--- a/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/NullPipeWriter.cs
+++ b/src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/NullPipeWriter.cs
@@ -10,7 +10,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{
internal class NullPipeWriter : PipeWriter
{
- private byte[] _buffer = new byte[1024 * 128];
+ // Should be large enough for any content attempting to write to the buffer
+ private readonly byte[] _buffer = new byte[1024 * 128];
public override void Advance(int bytes)
{
diff --git a/src/Servers/Kestrel/shared/test/PipeWriterHttp2FrameExtensions.cs b/src/Servers/Kestrel/shared/test/PipeWriterHttp2FrameExtensions.cs
new file mode 100644
index 0000000000..beb76e1c55
--- /dev/null
+++ b/src/Servers/Kestrel/shared/test/PipeWriterHttp2FrameExtensions.cs
@@ -0,0 +1,78 @@
+// 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.Buffers;
+using System.Collections.Generic;
+using System.IO.Pipelines;
+using System.Net.Http.HPack;
+using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
+
+namespace Microsoft.AspNetCore.Testing
+{
+ internal static class PipeWriterHttp2FrameExtensions
+ {
+ public static void WriteSettings(this PipeWriter writer, Http2PeerSettings clientSettings)
+ {
+ var frame = new Http2Frame();
+ frame.PrepareSettings(Http2SettingsFrameFlags.NONE);
+ var settings = clientSettings.GetNonProtocolDefaults();
+ var payload = new byte[settings.Count * Http2FrameReader.SettingSize];
+ frame.PayloadLength = payload.Length;
+ Http2FrameWriter.WriteSettings(settings, payload);
+ Http2FrameWriter.WriteHeader(frame, writer);
+ writer.Write(payload);
+ }
+
+ public static void WriteStartStream(this PipeWriter writer, int streamId, Http2HeadersEnumerator headers, HPackEncoder hpackEncoder, byte[] headerEncodingBuffer, bool endStream)
+ {
+ var frame = new Http2Frame();
+ frame.PrepareHeaders(Http2HeadersFrameFlags.NONE, streamId);
+
+ var buffer = headerEncodingBuffer.AsSpan();
+ var done = hpackEncoder.BeginEncode(headers, buffer, out var length);
+ frame.PayloadLength = length;
+
+ if (done)
+ {
+ frame.HeadersFlags = Http2HeadersFrameFlags.END_HEADERS;
+ }
+
+ if (endStream)
+ {
+ frame.HeadersFlags |= Http2HeadersFrameFlags.END_STREAM;
+ }
+
+ Http2FrameWriter.WriteHeader(frame, writer);
+ writer.Write(buffer.Slice(0, length));
+
+ while (!done)
+ {
+ frame.PrepareContinuation(Http2ContinuationFrameFlags.NONE, streamId);
+
+ done = hpackEncoder.Encode(buffer, out length);
+ frame.PayloadLength = length;
+
+ if (done)
+ {
+ frame.ContinuationFlags = Http2ContinuationFrameFlags.END_HEADERS;
+ }
+
+ Http2FrameWriter.WriteHeader(frame, writer);
+ writer.Write(buffer.Slice(0, length));
+ }
+ }
+
+ public static void WriteData(this PipeWriter writer, int streamId, Memory data, bool endStream)
+ {
+ var frame = new Http2Frame();
+
+ frame.PrepareData(streamId);
+ frame.PayloadLength = data.Length;
+ frame.DataFlags = endStream ? Http2DataFrameFlags.END_STREAM : Http2DataFrameFlags.NONE;
+
+ Http2FrameWriter.WriteHeader(frame, writer);
+ writer.Write(data.Span);
+ }
+ }
+}
diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs
index de1a57beb2..68076af39c 100644
--- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs
+++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2TestBase.cs
@@ -501,56 +501,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
var tcs = new TaskCompletionSource