File: Latency\AddServerTimingHeaderMiddleware.cs
Web Access
Project: src\src\Libraries\Microsoft.AspNetCore.Diagnostics.Middleware\Microsoft.AspNetCore.Diagnostics.Middleware.csproj (Microsoft.AspNetCore.Diagnostics.Middleware)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.Latency;
using Microsoft.Extensions.Primitives;
using Microsoft.Shared.Diagnostics;
 
namespace Microsoft.AspNetCore.Diagnostics.Latency;
 
/// <summary>
/// A middleware that populates Server-Timing header with response processing time.
/// </summary>
internal sealed class AddServerTimingHeaderMiddleware
{
    internal const string ServerTimingHeaderName = "Server-Timing";
    private readonly RequestDelegate _next;
 
    public AddServerTimingHeaderMiddleware(RequestDelegate next)
    {
        _next = Throw.IfNull(next);
    }
 
    /// <summary>
    /// Request handling method.
    /// </summary>
    /// <param name="context">The <see cref="HttpContext"/> for the current request.</param>
    /// <returns>A <see cref="Task"/> that represents the execution of this middleware.</returns>
    public Task InvokeAsync(HttpContext context)
    {
        context.Response.OnStarting(static ctx =>
        {
            var httpContext = (HttpContext)ctx;
            var latencyContext = httpContext.RequestServices.GetRequiredService<ILatencyContext>();
 
            if (latencyContext.TryGetCheckpoint(RequestCheckpointConstants.ElapsedTillHeaders, out var timestamp, out var timestampFrequency))
            {
                var elapsedMs = (long)(((double)timestamp / timestampFrequency) * 1000);
 
                if (httpContext.Response.Headers.TryGetValue(ServerTimingHeaderName, out var existing))
                {
                    httpContext.Response.Headers[ServerTimingHeaderName] = $"{existing}, reqlatency;dur={elapsedMs}";
                }
                else
                {
                    httpContext.Response.Headers.Append(ServerTimingHeaderName, $"reqlatency;dur={elapsedMs}");
                }
            }
 
            return Task.CompletedTask;
        }, context);
 
        return _next(context);
    }
}