File: HeaderCollectionBenchmark.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.Runtime.CompilerServices;
using System.Text;
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.Http;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Net.Http.Headers;
 
namespace Microsoft.AspNetCore.Server.Kestrel.Microbenchmarks;
 
public class HeaderCollectionBenchmark
{
    private const int InnerLoopCount = 1024 * 1024;
 
    private static readonly byte[] _bytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel");
    private static readonly DateHeaderValueManager _dateHeaderValueManager = new DateHeaderValueManager(TimeProvider.System);
    private HttpResponseHeaders _responseHeadersDirect;
    private HttpResponse _response;
 
    public enum BenchmarkTypes
    {
        ContentLengthNumeric,
        ContentLengthString,
        Plaintext,
        Common,
        Unknown
    }
 
    [Params(
        BenchmarkTypes.ContentLengthNumeric,
        BenchmarkTypes.ContentLengthString,
        BenchmarkTypes.Plaintext,
        BenchmarkTypes.Common,
        BenchmarkTypes.Unknown
    )]
    public BenchmarkTypes Type { get; set; }
 
    [Benchmark(OperationsPerInvoke = InnerLoopCount)]
    public void SetHeaders()
    {
        switch (Type)
        {
            case BenchmarkTypes.ContentLengthNumeric:
                SetContentLengthNumeric(InnerLoopCount);
                break;
            case BenchmarkTypes.ContentLengthString:
                SetContentLengthString(InnerLoopCount);
                break;
            case BenchmarkTypes.Plaintext:
                SetPlaintext(InnerLoopCount);
                break;
            case BenchmarkTypes.Common:
                SetCommon(InnerLoopCount);
                break;
            case BenchmarkTypes.Unknown:
                SetUnknown(InnerLoopCount);
                break;
        }
    }
 
