using System.Diagnostics; using System.Globalization; using System.Threading.Tasks; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Logging; namespace Jellyfin.Api.Middleware; /// <summary> /// Response time middleware. /// </summary> public class ResponseTimeMiddleware { private const string ResponseHeaderResponseTime = "X-Response-Time-ms"; private readonly RequestDelegate _next; private readonly ILogger<ResponseTimeMiddleware> _logger; /// <summary> /// Initializes a new instance of the <see cref="ResponseTimeMiddleware"/> class. /// </summary> /// <param name="next">Next request delegate.</param> /// <param name="logger">Instance of the <see cref="ILogger{ExceptionMiddleware}"/> interface.</param> public ResponseTimeMiddleware( RequestDelegate next, ILogger<ResponseTimeMiddleware> logger) { _next = next; _logger = logger; } /// <summary> /// Invoke request. /// </summary> /// <param name="context">Request context.</param> /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> /// <returns>Task.</returns> public async Task Invoke(HttpContext context, IServerConfigurationManager serverConfigurationManager) { var startTimestamp = Stopwatch.GetTimestamp(); var enableWarning = serverConfigurationManager.Configuration.EnableSlowResponseWarning; var warningThreshold = serverConfigurationManager.Configuration.SlowResponseThresholdMs; context.Response.OnStarting(() => { var responseTime = Stopwatch.GetElapsedTime(startTimestamp); var responseTimeMs = responseTime.TotalMilliseconds; if (enableWarning && responseTimeMs > warningThreshold && _logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug( "Slow HTTP Response from {Url} to {RemoteIP} in {Elapsed:g} with Status Code {StatusCode}", context.Request.GetDisplayUrl(), context.GetNormalizedRemoteIP(), responseTime, context.Response.StatusCode); } context.Response.Headers[ResponseHeaderResponseTime] = responseTimeMs.ToString(CultureInfo.InvariantCulture); return Task.CompletedTask; }); // Call the next delegate/middleware in the pipeline await this._next(context).ConfigureAwait(false); } }