< Summary - Jellyfin

Information
Class: Jellyfin.Api.Middleware.ExceptionMiddleware
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Middleware/ExceptionMiddleware.cs
Line coverage
43%
Covered lines: 14
Uncovered lines: 18
Coverable lines: 32
Total lines: 150
Line coverage: 43.7%
Branch coverage
55%
Covered branches: 11
Total branches: 20
Branch coverage: 55%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetActualException(...)33.33%21.19625%
GetStatusCode(...)64.28%23.431463.63%
NormalizeExceptionMessage(...)100%210%

File(s)

/srv/git/jellyfin/Jellyfin.Api/Middleware/ExceptionMiddleware.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using System.Net.Mime;
 4using System.Net.Sockets;
 5using System.Threading.Tasks;
 6using MediaBrowser.Common.Extensions;
 7using MediaBrowser.Controller.Authentication;
 8using MediaBrowser.Controller.Configuration;
 9using MediaBrowser.Controller.Net;
 10using Microsoft.AspNetCore.Hosting;
 11using Microsoft.AspNetCore.Http;
 12using Microsoft.Extensions.Hosting;
 13using Microsoft.Extensions.Logging;
 14
 15namespace Jellyfin.Api.Middleware;
 16
 17/// <summary>
 18/// Exception Middleware.
 19/// </summary>
 20public class ExceptionMiddleware
 21{
 22    private readonly RequestDelegate _next;
 23    private readonly ILogger<ExceptionMiddleware> _logger;
 24    private readonly IServerConfigurationManager _configuration;
 25    private readonly IWebHostEnvironment _hostEnvironment;
 26
 27    /// <summary>
 28    /// Initializes a new instance of the <see cref="ExceptionMiddleware"/> class.
 29    /// </summary>
 30    /// <param name="next">Next request delegate.</param>
 31    /// <param name="logger">Instance of the <see cref="ILogger{ExceptionMiddleware}"/> interface.</param>
 32    /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</p
 33    /// <param name="hostEnvironment">Instance of the <see cref="IWebHostEnvironment"/> interface.</param>
 34    public ExceptionMiddleware(
 35        RequestDelegate next,
 36        ILogger<ExceptionMiddleware> logger,
 37        IServerConfigurationManager serverConfigurationManager,
 38        IWebHostEnvironment hostEnvironment)
 39    {
 2240        _next = next;
 2241        _logger = logger;
 2242        _configuration = serverConfigurationManager;
 2243        _hostEnvironment = hostEnvironment;
 2244    }
 45
 46    /// <summary>
 47    /// Invoke request.
 48    /// </summary>
 49    /// <param name="context">Request context.</param>
 50    /// <returns>Task.</returns>
 51    public async Task Invoke(HttpContext context)
 52    {
 53        try
 54        {
 55            await _next(context).ConfigureAwait(false);
 56        }
 57        catch (Exception ex)
 58        {
 59            if (context.Response.HasStarted)
 60            {
 61                _logger.LogWarning("The response has already started, the exception middleware will not be executed.");
 62                throw;
 63            }
 64
 65            ex = GetActualException(ex);
 66
 67            bool ignoreStackTrace =
 68                ex is SocketException
 69                || ex is IOException
 70                || ex is OperationCanceledException
 71                || ex is SecurityException
 72                || ex is AuthenticationException
 73                || ex is FileNotFoundException;
 74
 75            if (ignoreStackTrace)
 76            {
 77                _logger.LogError(
 78                    "Error processing request: {ExceptionMessage}. URL {Method} {Url}.",
 79                    ex.Message.TrimEnd('.'),
 80                    context.Request.Method,
 81                    context.Request.Path);
 82            }
 83            else
 84            {
 85                _logger.LogError(
 86                    ex,
 87                    "Error processing request. URL {Method} {Url}.",
 88                    context.Request.Method,
 89                    context.Request.Path);
 90            }
 91
 92            context.Response.StatusCode = GetStatusCode(ex);
 93            context.Response.ContentType = MediaTypeNames.Text.Plain;
 94
 95            // Don't send exception unless the server is in a Development environment
 96            var errorContent = _hostEnvironment.IsDevelopment()
 97                    ? NormalizeExceptionMessage(ex.Message)
 98                    : "Error processing request.";
 99            await context.Response.WriteAsync(errorContent).ConfigureAwait(false);
 100        }
 101    }
 102
 103    private static Exception GetActualException(Exception ex)
 104    {
 10105        if (ex is AggregateException agg)
 106        {
 0107            var inner = agg.InnerException;
 0108            if (inner is not null)
 109            {
 0110                return GetActualException(inner);
 111            }
 112
 0113            var inners = agg.InnerExceptions;
 0114            if (inners.Count > 0)
 115            {
 0116                return GetActualException(inners[0]);
 117            }
 118        }
 119
 10120        return ex;
 121    }
 122
 123    private static int GetStatusCode(Exception ex)
 124    {
 10125        return ex switch
 10126        {
 3127            ArgumentException => StatusCodes.Status400BadRequest,
 0128            AuthenticationException => StatusCodes.Status401Unauthorized,
 1129            SecurityException => StatusCodes.Status403Forbidden,
 0130            DirectoryNotFoundException => StatusCodes.Status404NotFound,
 4131            FileNotFoundException => StatusCodes.Status404NotFound,
 2132            ResourceNotFoundException => StatusCodes.Status404NotFound,
 0133            MethodNotAllowedException => StatusCodes.Status405MethodNotAllowed,
 0134            _ => StatusCodes.Status500InternalServerError
 10135        };
 136    }
 137
 138    private string NormalizeExceptionMessage(string msg)
 139    {
 140        // Strip any information we don't want to reveal
 0141        return msg.Replace(
 0142                _configuration.ApplicationPaths.ProgramSystemPath,
 0143                string.Empty,
 0144                StringComparison.OrdinalIgnoreCase)
 0145            .Replace(
 0146                _configuration.ApplicationPaths.ProgramDataPath,
 0147                string.Empty,
 0148                StringComparison.OrdinalIgnoreCase);
 149    }
 150}