    [Benchmark(OperationsPerInvoke = InnerLoopCount)]
    public void GetHeaders()
    {
        switch (Type)
        {
            case BenchmarkTypes.ContentLengthNumeric:
                GetContentLengthNumeric(InnerLoopCount);
                break;
            case BenchmarkTypes.ContentLengthString:
                GetContentLengthString(InnerLoopCount);
                break;
            case BenchmarkTypes.Plaintext:
                GetPlaintext(InnerLoopCount);
                break;
            case BenchmarkTypes.Common:
                GetCommon(InnerLoopCount);
                break;
            case BenchmarkTypes.Unknown:
                GetUnknown(InnerLoopCount);
                break;
        }
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private void SetContentLengthNumeric(int count)
    {
        for (var i = 0; i < count; i++)
        {
            _responseHeadersDirect.Reset();
 
            _response.ContentLength = 0;
        }
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private void SetContentLengthString(int count)
    {
        for (var i = 0; i < count; i++)
        {
            _responseHeadersDirect.Reset();
 
            _response.Headers[HeaderNames.ContentLength] = "0";
        }
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private void SetPlaintext(int count)
    {
        for (var i = 0; i < count; i++)
        {
            AddHeaders();
        }
 
        void AddHeaders()
        {
            _responseHeadersDirect.Reset();
 
            _response.StatusCode = 200;
            _response.ContentType = "text/plain";
            _response.ContentLength = 13;
 
            var dateHeaderValues = _dateHeaderValueManager.GetDateHeaderValues();
            _responseHeadersDirect.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
            _responseHeadersDirect.SetRawServer("Kestrel", _bytesServer);
        }
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private void SetCommon(int count)
    {
        for (var i = 0; i < count; i++)
        {
            AddHeaders();
        }
 
        void AddHeaders()
        {
            _responseHeadersDirect.Reset();
 
            _response.StatusCode = 200;
            _response.ContentType = "text/css";
            _response.ContentLength = 421;
 
            var headers = _response.Headers;
 
            headers.Connection = "Close";
            headers.CacheControl = "public, max-age=30672000";
            headers.Vary = "Accept-Encoding";
            headers.ContentEncoding = "gzip";
            headers.Expires = "Fri, 12 Jan 2018 22:01:55 GMT";
            headers.LastModified = "Wed, 22 Jun 2016 20:08:29 GMT";
            headers.SetCookie = "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";
            headers.ETag = "\"54ef7954-1078\"";
            headers.TransferEncoding = "chunked";
            headers.ContentLanguage = "en-gb";
            headers.Upgrade = "websocket";
            headers.Via = "1.1 varnish";
            headers.AccessControlAllowOrigin = "*";
            headers.AccessControlAllowCredentials = "true";
            headers.AccessControlExposeHeaders = "Client-Protocol, Content-Length, Content-Type, X-Bandwidth-Est, X-Bandwidth-Est2, X-Bandwidth-Est-Comp, X-Bandwidth-Avg, X-Walltime-Ms, X-Sequence-Num";
 
            var dateHeaderValues = _dateHeaderValueManager.GetDateHeaderValues();
            _responseHeadersDirect.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
            _responseHeadersDirect.SetRawServer("Kestrel", _bytesServer);
        }
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private void SetUnknown(int count)
    {
        for (var i = 0; i < count; i++)
        {
            AddHeaders();
        }
 
        void AddHeaders()
        {
            _responseHeadersDirect.Reset();
 
            _response.StatusCode = 200;
            _response.ContentType = "text/plain";
            _response.ContentLength = 13;
 
            var headers = _response.Headers;
 
            headers.Link = "<https://www.gravatar.com/avatar/6ae816bfaad7bbc58b17fac49ef5cced?d=404&s=250>; rel=\"canonical\"";
            headers.XUACompatible = "IE=Edge";
            headers.XPoweredBy = "ASP.NET";
            headers.XContentTypeOptions = "nosniff";
            headers.XXSSProtection = "1; mode=block";
            headers.XFrameOptions = "SAMEORIGIN";
            headers.StrictTransportSecurity = "max-age=31536000; includeSubDomains; preload";
            headers.ContentSecurityPolicy = "default-src 'none'; script-src 'self' cdnjs.cloudflare.com code.jquery.com scotthelme.disqus.com a.disquscdn.com www.google-analytics.com go.disqus.com platform.twitter.com cdn.syndication.twimg.com; style-src 'self' a.disquscdn.com fonts.googleapis.com cdnjs.cloudflare.com platform.twitter.com; img-src 'self' data: www.gravatar.com www.google-analytics.com links.services.disqus.com referrer.disqus.com a.disquscdn.com cdn.syndication.twimg.com syndication.twitter.com pbs.twimg.com platform.twitter.com abs.twimg.com; child-src fusiontables.googleusercontent.com fusiontables.google.com www.google.com disqus.com www.youtube.com syndication.twitter.com platform.twitter.com; frame-src fusiontables.googleusercontent.com fusiontables.google.com www.google.com disqus.com www.youtube.com syndication.twitter.com platform.twitter.com; connect-src 'self' links.services.disqus.com; font-src 'self' cdnjs.cloudflare.com fonts.gstatic.com fonts.googleapis.com; form-action 'self'; upgrade-insecure-requests;";
 
            var dateHeaderValues = _dateHeaderValueManager.GetDateHeaderValues();
            _responseHeadersDirect.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
            _responseHeadersDirect.SetRawServer("Kestrel", _bytesServer);
        }
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private long? GetContentLengthNumeric(int count)
    {
        long? length = null;
        for (var i = 0; i < count; i++)
        {
            length = _response.ContentLength;
        }
 
        return length;
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private string GetContentLengthString(int count)
    {
        string value = null;
        for (var i = 0; i < count; i++)
        {
            value = _response.Headers[HeaderNames.ContentLength];
        }
 
        return value;
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private string GetPlaintext(int count)
    {
        string value = null;
        for (var i = 0; i < count; i++)
        {
            value = ReadHeaders();
        }
 
        return value;
 
        string ReadHeaders()
        {
            var headers = _response.Headers;
            var value = headers.Date;
            value = headers.Server;
            value = headers.ContentType;
            return value;
        }
 
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private string GetCommon(int count)
    {
        string value = null;
        for (var i = 0; i < count; i++)
        {
            value = ReadHeaders();
        }
 
        return value;
 
        string ReadHeaders()
        {
            var headers = _response.Headers;
            var value = headers.Date;
            value = headers.Server;
            value = headers.ContentType;
 
            value = headers.Connection;
            value = headers.CacheControl;
            value = headers.Vary;
            value = headers.ContentEncoding;
            value = headers.Expires;
            value = headers.LastModified;
            value = headers.SetCookie;
            value = headers.ETag;
            value = headers.TransferEncoding;
            value = headers.ContentLanguage;
            value = headers.Upgrade;
            value = headers.Via;
            value = headers.AccessControlAllowOrigin;
            value = headers.AccessControlAllowCredentials;
            value = headers.AccessControlExposeHeaders;
 
            return value;
        }
    }
 
    [MethodImpl(MethodImplOptions.NoInlining)]
    private string GetUnknown(int count)
    {
        string value = null;
        for (var i = 0; i < count; i++)
        {
            value = ReadHeaders();
        }
 
        return value;
 
        string ReadHeaders()
        {
            var headers = _response.Headers;
            value = headers.Date;
            value = headers.Server;
            value = headers.ContentType;
 
            value = headers.Link;
            value = headers.XUACompatible;
            value = headers.XPoweredBy;
            value = headers.XContentTypeOptions;
            value = headers.XXSSProtection;
            value = headers.XFrameOptions;
            value = headers.StrictTransportSecurity;
            value = headers.ContentSecurityPolicy;
 
            return value;
        }
    }
 
    [IterationSetup]
    public void Setup()
    {
        var memoryPool = PinnedBlockMemoryPoolFactory.Create();
        var options = new PipeOptions(memoryPool, readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline, useSynchronizationContext: false);
        var pair = DuplexPipe.CreateConnectionPair(options, options);
 
        var serviceContext = TestContextFactory.CreateServiceContext(
            serverOptions: new KestrelServerOptions(),
            httpParser: new HttpParser<Http1ParsingHandler>(),
            dateHeaderValueManager: _dateHeaderValueManager);
 
        var connectionContext = TestContextFactory.CreateHttpConnectionContext(
            serviceContext: serviceContext,
            connectionContext: null,
            transport: pair.Transport,
            memoryPool: memoryPool,
            connectionFeatures: new FeatureCollection());
 
        var http1Connection = new Http1Connection(connectionContext);
 
        http1Connection.Reset();
        serviceContext.DateHeaderValueManager.OnHeartbeat();
 
        _responseHeadersDirect = (HttpResponseHeaders)http1Connection.ResponseHeaders;
        var context = new DefaultHttpContext(http1Connection);
        _response = context.Response;
 
        switch (Type)
        {
            case BenchmarkTypes.ContentLengthNumeric:
                SetContentLengthNumeric(1);
                GetContentLengthNumeric(1);
                break;
            case BenchmarkTypes.ContentLengthString:
                SetContentLengthString(1);
                GetContentLengthString(1);
                break;
            case BenchmarkTypes.Plaintext:
                SetPlaintext(1);
                GetPlaintext(1);
                break;
            case BenchmarkTypes.Common:
                SetCommon(1);
                GetCommon(1);
                break;
            case BenchmarkTypes.Unknown:
                SetUnknown(1);
                GetUnknown(1);
                break;
        }
    }
}