ExceptionMiddleware.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. using System;
  2. using System.IO;
  3. using System.Net.Mime;
  4. using System.Net.Sockets;
  5. using System.Threading.Tasks;
  6. using MediaBrowser.Common.Extensions;
  7. using MediaBrowser.Controller.Authentication;
  8. using MediaBrowser.Controller.Configuration;
  9. using MediaBrowser.Controller.Net;
  10. using Microsoft.AspNetCore.Http;
  11. using Microsoft.Extensions.Logging;
  12. namespace Jellyfin.Server.Middleware
  13. {
  14. /// <summary>
  15. /// Exception Middleware.
  16. /// </summary>
  17. public class ExceptionMiddleware
  18. {
  19. private readonly RequestDelegate _next;
  20. private readonly ILogger<ExceptionMiddleware> _logger;
  21. private readonly IServerConfigurationManager _configuration;
  22. /// <summary>
  23. /// Initializes a new instance of the <see cref="ExceptionMiddleware"/> class.
  24. /// </summary>
  25. /// <param name="next">Next request delegate.</param>
  26. /// <param name="logger">Instance of the <see cref="ILogger{ExceptionMiddleware}"/> interface.</param>
  27. /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface.</param>
  28. public ExceptionMiddleware(
  29. RequestDelegate next,
  30. ILogger<ExceptionMiddleware> logger,
  31. IServerConfigurationManager serverConfigurationManager)
  32. {
  33. _next = next;
  34. _logger = logger;
  35. _configuration = serverConfigurationManager;
  36. }
  37. /// <summary>
  38. /// Invoke request.
  39. /// </summary>
  40. /// <param name="context">Request context.</param>
  41. /// <returns>Task.</returns>
  42. public async Task Invoke(HttpContext context)
  43. {
  44. try
  45. {
  46. await _next(context).ConfigureAwait(false);
  47. }
  48. catch (Exception ex)
  49. {
  50. if (context.Response.HasStarted)
  51. {
  52. _logger.LogWarning("The response has already started, the exception middleware will not be executed.");
  53. throw;
  54. }
  55. ex = GetActualException(ex);
  56. bool ignoreStackTrace =
  57. ex is SocketException
  58. || ex is IOException
  59. || ex is OperationCanceledException
  60. || ex is SecurityException
  61. || ex is AuthenticationException
  62. || ex is FileNotFoundException;
  63. if (ignoreStackTrace)
  64. {
  65. _logger.LogError(
  66. "Error processing request: {ExceptionMessage}. URL {Method} {Url}.",
  67. ex.Message.TrimEnd('.'),
  68. context.Request.Method,
  69. context.Request.Path);
  70. }
  71. else
  72. {
  73. _logger.LogError(
  74. ex,
  75. "Error processing request. URL {Method} {Url}.",
  76. context.Request.Method,
  77. context.Request.Path);
  78. }
  79. context.Response.StatusCode = GetStatusCode(ex);
  80. context.Response.ContentType = MediaTypeNames.Text.Plain;
  81. var errorContent = NormalizeExceptionMessage(ex.Message);
  82. await context.Response.WriteAsync(errorContent).ConfigureAwait(false);
  83. }
  84. }
  85. private static Exception GetActualException(Exception ex)
  86. {
  87. if (ex is AggregateException agg)
  88. {
  89. var inner = agg.InnerException;
  90. if (inner != null)
  91. {
  92. return GetActualException(inner);
  93. }
  94. var inners = agg.InnerExceptions;
  95. if (inners.Count > 0)
  96. {
  97. return GetActualException(inners[0]);
  98. }
  99. }
  100. return ex;
  101. }
  102. private static int GetStatusCode(Exception ex)
  103. {
  104. switch (ex)
  105. {
  106. case ArgumentException _: return StatusCodes.Status400BadRequest;
  107. case SecurityException _: return StatusCodes.Status401Unauthorized;
  108. case DirectoryNotFoundException _:
  109. case FileNotFoundException _:
  110. case ResourceNotFoundException _: return StatusCodes.Status404NotFound;
  111. case MethodNotAllowedException _: return StatusCodes.Status405MethodNotAllowed;
  112. default: return StatusCodes.Status500InternalServerError;
  113. }
  114. }
  115. private string NormalizeExceptionMessage(string msg)
  116. {
  117. if (msg == null)
  118. {
  119. return string.Empty;
  120. }
  121. // Strip any information we don't want to reveal
  122. return msg.Replace(
  123. _configuration.ApplicationPaths.ProgramSystemPath,
  124. string.Empty,
  125. StringComparison.OrdinalIgnoreCase)
  126. .Replace(
  127. _configuration.ApplicationPaths.ProgramDataPath,
  128. string.Empty,
  129. StringComparison.OrdinalIgnoreCase);
  130. }
  131. }
  132. }