actionpack 6.0.3.2 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +258 -223
 - data/MIT-LICENSE +1 -1
 - data/lib/abstract_controller.rb +1 -0
 - data/lib/abstract_controller/base.rb +35 -2
 - data/lib/abstract_controller/callbacks.rb +2 -2
 - data/lib/abstract_controller/helpers.rb +105 -90
 - data/lib/abstract_controller/rendering.rb +9 -9
 - data/lib/abstract_controller/translation.rb +8 -2
 - data/lib/action_controller.rb +2 -3
 - data/lib/action_controller/api.rb +2 -2
 - data/lib/action_controller/base.rb +4 -2
 - data/lib/action_controller/caching.rb +0 -1
 - data/lib/action_controller/log_subscriber.rb +3 -3
 - data/lib/action_controller/metal.rb +2 -2
 - data/lib/action_controller/metal/conditional_get.rb +10 -2
 - data/lib/action_controller/metal/content_security_policy.rb +1 -1
 - data/lib/action_controller/metal/cookies.rb +3 -1
 - data/lib/action_controller/metal/data_streaming.rb +1 -1
 - data/lib/action_controller/metal/etag_with_template_digest.rb +2 -4
 - data/lib/action_controller/metal/exceptions.rb +33 -0
 - data/lib/action_controller/metal/head.rb +7 -4
 - data/lib/action_controller/metal/helpers.rb +11 -1
 - data/lib/action_controller/metal/http_authentication.rb +4 -2
 - data/lib/action_controller/metal/implicit_render.rb +1 -1
 - data/lib/action_controller/metal/instrumentation.rb +11 -9
 - data/lib/action_controller/metal/live.rb +1 -1
 - data/lib/action_controller/metal/logging.rb +20 -0
 - data/lib/action_controller/metal/mime_responds.rb +6 -2
 - data/lib/action_controller/metal/parameter_encoding.rb +35 -4
 - data/lib/action_controller/metal/params_wrapper.rb +14 -8
 - data/lib/action_controller/metal/permissions_policy.rb +46 -0
 - data/lib/action_controller/metal/redirecting.rb +1 -1
 - data/lib/action_controller/metal/rendering.rb +6 -0
 - data/lib/action_controller/metal/request_forgery_protection.rb +48 -24
 - data/lib/action_controller/metal/rescue.rb +1 -1
 - data/lib/action_controller/metal/strong_parameters.rb +103 -15
 - data/lib/action_controller/renderer.rb +24 -13
 - data/lib/action_controller/test_case.rb +62 -56
 - data/lib/action_dispatch.rb +3 -2
 - data/lib/action_dispatch/http/cache.rb +12 -10
 - data/lib/action_dispatch/http/content_disposition.rb +2 -2
 - data/lib/action_dispatch/http/content_security_policy.rb +5 -1
 - data/lib/action_dispatch/http/filter_parameters.rb +1 -1
 - data/lib/action_dispatch/http/filter_redirect.rb +1 -1
 - data/lib/action_dispatch/http/headers.rb +3 -2
 - data/lib/action_dispatch/http/mime_negotiation.rb +20 -8
 - data/lib/action_dispatch/http/mime_type.rb +28 -15
 - data/lib/action_dispatch/http/parameters.rb +1 -19
 - data/lib/action_dispatch/http/permissions_policy.rb +173 -0
 - data/lib/action_dispatch/http/request.rb +26 -8
 - data/lib/action_dispatch/http/response.rb +17 -16
 - data/lib/action_dispatch/http/url.rb +3 -2
 - data/lib/action_dispatch/journey.rb +0 -2
 - data/lib/action_dispatch/journey/formatter.rb +53 -28
 - data/lib/action_dispatch/journey/gtg/builder.rb +22 -36
 - data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
 - data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -4
 - data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
 - data/lib/action_dispatch/journey/nodes/node.rb +4 -3
 - data/lib/action_dispatch/journey/parser.rb +13 -13
 - data/lib/action_dispatch/journey/parser.y +1 -1
 - data/lib/action_dispatch/journey/path/pattern.rb +13 -18
 - data/lib/action_dispatch/journey/route.rb +7 -18
 - data/lib/action_dispatch/journey/router.rb +26 -30
 - data/lib/action_dispatch/journey/router/utils.rb +6 -4
 - data/lib/action_dispatch/middleware/actionable_exceptions.rb +9 -2
 - data/lib/action_dispatch/middleware/cookies.rb +74 -33
 - data/lib/action_dispatch/middleware/debug_exceptions.rb +10 -17
 - data/lib/action_dispatch/middleware/debug_view.rb +1 -1
 - data/lib/action_dispatch/middleware/exception_wrapper.rb +29 -17
 - data/lib/action_dispatch/middleware/host_authorization.rb +25 -5
 - data/lib/action_dispatch/middleware/public_exceptions.rb +1 -1
 - data/lib/action_dispatch/middleware/remote_ip.rb +5 -4
 - data/lib/action_dispatch/middleware/request_id.rb +4 -5
 - data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -2
 - data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
 - data/lib/action_dispatch/middleware/ssl.rb +9 -6
 - data/lib/action_dispatch/middleware/stack.rb +18 -0
 - data/lib/action_dispatch/middleware/static.rb +154 -93
 - data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
 - data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +2 -5
 - data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +2 -2
 - data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -2
 - data/lib/action_dispatch/middleware/templates/rescues/layout.erb +100 -8
 - data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
 - data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +12 -1
 - data/lib/action_dispatch/railtie.rb +3 -2
 - data/lib/action_dispatch/request/session.rb +2 -8
 - data/lib/action_dispatch/request/utils.rb +26 -2
 - data/lib/action_dispatch/routing/inspector.rb +8 -7
 - data/lib/action_dispatch/routing/mapper.rb +102 -71
 - data/lib/action_dispatch/routing/polymorphic_routes.rb +12 -11
 - data/lib/action_dispatch/routing/redirection.rb +3 -3
 - data/lib/action_dispatch/routing/route_set.rb +49 -41
 - data/lib/action_dispatch/routing/url_for.rb +1 -0
 - data/lib/action_dispatch/system_test_case.rb +29 -24
 - data/lib/action_dispatch/system_testing/browser.rb +33 -27
 - data/lib/action_dispatch/system_testing/driver.rb +6 -7
 - data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +47 -6
 - data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +4 -7
 - data/lib/action_dispatch/testing/assertions.rb +1 -1
 - data/lib/action_dispatch/testing/assertions/response.rb +2 -4
 - data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
 - data/lib/action_dispatch/testing/integration.rb +38 -27
 - data/lib/action_dispatch/testing/test_process.rb +29 -4
 - data/lib/action_dispatch/testing/test_request.rb +3 -3
 - data/lib/action_pack.rb +1 -1
 - data/lib/action_pack/gem_version.rb +3 -3
 - metadata +21 -22
 - data/lib/action_controller/metal/force_ssl.rb +0 -58
 - data/lib/action_dispatch/http/parameter_filter.rb +0 -12
 - data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
 - data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
 - data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -119
 
