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.Server.Middleware { /// /// Response time middleware. /// public class ResponseTimeMiddleware { private const string ResponseHeaderResponseTime = "X-Response-Time-ms"; private readonly RequestDelegate _next; private readonly ILogger _logger; private readonly bool _enableWarning; private readonly long _warningThreshold; /// /// Initializes a new instance of the class. /// /// Next request delegate. /// Instance of the interface. /// Instance of the interface. public ResponseTimeMiddleware( RequestDelegate next, ILogger logger, IServerConfigurationManager serverConfigurationManager) { _next = next; _logger = logger; _enableWarning = serverConfigurationManager.Configuration.EnableSlowResponseWarning; _warningThreshold = serverConfigurationManager.Configuration.SlowResponseThresholdMs; } /// /// Invoke request. /// /// Request context. /// Task. public async Task Invoke(HttpContext context) { var watch = new Stopwatch(); watch.Start(); context.Response.OnStarting(() => { watch.Stop(); LogWarning(context, watch); var responseTimeForCompleteRequest = watch.ElapsedMilliseconds; context.Response.Headers[ResponseHeaderResponseTime] = responseTimeForCompleteRequest.ToString(CultureInfo.InvariantCulture); return Task.CompletedTask; }); // Call the next delegate/middleware in the pipeline await this._next(context).ConfigureAwait(false); } private void LogWarning(HttpContext context, Stopwatch watch) { if (_enableWarning && watch.ElapsedMilliseconds > _warningThreshold) { _logger.LogWarning( "Slow HTTP Response from {Url} to {RemoteIp} in {Elapsed:g} with Status Code {StatusCode}", context.Request.GetDisplayUrl(), context.GetNormalizedRemoteIp(), watch.Elapsed, context.Response.StatusCode); } } } }