ActionDispatch::Executor: report errors handled by ShowExceptions

Fix: https://github.com/rails/rails/issues/51002

In the default middleware stack, the `ShowExceptions` middleware is
lower than `ActionDispatch::Execturor` and will handle most exceptions
causing `Executor` not to witness any.

Instead we need to rely on `action_dispatch.exception` being added
into the request env.
This commit is contained in:
Jean Boussier 2024-02-12 16:41:17 +01:00
parent 9940dc879d
commit 4067c9565a
3 changed files with 34 additions and 2 deletions

View File

@ -14,6 +14,9 @@ module ActionDispatch
state = @executor.run!(reset: true)
begin
response = @app.call(env)
if rendered_error = env["action_dispatch.exception"]
@executor.error_reporter.report(rendered_error, handled: false, source: "application.action_dispatch")
end
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
rescue => error
@executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")

View File

@ -34,8 +34,10 @@ module ActionDispatch
request = ActionDispatch::Request.new env
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
request.set_header "action_dispatch.exception", wrapper.unwrapped_exception
if wrapper.show?(request)
render_exception(request, wrapper)
render_exception(request.dup, wrapper)
else
raise exception
end
@ -44,7 +46,6 @@ module ActionDispatch
private
def render_exception(request, wrapper)
status = wrapper.status_code
request.set_header "action_dispatch.exception", wrapper.unwrapped_exception
request.set_header "action_dispatch.original_path", request.path_info
request.set_header "action_dispatch.original_request_method", request.raw_request_method
fallback_to_html_format_if_invalid_mime_type(request)

View File

@ -119,6 +119,34 @@ class ExecutorTest < ActiveSupport::TestCase
assert_equal requests_count - 1, completed
end
def test_error_reporting
raised_error = nil
error_report = assert_error_reported do
raised_error = assert_raises TypeError do
call_and_return_body { 1 + "1" }
end
end
assert_same raised_error, error_report.error
end
def test_error_reporting_with_show_exception
middleware = Rack::Lint.new(
ActionDispatch::Executor.new(
ActionDispatch::ShowExceptions.new(
Rack::Lint.new(->(_env) { 1 + "1" }),
->(_env) { [500, {}, ["Oops"]] },
),
executor,
)
)
env = Rack::MockRequest.env_for("", {})
error_report = assert_error_reported do
middleware.call(env)
end
assert_instance_of TypeError, error_report.error
end
private
def call_and_return_body(&block)
app = block || proc { [200, {}, []] }