| 
         @@ -19,11 +19,13 @@ module ActionDispatch 
     | 
|
| 
       19 
19 
     | 
    
         
             
                      encoding = path.encoding
         
     | 
| 
       20 
20 
     | 
    
         
             
                      path = +"/#{path}"
         
     | 
| 
       21 
21 
     | 
    
         
             
                      path.squeeze!("/")
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                      path 
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                      unless path == "/"
         
     | 
| 
      
 24 
     | 
    
         
            +
                        path.delete_suffix!("/")
         
     | 
| 
      
 25 
     | 
    
         
            +
                        path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
         
     | 
| 
      
 26 
     | 
    
         
            +
                      end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       25 
28 
     | 
    
         
             
                      path.force_encoding(encoding)
         
     | 
| 
       26 
     | 
    
         
            -
                      path
         
     | 
| 
       27 
29 
     | 
    
         
             
                    end
         
     | 
| 
       28 
30 
     | 
    
         | 
| 
       29 
31 
     | 
    
         
             
                    # URI path and fragment escaping
         
     | 
| 
         @@ -1,6 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require "erb"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "uri"
         
     | 
| 
       4 
5 
     | 
    
         
             
            require "action_dispatch/http/request"
         
     | 
| 
       5 
6 
     | 
    
         
             
            require "active_support/actionable_error"
         
     | 
| 
       6 
7 
     | 
    
         | 
| 
         @@ -23,11 +24,17 @@ module ActionDispatch 
     | 
|
| 
       23 
24 
     | 
    
         | 
| 
       24 
25 
     | 
    
         
             
                private
         
     | 
| 
       25 
26 
     | 
    
         
             
                  def actionable_request?(request)
         
     | 
| 
       26 
     | 
    
         
            -
                    request.get_header("action_dispatch.show_detailed_exceptions") 
     | 
| 
      
 27 
     | 
    
         
            +
                    request.get_header("action_dispatch.show_detailed_exceptions") && request.post? && request.path == endpoint
         
     | 
| 
       27 
28 
     | 
    
         
             
                  end
         
     | 
| 
       28 
29 
     | 
    
         | 
| 
       29 
30 
     | 
    
         
             
                  def redirect_to(location)
         
     | 
| 
       30 
     | 
    
         
            -
                     
     | 
| 
      
 31 
     | 
    
         
            +
                    uri = URI.parse location
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    if uri.relative? || uri.scheme == "http" || uri.scheme == "https"
         
     | 
| 
      
 34 
     | 
    
         
            +
                      body = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(location)}\">redirected</a>.</body></html>"
         
     | 
| 
      
 35 
     | 
    
         
            +
                    else
         
     | 
| 
      
 36 
     | 
    
         
            +
                      return [400, { "Content-Type" => "text/plain" }, ["Invalid redirection URI"]]
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
       31 
38 
     | 
    
         | 
| 
       32 
