File: Http3\QPackDecoderBenchmark.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.Net.Http;
using System.Net.Http.QPack;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3;
 
namespace Microsoft.AspNetCore.Server.Kestrel.Microbenchmarks;
 
public class QPackDecoderBenchmark
{
    private static readonly byte[] _headerFieldLine_LargeLiteralValue;
    private static readonly byte[] _headerFieldLine_LargeLiteralValue_Multiple;
    private static readonly byte[] _headerFieldLine_Static_Multiple;
 
    static QPackDecoderBenchmark()
    {
        var headers = new HttpResponseHeaders();
        var enumerator = new Http3HeadersEnumerator();
 
        AddLargeHeader(headers, 'a');
        enumerator.Initialize(headers);
        _headerFieldLine_LargeLiteralValue = GenerateHeaderBytes(enumerator);
        headers.Reset();
 
        AddLargeHeader(headers, 'a');
        AddLargeHeader(headers, 'b');
        AddLargeHeader(headers, 'c');
        AddLargeHeader(headers, 'd');
        AddLargeHeader(headers, 'e');
        enumerator.Initialize(headers);
        _headerFieldLine_LargeLiteralValue_Multiple = GenerateHeaderBytes(enumerator);
        headers.Reset();
 
        ((IHeaderDictionary)headers).ContentLength = 0;
        ((IHeaderDictionary)headers).ContentType = "application/json";
        ((IHeaderDictionary)headers).Age = "0";
        ((IHeaderDictionary)headers).AcceptRanges = "bytes";
        ((IHeaderDictionary)headers).AccessControlAllowOrigin = "*";
        enumerator.Initialize(headers);
        _headerFieldLine_Static_Multiple = GenerateHeaderBytes(enumerator);
 
        static void AddLargeHeader(HttpResponseHeaders headers, char c)
        {
            var s = new string(c, 8193);
            var success = headers.TryAdd(s, s);
            if (!success)
            {
                throw new InvalidOperationException();
            }
        }
 
        static byte[] GenerateHeaderBytes(Http3HeadersEnumerator enumerator)
        {
            var buffer = new byte[1024 * 1024];
            var totalHeaderSize = 0;
            var success = QPackHeaderWriter.BeginEncodeHeaders(enumerator, buffer, ref totalHeaderSize, out var length);
            if (!success)
            {
                throw new InvalidOperationException();
            }
 
            return buffer[..length];
        }
    }
 
    private QPackDecoder _decoder;
    private TestHeadersHandler _testHeadersHandler;
 
    [GlobalSetup]
    public void GlobalSetup()
    {
        _decoder = new QPackDecoder(maxHeadersLength: 65536);
        _testHeadersHandler = new TestHeadersHandler();
    }
 
    [Benchmark]
    public void DecodeHeaderFieldLine_LargeLiteralValue()
    {
        _decoder.Decode(_headerFieldLine_LargeLiteralValue, endHeaders: true, handler: _testHeadersHandler);
        _decoder.Reset();
    }
 
    [Benchmark]
    public void DecodeHeaderFieldLine_LargeLiteralValue_Multiple()
    {
        _decoder.Decode(_headerFieldLine_LargeLiteralValue_Multiple, endHeaders: true, handler: _testHeadersHandler);
        _decoder.Reset();
    }
 
    [Benchmark]
    public void DecodeHeaderFieldLine_Static_Multiple()
    {
        _decoder.Decode(_headerFieldLine_Static_Multiple, endHeaders: true, handler: _testHeadersHandler);
        _decoder.Reset();
    }
 
    private sealed class TestHeadersHandler : IHttpStreamHeadersHandler
    {
        public void OnDynamicIndexedHeader(int? index, ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
        {
        }
 
        public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
        {
        }
 
        public void OnHeadersComplete(bool endStream)
        {
        }
 
        public void OnStaticIndexedHeader(int index)
        {
        }
 
        public void OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
        {
        }
    }
}