diff options
| author | Shadowghost <Ghost_of_Stone@web.de> | 2023-01-31 12:18:10 +0100 |
|---|---|---|
| committer | Shadowghost <Ghost_of_Stone@web.de> | 2023-02-02 18:50:33 +0100 |
| commit | f5f890e68562e55d4bed16c454c4b4305152b296 (patch) | |
| tree | b52e3b45ceb2faa446153866600b4456fed44c8b /Jellyfin.Api/Middleware | |
| parent | 58b3945805db4f88bc069ee84013bdf85d7429b1 (diff) | |
Migrate to file-scoped namespaces
Diffstat (limited to 'Jellyfin.Api/Middleware')
11 files changed, 470 insertions, 481 deletions
diff --git a/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs b/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs index 6bd9e0b08..7bcc328aa 100644 --- a/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs +++ b/Jellyfin.Api/Middleware/BaseUrlRedirectionMiddleware.cs @@ -7,75 +7,74 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; -namespace Jellyfin.Api.Middleware +namespace Jellyfin.Api.Middleware; + +/// <summary> +/// Redirect requests without baseurl prefix to the baseurl prefixed URL. +/// </summary> +public class BaseUrlRedirectionMiddleware { + private readonly RequestDelegate _next; + private readonly ILogger<BaseUrlRedirectionMiddleware> _logger; + private readonly IConfiguration _configuration; + /// <summary> - /// Redirect requests without baseurl prefix to the baseurl prefixed URL. + /// Initializes a new instance of the <see cref="BaseUrlRedirectionMiddleware"/> class. /// </summary> - public class BaseUrlRedirectionMiddleware + /// <param name="next">The next delegate in the pipeline.</param> + /// <param name="logger">The logger.</param> + /// <param name="configuration">The application configuration.</param> + public BaseUrlRedirectionMiddleware( + RequestDelegate next, + ILogger<BaseUrlRedirectionMiddleware> logger, + IConfiguration configuration) { - private readonly RequestDelegate _next; - private readonly ILogger<BaseUrlRedirectionMiddleware> _logger; - private readonly IConfiguration _configuration; + _next = next; + _logger = logger; + _configuration = configuration; + } - /// <summary> - /// Initializes a new instance of the <see cref="BaseUrlRedirectionMiddleware"/> class. - /// </summary> - /// <param name="next">The next delegate in the pipeline.</param> - /// <param name="logger">The logger.</param> - /// <param name="configuration">The application configuration.</param> - public BaseUrlRedirectionMiddleware( - RequestDelegate next, - ILogger<BaseUrlRedirectionMiddleware> logger, - IConfiguration configuration) - { - _next = next; - _logger = logger; - _configuration = configuration; - } + /// <summary> + /// Executes the middleware action. + /// </summary> + /// <param name="httpContext">The current HTTP context.</param> + /// <param name="serverConfigurationManager">The server configuration manager.</param> + /// <returns>The async task.</returns> + public async Task Invoke(HttpContext httpContext, IServerConfigurationManager serverConfigurationManager) + { + var localPath = httpContext.Request.Path.ToString(); + var baseUrlPrefix = serverConfigurationManager.GetNetworkConfiguration().BaseUrl; - /// <summary> - /// Executes the middleware action. - /// </summary> - /// <param name="httpContext">The current HTTP context.</param> - /// <param name="serverConfigurationManager">The server configuration manager.</param> - /// <returns>The async task.</returns> - public async Task Invoke(HttpContext httpContext, IServerConfigurationManager serverConfigurationManager) + if (string.IsNullOrEmpty(localPath) + || string.Equals(localPath, baseUrlPrefix, StringComparison.OrdinalIgnoreCase) + || string.Equals(localPath, baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase) + || string.Equals(localPath, baseUrlPrefix + "/web", StringComparison.OrdinalIgnoreCase) + || string.Equals(localPath, baseUrlPrefix + "/web/", StringComparison.OrdinalIgnoreCase) + || !localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase) + ) { - var localPath = httpContext.Request.Path.ToString(); - var baseUrlPrefix = serverConfigurationManager.GetNetworkConfiguration().BaseUrl; - - if (string.IsNullOrEmpty(localPath) - || string.Equals(localPath, baseUrlPrefix, StringComparison.OrdinalIgnoreCase) - || string.Equals(localPath, baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase) - || string.Equals(localPath, baseUrlPrefix + "/web", StringComparison.OrdinalIgnoreCase) - || string.Equals(localPath, baseUrlPrefix + "/web/", StringComparison.OrdinalIgnoreCase) - || !localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase) - ) + // Redirect health endpoint + if (string.Equals(localPath, "/health", StringComparison.OrdinalIgnoreCase) + || string.Equals(localPath, "/health/", StringComparison.OrdinalIgnoreCase)) { - // Redirect health endpoint - if (string.Equals(localPath, "/health", StringComparison.OrdinalIgnoreCase) - || string.Equals(localPath, "/health/", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogDebug("Redirecting /health check"); - httpContext.Response.Redirect(baseUrlPrefix + "/health"); - return; - } - - // Always redirect back to the default path if the base prefix is invalid or missing - _logger.LogDebug("Normalizing an URL at {LocalPath}", localPath); - - var port = httpContext.Request.Host.Port ?? -1; - var uri = new UriBuilder(httpContext.Request.Scheme, httpContext.Request.Host.Host, port, localPath).Uri; - var redirectUri = new UriBuilder(httpContext.Request.Scheme, httpContext.Request.Host.Host, port, baseUrlPrefix + "/" + _configuration[DefaultRedirectKey]).Uri; - var target = uri.MakeRelativeUri(redirectUri).ToString(); - _logger.LogDebug("Redirecting to {Target}", target); - - httpContext.Response.Redirect(target); + _logger.LogDebug("Redirecting /health check"); + httpContext.Response.Redirect(baseUrlPrefix + "/health"); return; } - await _next(httpContext).ConfigureAwait(false); + // Always redirect back to the default path if the base prefix is invalid or missing + _logger.LogDebug("Normalizing an URL at {LocalPath}", localPath); + + var port = httpContext.Request.Host.Port ?? -1; + var uri = new UriBuilder(httpContext.Request.Scheme, httpContext.Request.Host.Host, port, localPath).Uri; + var redirectUri = new UriBuilder(httpContext.Request.Scheme, httpContext.Request.Host.Host, port, baseUrlPrefix + "/" + _configuration[DefaultRedirectKey]).Uri; + var target = uri.MakeRelativeUri(redirectUri).ToString(); + _logger.LogDebug("Redirecting to {Target}", target); + + httpContext.Response.Redirect(target); + return; } + + await _next(httpContext).ConfigureAwait(false); } } diff --git a/Jellyfin.Api/Middleware/ExceptionMiddleware.cs b/Jellyfin.Api/Middleware/ExceptionMiddleware.cs index 6b3aeb187..060c14f89 100644 --- a/Jellyfin.Api/Middleware/ExceptionMiddleware.cs +++ b/Jellyfin.Api/Middleware/ExceptionMiddleware.cs @@ -12,140 +12,139 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -namespace Jellyfin.Api.Middleware +namespace Jellyfin.Api.Middleware; + +/// <summary> +/// Exception Middleware. +/// </summary> +public class ExceptionMiddleware { + private readonly RequestDelegate _next; + private readonly ILogger<ExceptionMiddleware> _logger; + private readonly IServerConfigurationManager _configuration; + private readonly IWebHostEnvironment _hostEnvironment; + /// <summary> - /// Exception Middleware. + /// Initializes a new instance of the <see cref="ExceptionMiddleware"/> class. /// </summary> - public class ExceptionMiddleware + /// <param name="next">Next request delegate.</param> + /// <param name="logger">Instance of the <see cref="ILogger{ExceptionMiddleware}"/> interface.</param> + /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> + /// <param name="hostEnvironment">Instance of the <see cref="IWebHostEnvironment"/> interface.</param> + public ExceptionMiddleware( + RequestDelegate next, + ILogger<ExceptionMiddleware> logger, + IServerConfigurationManager serverConfigurationManager, + IWebHostEnvironment hostEnvironment) { - private readonly RequestDelegate _next; - private readonly ILogger<ExceptionMiddleware> _logger; - private readonly IServerConfigurationManager _configuration; - private readonly IWebHostEnvironment _hostEnvironment; + _next = next; + _logger = logger; + _configuration = serverConfigurationManager; + _hostEnvironment = hostEnvironment; + } - /// <summary> - /// Initializes a new instance of the <see cref="ExceptionMiddleware"/> class. - /// </summary> - /// <param name="next">Next request delegate.</param> - /// <param name="logger">Instance of the <see cref="ILogger{ExceptionMiddleware}"/> interface.</param> - /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param> - /// <param name="hostEnvironment">Instance of the <see cref="IWebHostEnvironment"/> interface.</param> - public ExceptionMiddleware( - RequestDelegate next, - ILogger<ExceptionMiddleware> logger, - IServerConfigurationManager serverConfigurationManager, - IWebHostEnvironment hostEnvironment) + /// <summary> + /// Invoke request. + /// </summary> + /// <param name="context">Request context.</param> + /// <returns>Task.</returns> + public async Task Invoke(HttpContext context) + { + try { - _next = next; - _logger = logger; - _configuration = serverConfigurationManager; - _hostEnvironment = hostEnvironment; + await _next(context).ConfigureAwait(false); } - - /// <summary> - /// Invoke request. - /// </summary> - /// <param name="context">Request context.</param> - /// <returns>Task.</returns> - public async Task Invoke(HttpContext context) + catch (Exception ex) { - try + if (context.Response.HasStarted) { - await _next(context).ConfigureAwait(false); + _logger.LogWarning("The response has already started, the exception middleware will not be executed."); + throw; } - catch (Exception ex) - { - if (context.Response.HasStarted) - { - _logger.LogWarning("The response has already started, the exception middleware will not be executed."); - throw; - } - ex = GetActualException(ex); + ex = GetActualException(ex); - bool ignoreStackTrace = - ex is SocketException - || ex is IOException - || ex is OperationCanceledException - || ex is SecurityException - || ex is AuthenticationException - || ex is FileNotFoundException; + bool ignoreStackTrace = + ex is SocketException + || ex is IOException + || ex is OperationCanceledException + || ex is SecurityException + || ex is AuthenticationException + || ex is FileNotFoundException; - if (ignoreStackTrace) - { - _logger.LogError( - "Error processing request: {ExceptionMessage}. URL {Method} {Url}.", - ex.Message.TrimEnd('.'), - context.Request.Method, - context.Request.Path); - } - else - { - _logger.LogError( - ex, - "Error processing request. URL {Method} {Url}.", - context.Request.Method, - context.Request.Path); - } + if (ignoreStackTrace) + { + _logger.LogError( + "Error processing request: {ExceptionMessage}. URL {Method} {Url}.", + ex.Message.TrimEnd('.'), + context.Request.Method, + context.Request.Path); + } + else + { + _logger.LogError( + ex, + "Error processing request. URL {Method} {Url}.", + context.Request.Method, + context.Request.Path); + } - context.Response.StatusCode = GetStatusCode(ex); - context.Response.ContentType = MediaTypeNames.Text.Plain; + context.Response.StatusCode = GetStatusCode(ex); + context.Response.ContentType = MediaTypeNames.Text.Plain; - // Don't send exception unless the server is in a Development environment - var errorContent = _hostEnvironment.IsDevelopment() - ? NormalizeExceptionMessage(ex.Message) - : "Error processing request."; - await context.Response.WriteAsync(errorContent).ConfigureAwait(false); - } + // Don't send exception unless the server is in a Development environment + var errorContent = _hostEnvironment.IsDevelopment() + ? NormalizeExceptionMessage(ex.Message) + : "Error processing request."; + await context.Response.WriteAsync(errorContent).ConfigureAwait(false); } + } - private static Exception GetActualException(Exception ex) + private static Exception GetActualException(Exception ex) + { + if (ex is AggregateException agg) { - if (ex is AggregateException agg) + var inner = agg.InnerException; + if (inner is not null) { - var inner = agg.InnerException; - if (inner is not null) - { - return GetActualException(inner); - } - - var inners = agg.InnerExceptions; - if (inners.Count > 0) - { - return GetActualException(inners[0]); - } + return GetActualException(inner); } - return ex; - } - - private static int GetStatusCode(Exception ex) - { - switch (ex) + var inners = agg.InnerExceptions; + if (inners.Count > 0) { - case ArgumentException _: return StatusCodes.Status400BadRequest; - case AuthenticationException _: return StatusCodes.Status401Unauthorized; - case SecurityException _: return StatusCodes.Status403Forbidden; - case DirectoryNotFoundException _: - case FileNotFoundException _: - case ResourceNotFoundException _: return StatusCodes.Status404NotFound; - case MethodNotAllowedException _: return StatusCodes.Status405MethodNotAllowed; - default: return StatusCodes.Status500InternalServerError; + return GetActualException(inners[0]); } } - private string NormalizeExceptionMessage(string msg) + return ex; + } + + private static int GetStatusCode(Exception ex) + { + switch (ex) { - // Strip any information we don't want to reveal - return msg.Replace( - _configuration.ApplicationPaths.ProgramSystemPath, - string.Empty, - StringComparison.OrdinalIgnoreCase) - .Replace( - _configuration.ApplicationPaths.ProgramDataPath, - string.Empty, - StringComparison.OrdinalIgnoreCase); + case ArgumentException _: return StatusCodes.Status400BadRequest; + case AuthenticationException _: return StatusCodes.Status401Unauthorized; + case SecurityException _: return StatusCodes.Status403Forbidden; + case DirectoryNotFoundException _: + case FileNotFoundException _: + case ResourceNotFoundException _: return StatusCodes.Status404NotFound; + case MethodNotAllowedException _: return StatusCodes.Status405MethodNotAllowed; + default: return StatusCodes.Status500InternalServerError; } } + + private string NormalizeExceptionMessage(string msg) + { + // Strip any information we don't want to reveal + return msg.Replace( + _configuration.ApplicationPaths.ProgramSystemPath, + string.Empty, + StringComparison.OrdinalIgnoreCase) + .Replace( + _configuration.ApplicationPaths.ProgramDataPath, + string.Empty, + StringComparison.OrdinalIgnoreCase); + } } diff --git a/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs b/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs index f7af91e48..f45b6b5c0 100644 --- a/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs +++ b/Jellyfin.Api/Middleware/IpBasedAccessValidationMiddleware.cs @@ -4,47 +4,46 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using Microsoft.AspNetCore.Http; -namespace Jellyfin.Api.Middleware +namespace Jellyfin.Api.Middleware; + +/// <summary> +/// Validates the IP of requests coming from local networks wrt. remote access. +/// </summary> +public class IpBasedAccessValidationMiddleware { + private readonly RequestDelegate _next; + /// <summary> - /// Validates the IP of requests coming from local networks wrt. remote access. + /// Initializes a new instance of the <see cref="IpBasedAccessValidationMiddleware"/> class. /// </summary> - public class IpBasedAccessValidationMiddleware + /// <param name="next">The next delegate in the pipeline.</param> + public IpBasedAccessValidationMiddleware(RequestDelegate next) { - private readonly RequestDelegate _next; + _next = next; + } - /// <summary> - /// Initializes a new instance of the <see cref="IpBasedAccessValidationMiddleware"/> class. - /// </summary> - /// <param name="next">The next delegate in the pipeline.</param> - public IpBasedAccessValidationMiddleware(RequestDelegate next) + /// <summary> + /// Executes the middleware action. + /// </summary> + /// <param name="httpContext">The current HTTP context.</param> + /// <param name="networkManager">The network manager.</param> + /// <returns>The async task.</returns> + public async Task Invoke(HttpContext httpContext, INetworkManager networkManager) + { + if (httpContext.IsLocal()) { - _next = next; + // Running locally. + await _next(httpContext).ConfigureAwait(false); + return; } - /// <summary> - /// Executes the middleware action. - /// </summary> - /// <param name="httpContext">The current HTTP context.</param> - /// <param name="networkManager">The network manager.</param> - /// <returns>The async task.</returns> - public async Task Invoke(HttpContext httpContext, INetworkManager networkManager) - { - if (httpContext.IsLocal()) - { - // Running locally. - await _next(httpContext).ConfigureAwait(false); - return; - } - - var remoteIp = httpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback; - - if (!networkManager.HasRemoteAccess(remoteIp)) - { - return; - } + var remoteIp = httpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback; - await _next(httpContext).ConfigureAwait(false); + if (!networkManager.HasRemoteAccess(remoteIp)) + { + return; } + + await _next(httpContext).ConfigureAwait(false); } } diff --git a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs index 18f13bbce..7b05351e3 100644 --- a/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs +++ b/Jellyfin.Api/Middleware/LanFilteringMiddleware.cs @@ -5,41 +5,40 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using Microsoft.AspNetCore.Http; -namespace Jellyfin.Api.Middleware +namespace Jellyfin.Api.Middleware; + +/// <summary> +/// Validates the LAN host IP based on application configuration. +/// </summary> +public class LanFilteringMiddleware { + private readonly RequestDelegate _next; + /// <summary> - /// Validates the LAN host IP based on application configuration. + /// Initializes a new instance of the <see cref="LanFilteringMiddleware"/> class. /// </summary> - public class LanFilteringMiddleware + /// <param name="next">The next delegate in the pipeline.</param> + public LanFilteringMiddleware(RequestDelegate next) { - private readonly RequestDelegate _next; + _next = next; + } - /// <summary> - /// Initializes a new instance of the <see cref="LanFilteringMiddleware"/> class. - /// </summary> - /// <param name="next">The next delegate in the pipeline.</param> - public LanFilteringMiddleware(RequestDelegate next) - { - _next = next; - } + /// <summary> + /// Executes the middleware action. + /// </summary> + /// <param name="httpContext">The current HTTP context.</param> + /// <param name="networkManager">The network manager.</param> + /// <param name="serverConfigurationManager">The server configuration manager.</param> + /// <returns>The async task.</returns> + public async Task Invoke(HttpContext httpContext, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager) + { + var host = httpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback; - /// <summary> - /// Executes the middleware action. - /// </summary> - /// <param name="httpContext">The current HTTP context.</param> - /// <param name="networkManager">The network manager.</param> - /// <param name="serverConfigurationManager">The server configuration manager.</param> - /// <returns>The async task.</returns> - public async Task Invoke(HttpContext httpContext, INetworkManager networkManager, IServerConfigurationManager serverConfigurationManager) + if (!networkManager.IsInLocalNetwork(host) && !serverConfigurationManager.GetNetworkConfiguration().EnableRemoteAccess) { - var host = httpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback; - - if (!networkManager.IsInLocalNetwork(host) && !serverConfigurationManager.GetNetworkConfiguration().EnableRemoteAccess) - { - return; - } - - await _next(httpContext).ConfigureAwait(false); + return; } + + await _next(httpContext).ConfigureAwait(false); } } diff --git a/Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs b/Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs index b73923c1e..17d8997d5 100644 --- a/Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs +++ b/Jellyfin.Api/Middleware/LegacyEmbyRouteRewriteMiddleware.cs @@ -3,52 +3,51 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -namespace Jellyfin.Api.Middleware +namespace Jellyfin.Api.Middleware; + +/// <summary> +/// Removes /emby and /mediabrowser from requested route. +/// </summary> +public class LegacyEmbyRouteRewriteMiddleware { + private const string EmbyPath = "/emby"; + private const string MediabrowserPath = "/mediabrowser"; + + private readonly RequestDelegate _next; + private readonly ILogger<LegacyEmbyRouteRewriteMiddleware> _logger; + /// <summary> - /// Removes /emby and /mediabrowser from requested route. + /// Initializes a new instance of the <see cref="LegacyEmbyRouteRewriteMiddleware"/> class. /// </summary> - public class LegacyEmbyRouteRewriteMiddleware + /// <param name="next">The next delegate in the pipeline.</param> + /// <param name="logger">The logger.</param> + public LegacyEmbyRouteRewriteMiddleware( + RequestDelegate next, + ILogger<LegacyEmbyRouteRewriteMiddleware> logger) { - private const string EmbyPath = "/emby"; - private const string MediabrowserPath = "/mediabrowser"; - - private readonly RequestDelegate _next; - private readonly ILogger<LegacyEmbyRouteRewriteMiddleware> _logger; + _next = next; + _logger = logger; + } - /// <summary> - /// Initializes a new instance of the <see cref="LegacyEmbyRouteRewriteMiddleware"/> class. - /// </summary> - /// <param name="next">The next delegate in the pipeline.</param> - /// <param name="logger">The logger.</param> - public LegacyEmbyRouteRewriteMiddleware( - RequestDelegate next, - ILogger<LegacyEmbyRouteRewriteMiddleware> logger) + /// <summary> + /// Executes the middleware action. + /// </summary> + /// <param name="httpContext">The current HTTP context.</param> + /// <returns>The async task.</returns> + public async Task Invoke(HttpContext httpContext) + { + var localPath = httpContext.Request.Path.ToString(); + if (localPath.StartsWith(EmbyPath, StringComparison.OrdinalIgnoreCase)) { - _next = next; - _logger = logger; + httpContext.Request.Path = localPath[EmbyPath.Length..]; + _logger.LogDebug("Removing {EmbyPath} from route.", EmbyPath); } - - /// <summary> - /// Executes the middleware action. - /// </summary> - /// <param name="httpContext">The current HTTP context.</param> - /// <returns>The async task.</returns> - public async Task Invoke(HttpContext httpContext) + else if (localPath.StartsWith(MediabrowserPath, StringComparison.OrdinalIgnoreCase)) { - var localPath = httpContext.Request.Path.ToString(); - if (localPath.StartsWith(EmbyPath, StringComparison.OrdinalIgnoreCase)) - { - httpContext.Request.Path = localPath[EmbyPath.Length..]; - _logger.LogDebug("Removing {EmbyPath} from route.", EmbyPath); - } - else if (localPath.StartsWith(MediabrowserPath, StringComparison.OrdinalIgnoreCase)) - { - httpContext.Request.Path = localPath[MediabrowserPath.Length..]; - _logger.LogDebug("Removing {MediabrowserPath} from route.", MediabrowserPath); - } - - await _next(httpContext).ConfigureAwait(false); + httpContext.Request.Path = localPath[MediabrowserPath.Length..]; + _logger.LogDebug("Removing {MediabrowserPath} from route.", MediabrowserPath); } + + await _next(httpContext).ConfigureAwait(false); } } diff --git a/Jellyfin.Api/Middleware/QueryStringDecodingMiddleware.cs b/Jellyfin.Api/Middleware/QueryStringDecodingMiddleware.cs index 4b6304e0e..cb4169e99 100644 --- a/Jellyfin.Api/Middleware/QueryStringDecodingMiddleware.cs +++ b/Jellyfin.Api/Middleware/QueryStringDecodingMiddleware.cs @@ -2,38 +2,37 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; -namespace Jellyfin.Api.Middleware +namespace Jellyfin.Api.Middleware; + +/// <summary> +/// URL decodes the querystring before binding. +/// </summary> +public class QueryStringDecodingMiddleware { + private readonly RequestDelegate _next; + /// <summary> - /// URL decodes the querystring before binding. + /// Initializes a new instance of the <see cref="QueryStringDecodingMiddleware"/> class. /// </summary> - public class QueryStringDecodingMiddleware + /// <param name="next">The next delegate in the pipeline.</param> + public QueryStringDecodingMiddleware(RequestDelegate next) { - private readonly RequestDelegate _next; + _next = next; + } - /// <summary> - /// Initializes a new instance of the <see cref="QueryStringDecodingMiddleware"/> class. - /// </summary> - /// <param name="next">The next delegate in the pipeline.</param> - public QueryStringDecodingMiddleware(RequestDelegate next) + /// <summary> + /// Executes the middleware action. + /// </summary> + /// <param name="httpContext">The current HTTP context.</param> + /// <returns>The async task.</returns> + public async Task Invoke(HttpContext httpContext) + { + var feature = httpContext.Features.Get<IQueryFeature>(); + if (feature is not null) { - _next = next; + httpContext.Features.Set<IQueryFeature>(new UrlDecodeQueryFeature(feature)); } - /// <summary> - /// Executes the middleware action. - /// </summary> - /// <param name="httpContext">The current HTTP context.</param> - /// <returns>The async task.</returns> - public async Task Invoke(HttpContext httpContext) - { - var feature = httpContext.Features.Get<IQueryFeature>(); - if (feature is not null) - { - httpContext.Features.Set<IQueryFeature>(new UrlDecodeQueryFeature(feature)); - } - - await _next(httpContext).ConfigureAwait(false); - } + await _next(httpContext).ConfigureAwait(false); } } diff --git a/Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs b/Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs index 3701d0f45..db3917743 100644 --- a/Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs +++ b/Jellyfin.Api/Middleware/ResponseTimeMiddleware.cs @@ -7,63 +7,62 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Logging; -namespace Jellyfin.Api.Middleware +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> - /// Response time middleware. + /// Initializes a new instance of the <see cref="ResponseTimeMiddleware"/> class. /// </summary> - public class ResponseTimeMiddleware + /// <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) { - private const string ResponseHeaderResponseTime = "X-Response-Time-ms"; - - private readonly RequestDelegate _next; - private readonly ILogger<ResponseTimeMiddleware> _logger; + _next = next; + _logger = 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(); - /// <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 enableWarning = serverConfigurationManager.Configuration.EnableSlowResponseWarning; + var warningThreshold = serverConfigurationManager.Configuration.SlowResponseThresholdMs; + context.Response.OnStarting(() => { - 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)) { - 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); - } + _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; - }); + 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); - } + // Call the next delegate/middleware in the pipeline + await this._next(context).ConfigureAwait(false); } } diff --git a/Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs b/Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs index 2e69580be..8bf626035 100644 --- a/Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs +++ b/Jellyfin.Api/Middleware/RobotsRedirectionMiddleware.cs @@ -3,45 +3,44 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -namespace Jellyfin.Api.Middleware +namespace Jellyfin.Api.Middleware; + +/// <summary> +/// Redirect requests to robots.txt to web/robots.txt. +/// </summary> +public class RobotsRedirectionMiddleware { + private readonly RequestDelegate _next; + private readonly ILogger<RobotsRedirectionMiddleware> _logger; + /// <summary> - /// Redirect requests to robots.txt to web/robots.txt. + /// Initializes a new instance of the <see cref="RobotsRedirectionMiddleware"/> class. /// </summary> - public class RobotsRedirectionMiddleware + /// <param name="next">The next delegate in the pipeline.</param> + /// <param name="logger">The logger.</param> + public RobotsRedirectionMiddleware( + RequestDelegate next, + ILogger<RobotsRedirectionMiddleware> logger) { - private readonly RequestDelegate _next; - private readonly ILogger<RobotsRedirectionMiddleware> _logger; + _next = next; + _logger = logger; + } - /// <summary> - /// Initializes a new instance of the <see cref="RobotsRedirectionMiddleware"/> class. - /// </summary> - /// <param name="next">The next delegate in the pipeline.</param> - /// <param name="logger">The logger.</param> - public RobotsRedirectionMiddleware( - RequestDelegate next, - ILogger<RobotsRedirectionMiddleware> logger) + /// <summary> + /// Executes the middleware action. + /// </summary> + /// <param name="httpContext">The current HTTP context.</param> + /// <returns>The async task.</returns> + public async Task Invoke(HttpContext httpContext) + { + var localPath = httpContext.Request.Path.ToString(); + if (string.Equals(localPath, "/robots.txt", StringComparison.OrdinalIgnoreCase)) { - _next = next; - _logger = logger; + _logger.LogDebug("Redirecting robots.txt request to web/robots.txt"); + httpContext.Response.Redirect("web/robots.txt"); + return; } - /// <summary> - /// Executes the middleware action. - /// </summary> - /// <param name="httpContext">The current HTTP context.</param> - /// <returns>The async task.</returns> - public async Task Invoke(HttpContext httpContext) - { - var localPath = httpContext.Request.Path.ToString(); - if (string.Equals(localPath, "/robots.txt", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogDebug("Redirecting robots.txt request to web/robots.txt"); - httpContext.Response.Redirect("web/robots.txt"); - return; - } - - await _next(httpContext).ConfigureAwait(false); - } + await _next(httpContext).ConfigureAwait(false); } } diff --git a/Jellyfin.Api/Middleware/ServerStartupMessageMiddleware.cs b/Jellyfin.Api/Middleware/ServerStartupMessageMiddleware.cs index dcd64401a..dcb234658 100644 --- a/Jellyfin.Api/Middleware/ServerStartupMessageMiddleware.cs +++ b/Jellyfin.Api/Middleware/ServerStartupMessageMiddleware.cs @@ -5,47 +5,46 @@ using MediaBrowser.Controller; using MediaBrowser.Model.Globalization; using Microsoft.AspNetCore.Http; -namespace Jellyfin.Api.Middleware +namespace Jellyfin.Api.Middleware; + +/// <summary> +/// Shows a custom message during server startup. +/// </summary> +public class ServerStartupMessageMiddleware { + private readonly RequestDelegate _next; + /// <summary> - /// Shows a custom message during server startup. + /// Initializes a new instance of the <see cref="ServerStartupMessageMiddleware"/> class. /// </summary> - public class ServerStartupMessageMiddleware + /// <param name="next">The next delegate in the pipeline.</param> + public ServerStartupMessageMiddleware(RequestDelegate next) { - private readonly RequestDelegate _next; + _next = next; + } - /// <summary> - /// Initializes a new instance of the <see cref="ServerStartupMessageMiddleware"/> class. - /// </summary> - /// <param name="next">The next delegate in the pipeline.</param> - public ServerStartupMessageMiddleware(RequestDelegate next) + /// <summary> + /// Executes the middleware action. + /// </summary> + /// <param name="httpContext">The current HTTP context.</param> + /// <param name="serverApplicationHost">The server application host.</param> + /// <param name="localizationManager">The localization manager.</param> + /// <returns>The async task.</returns> + public async Task Invoke( + HttpContext httpContext, + IServerApplicationHost serverApplicationHost, + ILocalizationManager localizationManager) + { + if (serverApplicationHost.CoreStartupHasCompleted + || httpContext.Request.Path.Equals("/system/ping", StringComparison.OrdinalIgnoreCase)) { - _next = next; + await _next(httpContext).ConfigureAwait(false); + return; } - /// <summary> - /// Executes the middleware action. - /// </summary> - /// <param name="httpContext">The current HTTP context.</param> - /// <param name="serverApplicationHost">The server application host.</param> - /// <param name="localizationManager">The localization manager.</param> - /// <returns>The async task.</returns> - public async Task Invoke( - HttpContext httpContext, - IServerApplicationHost serverApplicationHost, - ILocalizationManager localizationManager) - { - if (serverApplicationHost.CoreStartupHasCompleted - || httpContext.Request.Path.Equals("/system/ping", StringComparison.OrdinalIgnoreCase)) - { - await _next(httpContext).ConfigureAwait(false); - return; - } - - var message = localizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); - httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; - httpContext.Response.ContentType = MediaTypeNames.Text.Html; - await httpContext.Response.WriteAsync(message, httpContext.RequestAborted).ConfigureAwait(false); - } + var message = localizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); + httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; + httpContext.Response.ContentType = MediaTypeNames.Text.Html; + await httpContext.Response.WriteAsync(message, httpContext.RequestAborted).ConfigureAwait(false); } } diff --git a/Jellyfin.Api/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Api/Middleware/UrlDecodeQueryFeature.cs index d35e0fcfd..f75d0d24e 100644 --- a/Jellyfin.Api/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Api/Middleware/UrlDecodeQueryFeature.cs @@ -6,79 +6,78 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Primitives; -namespace Jellyfin.Api.Middleware +namespace Jellyfin.Api.Middleware; + +/// <summary> +/// Defines the <see cref="UrlDecodeQueryFeature"/>. +/// </summary> +public class UrlDecodeQueryFeature : IQueryFeature { + private IQueryCollection? _store; + /// <summary> - /// Defines the <see cref="UrlDecodeQueryFeature"/>. + /// Initializes a new instance of the <see cref="UrlDecodeQueryFeature"/> class. /// </summary> - public class UrlDecodeQueryFeature : IQueryFeature + /// <param name="feature">The <see cref="IQueryFeature"/> instance.</param> + public UrlDecodeQueryFeature(IQueryFeature feature) { - private IQueryCollection? _store; + Query = feature.Query; + } - /// <summary> - /// Initializes a new instance of the <see cref="UrlDecodeQueryFeature"/> class. - /// </summary> - /// <param name="feature">The <see cref="IQueryFeature"/> instance.</param> - public UrlDecodeQueryFeature(IQueryFeature feature) + /// <summary> + /// Gets or sets a value indicating the url decoded <see cref="IQueryCollection"/>. + /// </summary> + public IQueryCollection Query + { + get { - Query = feature.Query; + return _store ?? QueryCollection.Empty; } - /// <summary> - /// Gets or sets a value indicating the url decoded <see cref="IQueryCollection"/>. - /// </summary> - public IQueryCollection Query + set { - get + // Only interested in where the querystring is encoded which shows up as one key with nothing in the value. + if (value.Count != 1) { - return _store ?? QueryCollection.Empty; + _store = value; + return; } - set + // Encoded querystrings have no value, so don't process anything if a value is present. + var (key, stringValues) = value.First(); + if (!string.IsNullOrEmpty(stringValues)) { - // Only interested in where the querystring is encoded which shows up as one key with nothing in the value. - if (value.Count != 1) - { - _store = value; - return; - } + _store = value; + return; + } - // Encoded querystrings have no value, so don't process anything if a value is present. - var (key, stringValues) = value.First(); - if (!string.IsNullOrEmpty(stringValues)) - { - _store = value; - return; - } + if (!key.Contains('=', StringComparison.Ordinal)) + { + _store = value; + return; + } - if (!key.Contains('=', StringComparison.Ordinal)) + var pairs = new Dictionary<string, StringValues>(); + foreach (var pair in key.SpanSplit('&')) + { + var i = pair.IndexOf('='); + if (i == -1) { - _store = value; - return; + // encoded is an equals. + // We use TryAdd so duplicate keys get ignored + pairs.TryAdd(pair.ToString(), StringValues.Empty); + continue; } - var pairs = new Dictionary<string, StringValues>(); - foreach (var pair in key.SpanSplit('&')) + var k = pair[..i].ToString(); + var v = pair[(i + 1)..].ToString(); + if (!pairs.TryAdd(k, new StringValues(v))) { - var i = pair.IndexOf('='); - if (i == -1) - { - // encoded is an equals. - // We use TryAdd so duplicate keys get ignored - pairs.TryAdd(pair.ToString(), StringValues.Empty); - continue; - } - - var k = pair[..i].ToString(); - var v = pair[(i + 1)..].ToString(); - if (!pairs.TryAdd(k, new StringValues(v))) - { - pairs[k] = StringValues.Concat(pairs[k], v); - } + pairs[k] = StringValues.Concat(pairs[k], v); } - - _store = new QueryCollection(pairs); } + + _store = new QueryCollection(pairs); } } } diff --git a/Jellyfin.Api/Middleware/WebSocketHandlerMiddleware.cs b/Jellyfin.Api/Middleware/WebSocketHandlerMiddleware.cs index 2cf1e5e4a..009fb6269 100644 --- a/Jellyfin.Api/Middleware/WebSocketHandlerMiddleware.cs +++ b/Jellyfin.Api/Middleware/WebSocketHandlerMiddleware.cs @@ -2,39 +2,38 @@ using System.Threading.Tasks; using MediaBrowser.Controller.Net; using Microsoft.AspNetCore.Http; -namespace Jellyfin.Api.Middleware +namespace Jellyfin.Api.Middleware; + +/// <summary> +/// Handles WebSocket requests. +/// </summary> +public class WebSocketHandlerMiddleware { + private readonly RequestDelegate _next; + /// <summary> - /// Handles WebSocket requests. + /// Initializes a new instance of the <see cref="WebSocketHandlerMiddleware"/> class. /// </summary> - public class WebSocketHandlerMiddleware + /// <param name="next">The next delegate in the pipeline.</param> + public WebSocketHandlerMiddleware(RequestDelegate next) { - private readonly RequestDelegate _next; + _next = next; + } - /// <summary> - /// Initializes a new instance of the <see cref="WebSocketHandlerMiddleware"/> class. - /// </summary> - /// <param name="next">The next delegate in the pipeline.</param> - public WebSocketHandlerMiddleware(RequestDelegate next) + /// <summary> + /// Executes the middleware action. + /// </summary> + /// <param name="httpContext">The current HTTP context.</param> + /// <param name="webSocketManager">The WebSocket connection manager.</param> + /// <returns>The async task.</returns> + public async Task Invoke(HttpContext httpContext, IWebSocketManager webSocketManager) + { + if (!httpContext.WebSockets.IsWebSocketRequest) { - _next = next; + await _next(httpContext).ConfigureAwait(false); + return; } - /// <summary> - /// Executes the middleware action. - /// </summary> - /// <param name="httpContext">The current HTTP context.</param> - /// <param name="webSocketManager">The WebSocket connection manager.</param> - /// <returns>The async task.</returns> - public async Task Invoke(HttpContext httpContext, IWebSocketManager webSocketManager) - { - if (!httpContext.WebSockets.IsWebSocketRequest) - { - await _next(httpContext).ConfigureAwait(false); - return; - } - - await webSocketManager.WebSocketRequestHandler(httpContext).ConfigureAwait(false); - } + await webSocketManager.WebSocketRequestHandler(httpContext).ConfigureAwait(false); } } |
