File: Http1ReadingBenchmark.cs
Web Access
Project: src\src\Servers\Kestrel\perf\Microbenchmarks\Microsoft.AspNetCore.Server.Kestrel.Microbenchmarks.csproj (Microsoft.AspNetCore.Server.Kestrel.Microbenchmarks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Buffers;
using System.IO.Pipelines;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
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.InternalTesting;
 
namespace Microsoft.AspNetCore.Server.Kestrel.Microbenchmarks;
 
public class Http1ReadingBenchmark
{
    // Standard completed task
    private static readonly Func<object, Task> _syncTaskFunc = (obj) => Task.CompletedTask;
    // Non-standard completed task
    private static readonly Task _pseudoAsyncTask = Task.FromResult(27);
    private static readonly Func<object, Task> _pseudoAsyncTaskFunc = (obj) => _pseudoAsyncTask;
 
    private TestHttp1Connection _http1Connection;
    private DuplexPipe.DuplexPipePair _pair;
    private MemoryPool<byte> _memoryPool;
 
    private readonly byte[] _readData = Encoding.ASCII.GetBytes(new string('a', 100));
 
    [GlobalSetup]
    public void GlobalSetup()
    {
        _memoryPool = PinnedBlockMemoryPoolFactory.Create();
        _http1Connection = MakeHttp1Connection();
    }
 
    [Params(true, false)]
    public bool WithHeaders { get; set; }
 
    //[Params(true, false)]
    //public bool Chunked { get; set; }
 
    [Params(Startup.None, Startup.Sync, Startup.Async)]
    public Startup OnStarting { get; set; }
 
    [IterationSetup]
    public void Setup()
    {
        _http1Connection.Reset();
 
        _http1Connection.RequestHeaders.ContentLength = _readData.Length;
 
        if (!WithHeaders)
        {
            _http1Connection.FlushAsync().GetAwaiter().GetResult();
        }
 
        ResetState();
 
        _pair.Application.Output.WriteAsync(_readData).GetAwaiter().GetResult();
    }
 
    private void ResetState()
    {
        if (WithHeaders)
        {
            _http1Connection.ResetState();
 
            switch (OnStarting)
            {
                case Startup.Sync:
                    _http1Connection.OnStarting(_syncTaskFunc, null);
                    break;
                case Startup.Async:
                    _http1Connection.OnStarting(_pseudoAsyncTaskFunc, null);
                    break;
            }
        }
    }
 
    [Benchmark]
    public Task ReadAsync()
    {
        ResetState();
 
        return _http1Connection.RequestBody.ReadAsync(new byte[100], default(CancellationToken)).AsTask();
    }
 
    private TestHttp1Connection MakeHttp1Connection()
    {
        var options = new PipeOptions(_memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false);
        var pair = DuplexPipe.CreateConnectionPair(options, options);
        _pair = pair;
 
        var serviceContext = TestContextFactory.CreateServiceContext(
            serverOptions: new KestrelServerOptions(),
            httpParser: new HttpParser<Http1ParsingHandler>(),
            dateHeaderValueManager: new DateHeaderValueManager(TimeProvider.System));
 
        var connectionContext = TestContextFactory.CreateHttpConnectionContext(
            serviceContext: serviceContext,
            connectionContext: null,
            transport: pair.Transport,
            timeoutControl: new TimeoutControl(timeoutHandler: null, TimeProvider.System),
            memoryPool: _memoryPool,
            connectionFeatures: new FeatureCollection());
 
        var http1Connection = new TestHttp1Connection(connectionContext);
 
        http1Connection.Reset();
        http1Connection.InitializeBodyControl(new Http1ContentLengthMessageBody(http1Connection, contentLength: 100, keepAlive: true));
        serviceContext.DateHeaderValueManager.OnHeartbeat();
 
        return http1Connection;
    }
 
    [IterationCleanup]
    public void Cleanup()
    {
        var reader = _pair.Application.Input;
        if (reader.TryRead(out var readResult))
        {
            reader.AdvanceTo(readResult.Buffer.End);
        }
    }
 
    public enum Startup
    {
        None,
        Sync,
        Async
    }
 
    [GlobalCleanup]
    public void Dispose()
    {
        _memoryPool?.Dispose();
    }
}