39 
     | 
    
         
             
                    [302, {
         
     | 
| 
       33 
40 
     | 
    
         
             
                      "Content-Type" => "text/html; charset=#{Response.default_charset}",
         
     | 
| 
         @@ -69,6 +69,10 @@ module ActionDispatch 
     | 
|
| 
       69 
69 
     | 
    
         
             
                  get_header Cookies::COOKIES_SERIALIZER
         
     | 
| 
       70 
70 
     | 
    
         
             
                end
         
     | 
| 
       71 
71 
     | 
    
         | 
| 
      
 72 
     | 
    
         
            +
                def cookies_same_site_protection
         
     | 
| 
      
 73 
     | 
    
         
            +
                  get_header(Cookies::COOKIES_SAME_SITE_PROTECTION) || Proc.new { }
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
       72 
76 
     | 
    
         
             
                def cookies_digest
         
     | 
| 
       73 
77 
     | 
    
         
             
                  get_header Cookies::COOKIES_DIGEST
         
     | 
| 
       74 
78 
     | 
    
         
             
                end
         
     | 
| 
         @@ -84,11 +88,10 @@ module ActionDispatch 
     | 
|
| 
       84 
88 
     | 
    
         
             
                # :startdoc:
         
     | 
| 
       85 
89 
     | 
    
         
             
              end
         
     | 
| 
       86 
90 
     | 
    
         | 
| 
       87 
     | 
    
         
            -
              #  
     | 
| 
      
 91 
     | 
    
         
            +
              # Read and write data to cookies through ActionController#cookies.
         
     | 
| 
       88 
92 
     | 
    
         
             
              #
         
     | 
| 
       89 
     | 
    
         
            -
              #  
     | 
| 
       90 
     | 
    
         
            -
              #  
     | 
| 
       91 
     | 
    
         
            -
              # the cookie object itself back, just the value it holds.
         
     | 
| 
      
 93 
     | 
    
         
            +
              # When reading cookie data, the data is read from the HTTP request header, Cookie.
         
     | 
| 
      
 94 
     | 
    
         
            +
              # When writing cookie data, the data is sent out in the HTTP response header, Set-Cookie.
         
     | 
| 
       92 
95 
     | 
    
         
             
              #
         
     | 
| 
       93 
96 
     | 
    
         
             
              # Examples of writing:
         
     | 
| 
       94 
97 
     | 
    
         
             
              #
         
     | 
| 
         @@ -150,8 +153,10 @@ module ActionDispatch 
     | 
|
| 
       150 
153 
     | 
    
         
             
              # * <tt>:domain</tt> - The domain for which this cookie applies so you can
         
     | 
| 
       151 
154 
     | 
    
         
             
              #   restrict to the domain level. If you use a schema like www.example.com
         
     | 
| 
       152 
155 
     | 
    
         
             
              #   and want to share session with user.example.com set <tt>:domain</tt>
         
     | 
| 
       153 
     | 
    
         
            -
              #   to <tt>:all</tt>.  
     | 
| 
       154 
     | 
    
         
            -
              #    
     | 
| 
      
 156 
     | 
    
         
            +
              #   to <tt>:all</tt>. To support multiple domains, provide an array, and
         
     | 
| 
      
 157 
     | 
    
         
            +
              #   the first domain matching <tt>request.host</tt> will be used. Make
         
     | 
| 
      
 158 
     | 
    
         
            +
              #   sure to specify the <tt>:domain</tt> option with <tt>:all</tt> or
         
     | 
| 
      
 159 
     | 
    
         
            +
              #   <tt>Array</tt> again when deleting cookies.
         
     | 
| 
       155 
160 
     | 
    
         
             
              #
         
     | 
| 
       156 
161 
     | 
    
         
             
              #     domain: nil  # Does not set cookie domain. (default)
         
     | 
| 
       157 
162 
     | 
    
         
             
              #     domain: :all # Allow the cookie for the top most level
         
     | 
| 
         @@ -181,6 +186,7 @@ module ActionDispatch 
     | 
|
| 
       181 
186 
     | 
    
         
             
                COOKIES_SERIALIZER = "action_dispatch.cookies_serializer"
         
     | 
| 
       182 
187 
     | 
    
         
             
                COOKIES_DIGEST = "action_dispatch.cookies_digest"
         
     | 
| 
       183 
188 
     | 
    
         
             
                COOKIES_ROTATIONS = "action_dispatch.cookies_rotations"
         
     | 
| 
      
 189 
     | 
    
         
            +
                COOKIES_SAME_SITE_PROTECTION = "action_dispatch.cookies_same_site_protection"
         
     | 
| 
       184 
190 
     | 
    
         
             
                USE_COOKIES_WITH_METADATA = "action_dispatch.use_cookies_with_metadata"
         
     | 
| 
       185 
191 
     | 
    
         | 
| 
       186 
192 
     | 
    
         
             
                # Cookies can typically store 4096 bytes.
         
     | 
| 
         @@ -259,6 +265,12 @@ module ActionDispatch 
     | 
|
| 
       259 
265 
     | 
    
         
             
                        request.use_authenticated_cookie_encryption
         
     | 
| 
       260 
266 
     | 
    
         
             
                    end
         
     | 
| 
       261 
267 
     | 
    
         | 
| 
      
 268 
     | 
    
         
            +
                    def prepare_upgrade_legacy_hmac_aes_cbc_cookies?
         
     | 
| 
      
 269 
     | 
    
         
            +
                      request.secret_key_base.present? &&
         
     | 
| 
      
 270 
     | 
    
         
            +
                        request.authenticated_encrypted_cookie_salt.present? &&
         
     | 
| 
      
 271 
     | 
    
         
            +
                        !request.use_authenticated_cookie_encryption
         
     | 
| 
      
 272 
     | 
    
         
            +
                    end
         
     | 
| 
      
 273 
     | 
    
         
            +
             
     | 
| 
       262 
274 
     | 
    
         
             
                    def encrypted_cookie_cipher
         
     | 
| 
       263 
275 
     | 
    
         
             
                      request.encrypted_cookie_cipher || "aes-256-gcm"
         
     | 
| 
       264 
276 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -286,9 +298,9 @@ module ActionDispatch 
     | 
|
| 
       286 
298 
     | 
    
         
             
                  DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
         
     | 
| 
       287 
299 
     | 
    
         | 
| 
       288 
300 
     | 
    
         
             
                  def self.build(req, cookies)
         
     | 
| 
       289 
     | 
    
         
            -
                    new(req) 
     | 
| 
       290 
     | 
    
         
            -
             
     | 
| 
       291 
     | 
    
         
            -
                     
     | 
| 
      
 301 
     | 
    
         
            +
                    jar = new(req)
         
     | 
| 
      
 302 
     | 
    
         
            +
                    jar.update(cookies)
         
     | 
| 
      
 303 
     | 
    
         
            +
                    jar
         
     | 
| 
       292 
304 
     | 
    
         
             
                  end
         
     | 
| 
       293 
305 
     | 
    
         | 
| 
       294 
306 
     | 
    
         
             
                  attr_reader :request
         
     | 
| 
         @@ -346,28 +358,6 @@ module ActionDispatch 
     | 
|
| 
       346 
358 
     | 
    
         
             
                    @cookies.map { |k, v| "#{escape(k)}=#{escape(v)}" }.join "; "
         
     | 
| 
       347 
359 
     | 
    
         
             
                  end
         
     | 
| 
       348 
360 
     | 
    
         | 
| 
       349 
     | 
    
         
            -
                  def handle_options(options) # :nodoc:
         
     | 
| 
       350 
     | 
    
         
            -
                    if options[:expires].respond_to?(:from_now)
         
     | 
| 
       351 
     | 
    
         
            -
                      options[:expires] = options[:expires].from_now
         
     | 
| 
       352 
     | 
    
         
            -
                    end
         
     | 
| 
       353 
     | 
    
         
            -
             
     | 
| 
       354 
     | 
    
         
            -
                    options[:path] ||= "/"
         
     | 
| 
       355 
     | 
    
         
            -
             
     | 
| 
       356 
     | 
    
         
            -
                    if options[:domain] == :all || options[:domain] == "all"
         
     | 
| 
       357 
     | 
    
         
            -
                      # If there is a provided tld length then we use it otherwise default domain regexp.
         
     | 
| 
       358 
     | 
    
         
            -
                      domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
         
     | 
| 
       359 
     | 
    
         
            -
             
     | 
| 
       360 
     | 
    
         
            -
                      # If host is not ip and matches domain regexp.
         
     | 
| 
       361 
     | 
    
         
            -
                      # (ip confirms to domain regexp so we explicitly check for ip)
         
     | 
| 
       362 
     | 
    
         
            -
                      options[:domain] = if (request.host !~ /^[\d.]+$/) && (request.host =~ domain_regexp)
         
     | 
| 
       363 
     | 
    
         
            -
                        ".#{$&}"
         
     | 
| 
       364 
     | 
    
         
            -
                      end
         
     | 
| 
       365 
     | 
    
         
            -
                    elsif options[:domain].is_a? Array
         
     | 
| 
       366 
     | 
    
         
            -
                      # If host matches one of the supplied domains without a dot in front of it.
         
     | 
| 
       367 
     | 
    
         
            -
                      options[:domain] = options[:domain].find { |domain| request.host.include? domain.sub(/^\./, "") }
         
     | 
| 
       368 
     | 
    
         
            -
                    end
         
     | 
| 
       369 
     | 
    
         
            -
                  end
         
     | 
| 
       370 
     | 
    
         
            -
             
     | 
| 
       371 
361 
     | 
    
         
             
                  # Sets the cookie named +name+. The second argument may be the cookie's
         
     | 
| 
       372 
362 
     | 
    
         
             
                  # value or a hash of options as documented above.
         
     | 
| 
       373 
363 
     | 
    
         
             
                  def []=(name, options)
         
     | 
| 
         @@ -447,6 +437,34 @@ module ActionDispatch 
     | 
|
| 
       447 
437 
     | 
    
         
             
                    def write_cookie?(cookie)
         
     | 
| 
       448 
438 
     | 
    
         
             
                      request.ssl? || !cookie[:secure] || always_write_cookie
         
     | 
| 
       449 
439 
     | 
    
         
             
                    end
         
     | 
| 
      
 440 
     | 
    
         
            +
             
     | 
| 
      
 441 
     | 
    
         
            +
                    def handle_options(options)
         
     | 
| 
      
 442 
     | 
    
         
            +
                      if options[:expires].respond_to?(:from_now)
         
     | 
| 
      
 443 
     | 
    
         
            +
                        options[:expires] = options[:expires].from_now
         
     | 
| 
      
 444 
     | 
    
         
            +
                      end
         
     | 
| 
      
 445 
     | 
    
         
            +
             
     | 
| 
      
 446 
     | 
    
         
            +
                      options[:path]      ||= "/"
         
     | 
| 
      
 447 
     | 
    
         
            +
             
     | 
| 
      
 448 
     | 
    
         
            +
                      cookies_same_site_protection = request.cookies_same_site_protection
         
     | 
| 
      
 449 
     | 
    
         
            +
                      options[:same_site] ||= cookies_same_site_protection.call(request)
         
     | 
| 
      
 450 
     | 
    
         
            +
             
     | 
| 
      
 451 
     | 
    
         
            +
                      if options[:domain] == :all || options[:domain] == "all"
         
     | 
| 
      
 452 
     | 
    
         
            +
                        # If there is a provided tld length then we use it otherwise default domain regexp.
         
     | 
| 
      
 453 
     | 
    
         
            +
                        domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
         
     | 
| 
      
 454 
     | 
    
         
            +
             
     | 
| 
      
 455 
     | 
    
         
            +
                        # If host is not ip and matches domain regexp.
         
     | 
| 
      
 456 
     | 
    
         
            +
                        # (ip confirms to domain regexp so we explicitly check for ip)
         
     | 
| 
      
 457 
     | 
    
         
            +
                        options[:domain] = if !request.host.match?(/^[\d.]+$/) && (request.host =~ domain_regexp)
         
     | 
| 
      
 458 
     | 
    
         
            +
                          ".#{$&}"
         
     | 
| 
      
 459 
     | 
    
         
            +
                        end
         
     | 
| 
      
 460 
     | 
    
         
            +
                      elsif options[:domain].is_a? Array
         
     | 
| 
      
 461 
     | 
    
         
            +
                        # If host matches one of the supplied domains.
         
     | 
| 
      
 462 
     | 
    
         
            +
                        options[:domain] = options[:domain].find do |domain|
         
     | 
| 
      
 463 
     | 
    
         
            +
                          domain = domain.delete_prefix(".")
         
     | 
| 
      
 464 
     | 
    
         
            +
                          request.host == domain || request.host.end_with?(".#{domain}")
         
     | 
| 
      
 465 
     | 
    
         
            +
                        end
         
     | 
| 
      
 466 
     | 
    
         
            +
                      end
         
     | 
| 
      
 467 
     | 
    
         
            +
                    end
         
     | 
| 
       450 
468 
     | 
    
         
             
                end
         
     | 
| 
       451 
469 
     | 
    
         | 
| 
       452 
470 
     | 
    
         
             
                class AbstractCookieJar # :nodoc:
         
     | 
| 
         @@ -458,7 +476,13 @@ module ActionDispatch 
     | 
|
| 
       458 
476 
     | 
    
         | 
| 
       459 
477 
     | 
    
         
             
                  def [](name)
         
     | 
| 
       460 
478 
     | 
    
         
             
                    if data = @parent_jar[name.to_s]
         
     | 
| 
       461 
     | 
    
         
            -
                      parse(name, data, purpose: "cookie.#{name}") 
     | 
| 
      
 479 
     | 
    
         
            +
                      result = parse(name, data, purpose: "cookie.#{name}")
         
     | 
| 
      
 480 
     | 
    
         
            +
             
     | 
| 
      
 481 
     | 
    
         
            +
                      if result.nil?
         
     | 
| 
      
 482 
     | 
    
         
            +
                        parse(name, data)
         
     | 
| 
      
 483 
     | 
    
         
            +
                      else
         
     | 
| 
      
 484 
     | 
    
         
            +
                        result
         
     | 
| 
      
 485 
     | 
    
         
            +
                      end
         
     | 
| 
       462 
486 
     | 
    
         
             
                    end
         
     | 
| 
       463 
487 
     | 
    
         
             
                  end
         
     | 
| 
       464 
488 
     | 
    
         | 
| 
         @@ -502,6 +526,18 @@ module ActionDispatch 
     | 
|
| 
       502 
526 
     | 
    
         
             
                    end
         
     | 
| 
       503 
527 
     | 
    
         
             
                end
         
     | 
| 
       504 
528 
     | 
    
         | 
| 
      
 529 
     | 
    
         
            +
                class MarshalWithJsonFallback # :nodoc:
         
     | 
| 
      
 530 
     | 
    
         
            +
                  def self.load(value)
         
     | 
| 
      
 531 
     | 
    
         
            +
                    Marshal.load(value)
         
     | 
| 
      
 532 
     | 
    
         
            +
                  rescue TypeError => e
         
     | 
| 
      
 533 
     | 
    
         
            +
                    ActiveSupport::JSON.decode(value) rescue raise e
         
     | 
| 
      
 534 
     | 
    
         
            +
                  end
         
     | 
| 
      
 535 
     | 
    
         
            +
             
     | 
| 
      
 536 
     | 
    
         
            +
                  def self.dump(value)
         
     | 
| 
      
 537 
     | 
    
         
            +
                    Marshal.dump(value)
         
     | 
| 
      
 538 
     | 
    
         
            +
                  end
         
     | 
| 
      
 539 
     | 
    
         
            +
                end
         
     | 
| 
      
 540 
     | 
    
         
            +
             
     | 
| 
       505 
541 
     | 
    
         
             
                class JsonSerializer # :nodoc:
         
     | 
| 
       506 
542 
     | 
    
         
             
                  def self.load(value)
         
     | 
| 
       507 
543 
     | 
    
         
             
                    ActiveSupport::JSON.decode(value)
         
     | 
| 
         @@ -549,7 +585,7 @@ module ActionDispatch 
     | 
|
| 
       549 
585 
     | 
    
         
             
                      serializer = request.cookies_serializer || :marshal
         
     | 
| 
       550 
586 
     | 
    
         
             
                      case serializer
         
     | 
| 
       551 
587 
     | 
    
         
             
                      when :marshal
         
     | 
| 
       552 
     | 
    
         
            -
                         
     | 
| 
      
 588 
     | 
    
         
            +
                        MarshalWithJsonFallback
         
     | 
| 
       553 
589 
     | 
    
         
             
                      when :json, :hybrid
         
     | 
| 
       554 
590 
     | 
    
         
             
                        JsonSerializer
         
     | 
| 
       555 
591 
     | 
    
         
             
                      else
         
     | 
| 
         @@ -619,6 +655,11 @@ module ActionDispatch 
     | 
|
| 
       619 
655 
     | 
    
         
             
                      sign_secret = request.key_generator.generate_key(request.encrypted_signed_cookie_salt)
         
     | 
| 
       620 
656 
     | 
    
         | 
| 
       621 
657 
     | 
    
         
             
                      @encryptor.rotate(secret, sign_secret, cipher: legacy_cipher, digest: digest, serializer: SERIALIZER)
         
     | 
| 
      
 658 
     | 
    
         
            +
                    elsif prepare_upgrade_legacy_hmac_aes_cbc_cookies?
         
     | 
| 
      
 659 
     | 
    
         
            +
                      future_cipher = encrypted_cookie_cipher
         
     | 
| 
      
 660 
     | 
    
         
            +
                      secret = request.key_generator.generate_key(request.authenticated_encrypted_cookie_salt, ActiveSupport::MessageEncryptor.key_len(future_cipher))
         
     | 
| 
      
 661 
     | 
    
         
            +
             
     | 
| 
      
 662 
     | 
    
         
            +
                      @encryptor.rotate(secret, nil, cipher: future_cipher, serializer: SERIALIZER)
         
     | 
| 
       622 
663 
     | 
    
         
             
                    end
         
     | 
| 
       623 
664 
     | 
    
         
             
                  end
         
     | 
| 
       624 
665 
     | 
    
         | 
| 
         @@ -4,10 +4,7 @@ require "action_dispatch/http/request" 
     | 
|
| 
       4 
4 
     | 
    
         
             
            require "action_dispatch/middleware/exception_wrapper"
         
     | 
| 
       5 
5 
     | 
    
         
             
            require "action_dispatch/routing/inspector"
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
            require "active_support/actionable_error"
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
7 
     | 
    
         
             
            require "action_view"
         
     | 
| 
       10 
     | 
    
         
            -
            require "action_view/base"
         
     | 
| 
       11 
8 
     | 
    
         | 
| 
       12 
9 
     | 
    
         
             
            module ActionDispatch
         
     | 
| 
       13 
10 
     | 
    
         
             
              # This middleware is responsible for logging exceptions and
         
     | 
| 
         @@ -63,8 +60,8 @@ module ActionDispatch 
     | 
|
| 
       63 
60 
     | 
    
         
             
                    if request.get_header("action_dispatch.show_detailed_exceptions")
         
     | 
| 
       64 
61 
     | 
    
         
             
                      begin
         
     | 
| 
       65 
62 
     | 
    
         
             
                        content_type = request.formats.first
         
     | 
| 
       66 
     | 
    
         
            -
                      rescue  
     | 
| 
       67 
     | 
    
         
            -
                         
     | 
| 
      
 63 
     | 
    
         
            +
                      rescue ActionDispatch::Http::MimeNegotiation::InvalidType
         
     | 
| 
      
 64 
     | 
    
         
            +
                        content_type = Mime[:text]
         
     | 
| 
       68 
65 
     | 
    
         
             
                      end
         
     | 
| 
       69 
66 
     | 
    
         | 
| 
       70 
67 
     | 
    
         
             
                      if api_request?(content_type)
         
     | 
| 
         @@ -140,20 +137,16 @@ module ActionDispatch 
     | 
|
| 
       140 
137 
     | 
    
         
             
                    return unless logger
         
     | 
| 
       141 
138 
     | 
    
         | 
| 
       142 
139 
     | 
    
         
             
                    exception = wrapper.exception
         
     | 
| 
      
 140 
     | 
    
         
            +
                    trace = wrapper.exception_trace
         
     | 
| 
       143 
141 
     | 
    
         | 
| 
       144 
     | 
    
         
            -
                     
     | 
| 
       145 
     | 
    
         
            -
                     
     | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
                     
     | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
                      message << "#{exception.class} (#{exception.message}):"
         
     | 
| 
       151 
     | 
    
         
            -
                      message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code)
         
     | 
| 
       152 
     | 
    
         
            -
                      message << "  "
         
     | 
| 
       153 
     | 
    
         
            -
                      message.concat(trace)
         
     | 
| 
      
 142 
     | 
    
         
            +
                    message = []
         
     | 
| 
      
 143 
     | 
    
         
            +
                    message << "  "
         
     | 
| 
      
 144 
     | 
    
         
            +
                    message << "#{exception.class} (#{exception.message}):"
         
     | 
| 
      
 145 
     | 
    
         
            +
                    message.concat(exception.annotated_source_code) if exception.respond_to?(:annotated_source_code)
         
     | 
| 
      
 146 
     | 
    
         
            +
                    message << "  "
         
     | 
| 
      
 147 
     | 
    
         
            +
                    message.concat(trace)
         
     | 
| 
       154 
148 
     | 
    
         | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
                    end
         
     | 
| 
      
 149 
     | 
    
         
            +
                    log_array(logger, message)
         
     | 
| 
       157 
150 
     | 
    
         
             
                  end
         
     | 
| 
       158 
151 
     | 
    
         | 
| 
       159 
152 
     | 
    
         
             
                  def log_array(logger, array)
         
     | 
| 
         @@ -6,21 +6,21 @@ require "rack/utils" 
     | 
|
| 
       6 
6 
     | 
    
         
             
            module ActionDispatch
         
     | 
| 
       7 
7 
     | 
    
         
             
              class ExceptionWrapper
         
     | 
| 
       8 
8 
     | 
    
         
             
                cattr_accessor :rescue_responses, default: Hash.new(:internal_server_error).merge!(
         
     | 
| 
       9 
     | 
    
         
            -
                  "ActionController::RoutingError" 
     | 
| 
       10 
     | 
    
         
            -
                  "AbstractController::ActionNotFound" 
     | 
| 
       11 
     | 
    
         
            -
                  "ActionController::MethodNotAllowed" 
     | 
| 
       12 
     | 
    
         
            -
                  "ActionController::UnknownHttpMethod" 
     | 
| 
       13 
     | 
    
         
            -
                  "ActionController::NotImplemented" 
     | 
| 
       14 
     | 
    
         
            -
                  "ActionController::UnknownFormat" 
     | 
| 
       15 
     | 
    
         
            -
                  " 
     | 
| 
       16 
     | 
    
         
            -
                  "ActionController::MissingExactTemplate" 
     | 
| 
       17 
     | 
    
         
            -
                  "ActionController::InvalidAuthenticityToken" 
     | 
| 
       18 
     | 
    
         
            -
                  "ActionController::InvalidCrossOriginRequest" 
     | 
| 
       19 
     | 
    
         
            -
                  "ActionDispatch::Http::Parameters::ParseError" 
     | 
| 
       20 
     | 
    
         
            -
                  "ActionController::BadRequest" 
     | 
| 
       21 
     | 
    
         
            -
                  "ActionController::ParameterMissing" 
     | 
| 
       22 
     | 
    
         
            -
                  "Rack::QueryParser::ParameterTypeError" 
     | 
| 
       23 
     | 
    
         
            -
                  "Rack::QueryParser::InvalidParameterError" 
     | 
| 
      
 9 
     | 
    
         
            +
                  "ActionController::RoutingError"                     => :not_found,
         
     | 
| 
      
 10 
     | 
    
         
            +
                  "AbstractController::ActionNotFound"                 => :not_found,
         
     | 
| 
      
 11 
     | 
    
         
            +
                  "ActionController::MethodNotAllowed"                 => :method_not_allowed,
         
     | 
| 
      
 12 
     | 
    
         
            +
                  "ActionController::UnknownHttpMethod"                => :method_not_allowed,
         
     | 
| 
      
 13 
     | 
    
         
            +
                  "ActionController::NotImplemented"                   => :not_implemented,
         
     | 
| 
      
 14 
     | 
    
         
            +
                  "ActionController::UnknownFormat"                    => :not_acceptable,
         
     | 
| 
      
 15 
     | 
    
         
            +
                  "ActionDispatch::Http::MimeNegotiation::InvalidType" => :not_acceptable,
         
     | 
| 
      
 16 
     | 
    
         
            +
                  "ActionController::MissingExactTemplate"             => :not_acceptable,
         
     | 
| 
      
 17 
     | 
    
         
            +
                  "ActionController::InvalidAuthenticityToken"         => :unprocessable_entity,
         
     | 
| 
      
 18 
     | 
    
         
            +
                  "ActionController::InvalidCrossOriginRequest"        => :unprocessable_entity,
         
     | 
| 
      
 19 
     | 
    
         
            +
                  "ActionDispatch::Http::Parameters::ParseError"       => :bad_request,
         
     | 
| 
      
 20 
     | 
    
         
            +
                  "ActionController::BadRequest"                       => :bad_request,
         
     | 
| 
      
 21 
     | 
    
         
            +
                  "ActionController::ParameterMissing"                 => :bad_request,
         
     | 
| 
      
 22 
     | 
    
         
            +
                  "Rack::QueryParser::ParameterTypeError"              => :bad_request,
         
     | 
| 
      
 23 
     | 
    
         
            +
                  "Rack::QueryParser::InvalidParameterError"           => :bad_request
         
     | 
| 
       24 
24 
     | 
    
         
             
                )
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                cattr_accessor :rescue_templates, default: Hash.new("diagnostics").merge!(
         
     | 
| 
         @@ -36,18 +36,24 @@ module ActionDispatch 
     | 
|
| 
       36 
36 
     | 
    
         
             
                  "ActionView::Template::Error"
         
     | 
| 
       37 
37 
     | 
    
         
             
                ]
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
      
 39 
     | 
    
         
            +
                cattr_accessor :silent_exceptions, default: [
         
     | 
| 
      
 40 
     | 
    
         
            +
                  "ActionController::RoutingError",
         
     | 
| 
      
 41 
     | 
    
         
            +
                  "ActionDispatch::Http::MimeNegotiation::InvalidType"
         
     | 
| 
      
 42 
     | 
    
         
            +
                ]
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
       39 
44 
     | 
    
         
             
                attr_reader :backtrace_cleaner, :exception, :wrapped_causes, :line_number, :file
         
     | 
| 
       40 
45 
     | 
    
         | 
| 
       41 
46 
     | 
    
         
             
                def initialize(backtrace_cleaner, exception)
         
     | 
| 
       42 
47 
     | 
    
         
             
                  @backtrace_cleaner = backtrace_cleaner
         
     | 
| 
       43 
48 
     | 
    
         
             
                  @exception = exception
         
     | 
| 
      
 49 
     | 
    
         
            +
                  @exception_class_name = @exception.class.name
         
     | 
| 
       44 
50 
     | 
    
         
             
                  @wrapped_causes = wrapped_causes_for(exception, backtrace_cleaner)
         
     | 
| 
       45 
51 
     | 
    
         | 
| 
       46 
52 
     | 
    
         
             
                  expand_backtrace if exception.is_a?(SyntaxError) || exception.cause.is_a?(SyntaxError)
         
     | 
| 
       47 
53 
     | 
    
         
             
                end
         
     | 
| 
       48 
54 
     | 
    
         | 
| 
       49 
55 
     | 
    
         
             
                def unwrapped_exception
         
     | 
| 
       50 
     | 
    
         
            -
                  if wrapper_exceptions.include?( 
     | 
| 
      
 56 
     | 
    
         
            +
                  if wrapper_exceptions.include?(@exception_class_name)
         
     | 
| 
       51 
57 
     | 
    
         
             
                    exception.cause
         
     | 
| 
       52 
58 
     | 
    
         
             
                  else
         
     | 
| 
       53 
59 
     | 
    
         
             
                    exception
         
     | 
| 
         @@ -55,13 +61,19 @@ module ActionDispatch 
     | 
|
| 
       55 
61 
     | 
    
         
             
                end
         
     | 
| 
       56 
62 
     | 
    
         | 
| 
       57 
63 
     | 
    
         
             
                def rescue_template
         
     | 
| 
       58 
     | 
    
         
            -
                  @@rescue_templates[@ 
     | 
| 
      
 64 
     | 
    
         
            +
                  @@rescue_templates[@exception_class_name]
         
     | 
| 
       59 
65 
     | 
    
         
             
                end
         
     | 
| 
       60 
66 
     | 
    
         | 
| 
       61 
67 
     | 
    
         
             
                def status_code
         
     | 
| 
       62 
68 
     | 
    
         
             
                  self.class.status_code_for_exception(unwrapped_exception.class.name)
         
     | 
| 
       63 
69 
     | 
    
         
             
                end
         
     | 
| 
       64 
70 
     | 
    
         | 
| 
      
 71 
     | 
    
         
            +
                def exception_trace
         
     | 
| 
      
 72 
     | 
    
         
            +
                  trace = application_trace
         
     | 
| 
      
 73 
     | 
    
         
            +
                  trace = framework_trace if trace.empty? && !silent_exceptions.include?(@exception_class_name)
         
     | 
| 
      
 74 
     | 
    
         
            +
                  trace
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
       65 
77 
     | 
    
         
             
                def application_trace
         
     | 
| 
       66 
78 
     | 
    
         
             
                  clean_backtrace(:silent)
         
     | 
| 
       67 
79 
     | 
    
         
             
                end
         
     | 
| 
         @@ -4,7 +4,12 @@ require "action_dispatch/http/request" 
     | 
|
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            module ActionDispatch
         
     | 
| 
       6 
6 
     | 
    
         
             
              # This middleware guards from DNS rebinding attacks by explicitly permitting
         
     | 
| 
       7 
     | 
    
         
            -
              # the hosts a request can be sent to 
     | 
| 
      
 7 
     | 
    
         
            +
              # the hosts a request can be sent to, and is passed the options set in
         
     | 
| 
      
 8 
     | 
    
         
            +
              # +config.host_authorization+.
         
     | 
| 
      
 9 
     | 
    
         
            +
              #
         
     | 
| 
      
 10 
     | 
    
         
            +
              # Requests can opt-out of Host Authorization with +exclude+:
         
     | 
| 
      
 11 
     | 
    
         
            +
              #
         
     | 
| 
      
 12 
     | 
    
         
            +
              #    config.host_authorization = { exclude: ->(request) { request.path =~ /healthcheck/ } }
         
     | 
| 
       8 
13 
     | 
    
         
             
              #
         
     | 
| 
       9 
14 
     | 
    
         
             
              # When a request comes to an unauthorized host, the +response_app+
         
     | 
| 
       10 
15 
     | 
    
         
             
              # application will be executed and rendered. If no +response_app+ is given, a
         
     | 
| 
         @@ -46,9 +51,9 @@ module ActionDispatch 
     | 
|
| 
       46 
51 
     | 
    
         | 
| 
       47 
52 
     | 
    
         
             
                    def sanitize_string(host)
         
     | 
| 
       48 
53 
     | 
    
         
             
                      if host.start_with?(".")
         
     | 
| 
       49 
     | 
    
         
            -
                        /\A(.+\.)?#{Regexp.escape(host[1..-1])}\z/
         
     | 
| 
      
 54 
     | 
    
         
            +
                        /\A(.+\.)?#{Regexp.escape(host[1..-1])}\z/i
         
     | 
| 
       50 
55 
     | 
    
         
             
                      else
         
     | 
| 
       51 
     | 
    
         
            -
                        host
         
     | 
| 
      
 56 
     | 
    
         
            +
                        /\A#{host}\z/i
         
     | 
| 
       52 
57 
     | 
    
         
             
                      end
         
     | 
| 
       53 
58 
     | 
    
         
             
                    end
         
     | 
| 
       54 
59 
     | 
    
         
             
                end
         
     | 
| 
         @@ -66,9 +71,20 @@ module ActionDispatch 
     | 
|
| 
       66 
71 
     | 
    
         
             
                  }, [body]]
         
     | 
| 
       67 
72 
     | 
    
         
             
                end
         
     | 
| 
       68 
73 
     | 
    
         | 
| 
       69 
     | 
    
         
            -
                def initialize(app, hosts,  
     | 
| 
      
 74 
     | 
    
         
            +
                def initialize(app, hosts, deprecated_response_app = nil, exclude: nil, response_app: nil)
         
     | 
| 
       70 
75 
     | 
    
         
             
                  @app = app
         
     | 
| 
       71 
76 
     | 
    
         
             
                  @permissions = Permissions.new(hosts)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @exclude = exclude
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  unless deprecated_response_app.nil?
         
     | 
| 
      
 80 
     | 
    
         
            +
                    ActiveSupport::Deprecation.warn(<<-MSG.squish)
         
     | 
| 
      
 81 
     | 
    
         
            +
                      `action_dispatch.hosts_response_app` is deprecated and will be ignored in Rails 6.2.
         
     | 
| 
      
 82 
     | 
    
         
            +
                      Use the Host Authorization `response_app` setting instead.
         
     | 
| 
      
 83 
     | 
    
         
            +
                    MSG
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    response_app ||= deprecated_response_app
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
       72 
88 
     | 
    
         
             
                  @response_app = response_app || DEFAULT_RESPONSE_APP
         
     | 
| 
       73 
89 
     | 
    
         
             
                end
         
     | 
| 
       74 
90 
     | 
    
         | 
| 
         @@ -77,7 +93,7 @@ module ActionDispatch 
     | 
|
| 
       77 
93 
     | 
    
         | 
| 
       78 
94 
     | 
    
         
             
                  request = Request.new(env)
         
     | 
| 
       79 
95 
     | 
    
         | 
| 
       80 
     | 
    
         
            -
                  if authorized?(request)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  if authorized?(request) || excluded?(request)
         
     | 
| 
       81 
97 
     | 
    
         
             
                    mark_as_authorized(request)
         
     | 
| 
       82 
98 
     | 
    
         
             
                    @app.call(env)
         
     | 
| 
       83 
99 
     | 
    
         
             
                  else
         
     | 
| 
         @@ -94,6 +110,10 @@ module ActionDispatch 
     | 
|
| 
       94 
110 
     | 
    
         
             
                      (forwarded_host.blank? || @permissions.allows?(forwarded_host))
         
     | 
| 
       95 
111 
     | 
    
         
             
                  end
         
     | 
| 
       96 
112 
     | 
    
         | 
| 
      
 113 
     | 
    
         
            +
                  def excluded?(request)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    @exclude && @exclude.call(request)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
       97 
117 
     | 
    
         
             
                  def mark_as_authorized(request)
         
     | 
| 
       98 
118 
     | 
    
         
             
                    request.set_header("action_dispatch.authorized_host", request.host)
         
     | 
| 
       99 
119 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -23,7 +23,7 @@ module ActionDispatch 
     | 
|
| 
       23 
23 
     | 
    
         
             
                  status       = request.path_info[1..-1].to_i
         
     | 
| 
       24 
24 
     | 
    
         
             
                  begin
         
     | 
| 
       25 
25 
     | 
    
         
             
                    content_type = request.formats.first
         
     | 
| 
       26 
     | 
    
         
            -
                  rescue  
     | 
| 
      
 26 
     | 
    
         
            +
                  rescue ActionDispatch::Http::MimeNegotiation::InvalidType
         
     | 
| 
       27 
27 
     | 
    
         
             
                    content_type = Mime[:text]
         
     | 
| 
       28 
28 
     | 
    
         
             
                  end
         
     | 
| 
       29 
29 
     | 
    
         
             
                  body = { status: status, error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
         
     |