From 2a434d0458c29792d42b3b818718a2579b10bc64 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Wed, 25 Aug 2021 12:48:31 -0500 Subject: [PATCH] Fix crash in `ActionController::Instrumentation` with invalid HTTP formats Fixes https://github.com/rails/rails/issues/43094 --- .../middleware/show_exceptions.rb | 10 +++++++ .../application/middleware/exceptions_test.rb | 29 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 728c1ae68e..18e3002f86 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -40,6 +40,7 @@ module ActionDispatch 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) request.path_info = "/#{status}" request.request_method = "GET" response = @exceptions_app.call(request.env) @@ -54,6 +55,15 @@ module ActionDispatch "went wrong."]] end + def fallback_to_html_format_if_invalid_mime_type(request) + # If the MIME type for the request is invalid then the + # @exceptions_app may not be able to handle it. To make it + # easier to handle, we switch to HTML. + request.formats + rescue ActionDispatch::Http::MimeNegotiation::InvalidType + request.set_header "HTTP_ACCEPT", "text/html" + end + def pass_response(status) [status, { "Content-Type" => "text/html; charset=#{Response.default_charset}", "Content-Length" => "0" }, []] end diff --git a/railties/test/application/middleware/exceptions_test.rb b/railties/test/application/middleware/exceptions_test.rb index 876646fefa..b638cdf5be 100644 --- a/railties/test/application/middleware/exceptions_test.rb +++ b/railties/test/application/middleware/exceptions_test.rb @@ -65,6 +65,35 @@ module ApplicationTests assert_equal 405, last_response.status end + test "renders unknown http formats as 406 when routes are used as the custom exceptions app" do + controller :foo, <<-RUBY + class FooController < ActionController::Base + def index + render plain: "rendering index!" + end + + def not_acceptable + render json: { error: "some error message" }, status: :not_acceptable + end + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + get "/foo", to: "foo#index" + match "/406", to: "foo#not_acceptable", via: :all + end + RUBY + + add_to_config "config.exceptions_app = self.routes" + add_to_config "config.action_dispatch.show_exceptions = true" + add_to_config "config.consider_all_requests_local = false" + + get "/foo", {}, { "HTTP_ACCEPT" => "invalid" } + assert_equal 406, last_response.status + assert_not_equal "rendering index!", last_response.body + end + test "uses custom exceptions app" do add_to_config <<-RUBY config.exceptions_app = lambda do |env|