actionpack 7.1.3 → 7.2.3

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.
Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +121 -465
  3. data/README.rdoc +1 -1
  4. data/lib/abstract_controller/asset_paths.rb +2 -0
  5. data/lib/abstract_controller/base.rb +93 -100
  6. data/lib/abstract_controller/caching/fragments.rb +50 -53
  7. data/lib/abstract_controller/caching.rb +2 -0
  8. data/lib/abstract_controller/callbacks.rb +66 -64
  9. data/lib/abstract_controller/collector.rb +7 -7
  10. data/lib/abstract_controller/deprecator.rb +2 -0
  11. data/lib/abstract_controller/error.rb +2 -0
  12. data/lib/abstract_controller/helpers.rb +71 -84
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/rendering.rb +13 -12
  16. data/lib/abstract_controller/translation.rb +15 -7
  17. data/lib/abstract_controller/url_for.rb +8 -6
  18. data/lib/abstract_controller.rb +2 -0
  19. data/lib/action_controller/api/api_rendering.rb +2 -0
  20. data/lib/action_controller/api.rb +75 -72
  21. data/lib/action_controller/base.rb +198 -126
  22. data/lib/action_controller/caching.rb +15 -12
  23. data/lib/action_controller/deprecator.rb +2 -0
  24. data/lib/action_controller/form_builder.rb +20 -17
  25. data/lib/action_controller/log_subscriber.rb +3 -1
  26. data/lib/action_controller/metal/allow_browser.rb +123 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  28. data/lib/action_controller/metal/conditional_get.rb +187 -174
  29. data/lib/action_controller/metal/content_security_policy.rb +25 -24
  30. data/lib/action_controller/metal/cookies.rb +4 -2
  31. data/lib/action_controller/metal/data_streaming.rb +64 -55
  32. data/lib/action_controller/metal/default_headers.rb +5 -3
  33. data/lib/action_controller/metal/etag_with_flash.rb +3 -1
  34. data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
  35. data/lib/action_controller/metal/exceptions.rb +11 -9
  36. data/lib/action_controller/metal/flash.rb +12 -10
  37. data/lib/action_controller/metal/head.rb +12 -10
  38. data/lib/action_controller/metal/helpers.rb +63 -55
  39. data/lib/action_controller/metal/http_authentication.rb +211 -206
  40. data/lib/action_controller/metal/implicit_render.rb +17 -15
  41. data/lib/action_controller/metal/instrumentation.rb +15 -12
  42. data/lib/action_controller/metal/live.rb +117 -108
  43. data/lib/action_controller/metal/logging.rb +6 -4
  44. data/lib/action_controller/metal/mime_responds.rb +151 -142
  45. data/lib/action_controller/metal/parameter_encoding.rb +34 -32
  46. data/lib/action_controller/metal/params_wrapper.rb +57 -59
  47. data/lib/action_controller/metal/permissions_policy.rb +13 -12
  48. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  49. data/lib/action_controller/metal/redirecting.rb +112 -85
  50. data/lib/action_controller/metal/renderers.rb +50 -49
  51. data/lib/action_controller/metal/rendering.rb +104 -76
  52. data/lib/action_controller/metal/request_forgery_protection.rb +165 -134
  53. data/lib/action_controller/metal/rescue.rb +11 -9
  54. data/lib/action_controller/metal/streaming.rb +138 -136
  55. data/lib/action_controller/metal/strong_parameters.rb +525 -483
  56. data/lib/action_controller/metal/testing.rb +2 -0
  57. data/lib/action_controller/metal/url_for.rb +17 -15
  58. data/lib/action_controller/metal.rb +86 -60
  59. data/lib/action_controller/railtie.rb +3 -0
  60. data/lib/action_controller/railties/helpers.rb +2 -0
  61. data/lib/action_controller/renderer.rb +41 -36
  62. data/lib/action_controller/template_assertions.rb +4 -2
  63. data/lib/action_controller/test_case.rb +148 -129
  64. data/lib/action_controller.rb +10 -3
  65. data/lib/action_dispatch/constants.rb +8 -0
  66. data/lib/action_dispatch/deprecator.rb +2 -0
  67. data/lib/action_dispatch/http/cache.rb +27 -26
  68. data/lib/action_dispatch/http/content_disposition.rb +2 -0
  69. data/lib/action_dispatch/http/content_security_policy.rb +61 -42
  70. data/lib/action_dispatch/http/filter_parameters.rb +18 -9
  71. data/lib/action_dispatch/http/filter_redirect.rb +22 -1
  72. data/lib/action_dispatch/http/headers.rb +22 -22
  73. data/lib/action_dispatch/http/mime_negotiation.rb +35 -41
  74. data/lib/action_dispatch/http/mime_type.rb +31 -24
  75. data/lib/action_dispatch/http/mime_types.rb +2 -0
  76. data/lib/action_dispatch/http/parameters.rb +11 -9
  77. data/lib/action_dispatch/http/permissions_policy.rb +20 -44
  78. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  79. data/lib/action_dispatch/http/request.rb +96 -76
  80. data/lib/action_dispatch/http/response.rb +88 -62
  81. data/lib/action_dispatch/http/upload.rb +18 -16
  82. data/lib/action_dispatch/http/url.rb +77 -75
  83. data/lib/action_dispatch/journey/formatter.rb +21 -9
  84. data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
  85. data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
  86. data/lib/action_dispatch/journey/gtg/transition_table.rb +14 -12
  87. data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
  88. data/lib/action_dispatch/journey/nodes/node.rb +6 -5
  89. data/lib/action_dispatch/journey/parser.rb +4 -3
  90. data/lib/action_dispatch/journey/parser_extras.rb +2 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +4 -1
  92. data/lib/action_dispatch/journey/route.rb +9 -7
  93. data/lib/action_dispatch/journey/router/utils.rb +16 -15
  94. data/lib/action_dispatch/journey/router.rb +4 -2
  95. data/lib/action_dispatch/journey/routes.rb +4 -2
  96. data/lib/action_dispatch/journey/scanner.rb +4 -2
  97. data/lib/action_dispatch/journey/visitors.rb +2 -0
  98. data/lib/action_dispatch/journey.rb +2 -0
  99. data/lib/action_dispatch/log_subscriber.rb +2 -0
  100. data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
  101. data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
  102. data/lib/action_dispatch/middleware/callbacks.rb +3 -1
  103. data/lib/action_dispatch/middleware/cookies.rb +119 -104
  104. data/lib/action_dispatch/middleware/debug_exceptions.rb +16 -6
  105. data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
  106. data/lib/action_dispatch/middleware/debug_view.rb +2 -0
  107. data/lib/action_dispatch/middleware/exception_wrapper.rb +9 -14
  108. data/lib/action_dispatch/middleware/executor.rb +13 -2
  109. data/lib/action_dispatch/middleware/flash.rb +63 -51
  110. data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
  111. data/lib/action_dispatch/middleware/public_exceptions.rb +13 -7
  112. data/lib/action_dispatch/middleware/reloader.rb +5 -3
  113. data/lib/action_dispatch/middleware/remote_ip.rb +76 -72
  114. data/lib/action_dispatch/middleware/request_id.rb +14 -9
  115. data/lib/action_dispatch/middleware/server_timing.rb +4 -2
  116. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
  117. data/lib/action_dispatch/middleware/session/cache_store.rb +13 -8
  118. data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
  119. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
  120. data/lib/action_dispatch/middleware/show_exceptions.rb +31 -21
  121. data/lib/action_dispatch/middleware/ssl.rb +43 -40
  122. data/lib/action_dispatch/middleware/stack.rb +11 -10
  123. data/lib/action_dispatch/middleware/static.rb +33 -31
  124. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +1 -1
  125. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -1
  126. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
  127. data/lib/action_dispatch/railtie.rb +2 -4
  128. data/lib/action_dispatch/request/session.rb +23 -21
  129. data/lib/action_dispatch/request/utils.rb +2 -0
  130. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  131. data/lib/action_dispatch/routing/inspector.rb +6 -4
  132. data/lib/action_dispatch/routing/mapper.rb +673 -637
  133. data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
  134. data/lib/action_dispatch/routing/redirection.rb +37 -32
  135. data/lib/action_dispatch/routing/route_set.rb +60 -46
  136. data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
  137. data/lib/action_dispatch/routing/url_for.rb +130 -125
  138. data/lib/action_dispatch/routing.rb +150 -148
  139. data/lib/action_dispatch/system_test_case.rb +91 -81
  140. data/lib/action_dispatch/system_testing/browser.rb +10 -3
  141. data/lib/action_dispatch/system_testing/driver.rb +3 -1
  142. data/lib/action_dispatch/system_testing/server.rb +2 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
  144. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  145. data/lib/action_dispatch/testing/assertion_response.rb +9 -7
  146. data/lib/action_dispatch/testing/assertions/response.rb +26 -23
  147. data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
  148. data/lib/action_dispatch/testing/assertions.rb +2 -0
  149. data/lib/action_dispatch/testing/integration.rb +222 -222
  150. data/lib/action_dispatch/testing/request_encoder.rb +11 -9
  151. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  152. data/lib/action_dispatch/testing/test_process.rb +11 -8
  153. data/lib/action_dispatch/testing/test_request.rb +3 -1
  154. data/lib/action_dispatch/testing/test_response.rb +27 -26
  155. data/lib/action_dispatch.rb +25 -27
  156. data/lib/action_pack/gem_version.rb +4 -2
  157. data/lib/action_pack/version.rb +3 -1
  158. data/lib/action_pack.rb +17 -16
  159. metadata +50 -16
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionDispatch
4
6
  module Http
5
7
  module Parameters
@@ -14,8 +16,8 @@ module ActionDispatch
14
16
  }
15
17
  }
16
18
 
17
- # Raised when raw data from the request cannot be parsed by the parser
18
- # defined for request's content MIME type.
19
+ # Raised when raw data from the request cannot be parsed by the parser defined
20
+ # for request's content MIME type.
19
21
  class ParseError < StandardError
20
22
  def initialize(message = $!.message)
21
23
  super(message)
@@ -34,8 +36,8 @@ module ActionDispatch
34
36
  module ClassMethods
35
37
  # Configure the parameter parser for a given MIME type.
36
38
  #
37
- # It accepts a hash where the key is the symbol of the MIME type
38
- # and the value is a proc.
39
+ # It accepts a hash where the key is the symbol of the MIME type and the value
40
+ # is a proc.
39
41
  #
40
42
  # original_parsers = ActionDispatch::Request.parameter_parsers
41
43
  # xml_parser = -> (raw_post) { Hash.from_xml(raw_post) || {} }
@@ -46,7 +48,7 @@ module ActionDispatch
46
48
  end
47
49
  end
48
50
 
49
- # Returns both GET and POST \parameters in a single hash.
51
+ # Returns both GET and POST parameters in a single hash.
50
52
  def parameters
51
53
  params = get_header("action_dispatch.request.parameters")
52
54
  return params if params
@@ -66,8 +68,8 @@ module ActionDispatch
66
68
  delete_header("action_dispatch.request.parameters")
67
69
 
68
70
  parameters = Request::Utils.set_binary_encoding(self, parameters, parameters[:controller], parameters[:action])
69
- # If any of the path parameters has an invalid encoding then
70
- # raise since it's likely to trigger errors further on.
71
+ # If any of the path parameters has an invalid encoding then raise since it's
72
+ # likely to trigger errors further on.
71
73
  Request::Utils.check_param_encoding(parameters)
72
74
 
73
75
  set_header PARAMETERS_KEY, parameters
@@ -75,10 +77,10 @@ module ActionDispatch
75
77
  raise ActionController::BadRequest.new("Invalid path parameters: #{e.message}")
76
78
  end
77
79
 
78
- # Returns a hash with the \parameters used to form the \path of the request.
80
+ # Returns a hash with the parameters used to form the path of the request.
79
81
  # Returned hash keys are symbols:
80
82
  #
81
- # { action: "my_action", controller: "my_controller" }
83
+ # { action: "my_action", controller: "my_controller" }
82
84
  def path_parameters
83
85
  get_header(PARAMETERS_KEY) || set_header(PARAMETERS_KEY, {})
84
86
  end
@@ -1,31 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "active_support/core_ext/object/deep_dup"
4
6
 
5
7
  module ActionDispatch # :nodoc:
6
- # = Action Dispatch \PermissionsPolicy
8
+ # # Action Dispatch PermissionsPolicy
7
9
  #
8
10
  # Configures the HTTP
9
- # {Feature-Policy}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy]
10
- # response header to specify which browser features the current document and
11
- # its iframes can use.
11
+ # [Feature-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy)
12
+ # response header to specify which browser features the current
13
+ # document and its iframes can use.
12
14
  #
13
15
  # Example global policy:
14
16
  #
15
- # Rails.application.config.permissions_policy do |policy|
16
- # policy.camera :none
17
- # policy.gyroscope :none
18
- # policy.microphone :none
19
- # policy.usb :none
20
- # policy.fullscreen :self
21
- # policy.payment :self, "https://secure.example.com"
22
- # end
17
+ # Rails.application.config.permissions_policy do |policy|
18
+ # policy.camera :none
19
+ # policy.gyroscope :none
20
+ # policy.microphone :none
21
+ # policy.usb :none
22
+ # policy.fullscreen :self
23
+ # policy.payment :self, "https://secure.example.com"
24
+ # end
23
25
  #
24
- # The Feature-Policy header has been renamed to Permissions-Policy.
25
- # The Permissions-Policy requires a different implementation and isn't
26
- # yet supported by all browsers. To avoid having to rename this
27
- # middleware in the future we use the new name for the middleware but
28
- # keep the old header name and implementation for now.
26
+ # The Feature-Policy header has been renamed to Permissions-Policy. The
27
+ # Permissions-Policy requires a different implementation and isn't yet supported
28
+ # by all browsers. To avoid having to rename this middleware in the future we
29
+ # use the new name for the middleware but keep the old header name and
30
+ # implementation for now.
29
31
  class PermissionsPolicy
30
32
  class Middleware
31
33
  def initialize(app)
@@ -35,7 +37,6 @@ module ActionDispatch # :nodoc:
35
37
  def call(env)
36
38
  _, headers, _ = response = @app.call(env)
37
39
 
38
- return response unless html_response?(headers)
39
40
  return response if policy_present?(headers)
40
41
 
41
42
  request = ActionDispatch::Request.new(env)
@@ -52,12 +53,6 @@ module ActionDispatch # :nodoc:
52
53
  end
53
54
 
54
55
  private
55
- def html_response?(headers)
56
- if content_type = headers[Rack::CONTENT_TYPE]
57
- content_type.include?("html")
58
- end
59
- end
60
-
61
56
  def policy_present?(headers)
62
57
  headers[ActionDispatch::Constants::FEATURE_POLICY]
63
58
  end
@@ -96,7 +91,7 @@ module ActionDispatch # :nodoc:
96
91
  geolocation: "geolocation",
97
92
  gyroscope: "gyroscope",
98
93
  hid: "hid",
99
- idle_detection: "idle_detection",
94
+ idle_detection: "idle-detection",
100
95
  magnetometer: "magnetometer",
101
96
  microphone: "microphone",
102
97
  midi: "midi",
@@ -132,25 +127,6 @@ module ActionDispatch # :nodoc:
132
127
  end
133
128
  end
134
129
 
135
- %w[speaker vibrate vr].each do |directive|
136
- define_method(directive) do |*sources|
137
- ActionDispatch.deprecator.warn(<<~MSG)
138
- The `#{directive}` permissions policy directive is deprecated
139
- and will be removed in Rails 7.2.
140
-
141
- There is no browser support for this directive, and no plan
142
- for browser support in the future. You can just remove this
143
- directive from your application.
144
- MSG
145
-
146
- if sources.first
147
- @directives[directive] = apply_mappings(sources)
148
- else
149
- @directives.delete(directive)
150
- end
151
- end
152
- end
153
-
154
130
  def build(context = nil)
155
131
  build_directives(context).compact.join("; ")
156
132
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  # :enddoc:
4
4
 
5
+ # :markup: markdown
6
+
5
7
  require "rack/cache"
6
8
  require "rack/cache/context"
7
9
  require "active_support/cache"
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "stringio"
4
6
 
5
7
  require "active_support/inflector"
@@ -102,26 +104,26 @@ module ActionDispatch
102
104
 
103
105
  # Returns true if the request has a header matching the given key parameter.
104
106
  #
105
- # request.key? :ip_spoofing_check # => true
107
+ # request.key? :ip_spoofing_check # => true
106
108
  def key?(key)
107
109
  has_header? key
108
110
  end
109
111
 
110
- # HTTP methods from {RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1}[https://www.ietf.org/rfc/rfc2616.txt]
112
+ # HTTP methods from [RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1](https://www.ietf.org/rfc/rfc2616.txt)
111
113
  RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
112
- # HTTP methods from {RFC 2518: HTTP Extensions for Distributed Authoring -- WEBDAV}[https://www.ietf.org/rfc/rfc2518.txt]
114
+ # HTTP methods from [RFC 2518: HTTP Extensions for Distributed Authoring -- WEBDAV](https://www.ietf.org/rfc/rfc2518.txt)
113
115
  RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
114
- # HTTP methods from {RFC 3253: Versioning Extensions to WebDAV}[https://www.ietf.org/rfc/rfc3253.txt]
116
+ # HTTP methods from [RFC 3253: Versioning Extensions to WebDAV](https://www.ietf.org/rfc/rfc3253.txt)
115
117
  RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
116
- # HTTP methods from {RFC 3648: WebDAV Ordered Collections Protocol}[https://www.ietf.org/rfc/rfc3648.txt]
118
+ # HTTP methods from [RFC 3648: WebDAV Ordered Collections Protocol](https://www.ietf.org/rfc/rfc3648.txt)
117
119
  RFC3648 = %w(ORDERPATCH)
118
- # HTTP methods from {RFC 3744: WebDAV Access Control Protocol}[https://www.ietf.org/rfc/rfc3744.txt]
120
+ # HTTP methods from [RFC 3744: WebDAV Access Control Protocol](https://www.ietf.org/rfc/rfc3744.txt)
119
121
  RFC3744 = %w(ACL)
120
- # HTTP methods from {RFC 5323: WebDAV SEARCH}[https://www.ietf.org/rfc/rfc5323.txt]
122
+ # HTTP methods from [RFC 5323: WebDAV SEARCH](https://www.ietf.org/rfc/rfc5323.txt)
121
123
  RFC5323 = %w(SEARCH)
122
- # HTTP methods from {RFC 4791: Calendaring Extensions to WebDAV}[https://www.ietf.org/rfc/rfc4791.txt]
124
+ # HTTP methods from [RFC 4791: Calendaring Extensions to WebDAV](https://www.ietf.org/rfc/rfc4791.txt)
123
125
  RFC4791 = %w(MKCALENDAR)
124
- # HTTP methods from {RFC 5789: PATCH Method for HTTP}[https://www.ietf.org/rfc/rfc5789.txt]
126
+ # HTTP methods from [RFC 5789: PATCH Method for HTTP](https://www.ietf.org/rfc/rfc5789.txt)
125
127
  RFC5789 = %w(PATCH)
126
128
 
127
129
  HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789
@@ -130,25 +132,24 @@ module ActionDispatch
130
132
 
131
133
  # Populate the HTTP method lookup cache.
132
134
  HTTP_METHODS.each { |method|
133
- HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
135
+ HTTP_METHOD_LOOKUP[method] = method.downcase.underscore.to_sym
134
136
  }
135
137
 
136
138
  alias raw_request_method request_method # :nodoc:
137
139
 
138
- # Returns the HTTP \method that the application should see.
139
- # In the case where the \method was overridden by a middleware
140
- # (for instance, if a HEAD request was converted to a GET,
141
- # or if a _method parameter was used to determine the \method
142
- # the application should use), this \method returns the overridden
143
- # value, not the original.
140
+ # Returns the HTTP method that the application should see. In the case where the
141
+ # method was overridden by a middleware (for instance, if a HEAD request was
142
+ # converted to a GET, or if a _method parameter was used to determine the method
143
+ # the application should use), this method returns the overridden value, not the
144
+ # original.
144
145
  def request_method
145
146
  @request_method ||= check_method(super)
146
147
  end
147
148
 
148
- # Returns the URI pattern of the matched route for the request,
149
- # using the same format as `bin/rails routes`:
149
+ # Returns the URI pattern of the matched route for the request, using the same
150
+ # format as `bin/rails routes`:
150
151
  #
151
- # request.route_uri_pattern # => "/:controller(/:action(/:id))(.:format)"
152
+ # request.route_uri_pattern # => "/:controller(/:action(/:id))(.:format)"
152
153
  def route_uri_pattern
153
154
  get_header("action_dispatch.route_uri_pattern")
154
155
  end
@@ -196,12 +197,11 @@ module ActionDispatch
196
197
  HTTP_METHOD_LOOKUP[request_method]
197
198
  end
198
199
 
199
- # Returns the original value of the environment's REQUEST_METHOD,
200
- # even if it was overridden by middleware. See #request_method for
201
- # more information.
200
+ # Returns the original value of the environment's REQUEST_METHOD, even if it was
201
+ # overridden by middleware. See #request_method for more information.
202
202
  #
203
- # For debugging purposes, when called with arguments this method will
204
- # fall back to Object#method
203
+ # For debugging purposes, when called with arguments this method will fall back
204
+ # to Object#method
205
205
  def method(*args)
206
206
  if args.empty?
207
207
  @method ||= check_method(
@@ -221,62 +221,62 @@ module ActionDispatch
221
221
 
222
222
  # Provides access to the request's HTTP headers, for example:
223
223
  #
224
- # request.headers["Content-Type"] # => "text/plain"
224
+ # request.headers["Content-Type"] # => "text/plain"
225
225
  def headers
226
226
  @headers ||= Http::Headers.new(self)
227
227
  end
228
228
 
229
- # Early Hints is an HTTP/2 status code that indicates hints to help a client start
230
- # making preparations for processing the final response.
229
+ # Early Hints is an HTTP/2 status code that indicates hints to help a client
230
+ # start making preparations for processing the final response.
231
231
  #
232
- # If the env contains +rack.early_hints+ then the server accepts HTTP2 push for Link headers.
232
+ # If the env contains `rack.early_hints` then the server accepts HTTP2 push for
233
+ # link headers.
233
234
  #
234
- # The +send_early_hints+ method accepts a hash of links as follows:
235
+ # The `send_early_hints` method accepts a hash of links as follows:
235
236
  #
236
- # send_early_hints("Link" => "</style.css>; rel=preload; as=style\n</script.js>; rel=preload")
237
+ # send_early_hints("link" => "</style.css>; rel=preload; as=style,</script.js>; rel=preload")
237
238
  #
238
- # If you are using +javascript_include_tag+ or +stylesheet_link_tag+ the
239
- # Early Hints headers are included by default if supported.
239
+ # If you are using {javascript_include_tag}[rdoc-ref:ActionView::Helpers::AssetTagHelper#javascript_include_tag]
240
+ # or {stylesheet_link_tag}[rdoc-ref:ActionView::Helpers::AssetTagHelper#stylesheet_link_tag]
241
+ # the Early Hints headers are included by default if supported.
240
242
  def send_early_hints(links)
241
- return unless env["rack.early_hints"]
242
-
243
- env["rack.early_hints"].call(links)
243
+ env["rack.early_hints"]&.call(links)
244
244
  end
245
245
 
246
- # Returns a +String+ with the last requested path including their params.
246
+ # Returns a `String` with the last requested path including their params.
247
247
  #
248
- # # get '/foo'
249
- # request.original_fullpath # => '/foo'
248
+ # # get '/foo'
249
+ # request.original_fullpath # => '/foo'
250
250
  #
251
- # # get '/foo?bar'
252
- # request.original_fullpath # => '/foo?bar'
251
+ # # get '/foo?bar'
252
+ # request.original_fullpath # => '/foo?bar'
253
253
  def original_fullpath
254
254
  @original_fullpath ||= (get_header("ORIGINAL_FULLPATH") || fullpath)
255
255
  end
256
256
 
257
- # Returns the +String+ full path including params of the last URL requested.
257
+ # Returns the `String` full path including params of the last URL requested.
258
258
  #
259
- # # get "/articles"
260
- # request.fullpath # => "/articles"
259
+ # # get "/articles"
260
+ # request.fullpath # => "/articles"
261
261
  #
262
- # # get "/articles?page=2"
263
- # request.fullpath # => "/articles?page=2"
262
+ # # get "/articles?page=2"
263
+ # request.fullpath # => "/articles?page=2"
264
264
  def fullpath
265
265
  @fullpath ||= super
266
266
  end
267
267
 
268
- # Returns the original request URL as a +String+.
268
+ # Returns the original request URL as a `String`.
269
269
  #
270
- # # get "/articles?page=2"
271
- # request.original_url # => "http://www.example.com/articles?page=2"
270
+ # # get "/articles?page=2"
271
+ # request.original_url # => "http://www.example.com/articles?page=2"
272
272
  def original_url
273
273
  base_url + original_fullpath
274
274
  end
275
275
 
276
- # The +String+ MIME type of the request.
276
+ # The `String` MIME type of the request.
277
277
  #
278
- # # get "/articles"
279
- # request.media_type # => "application/x-www-form-urlencoded"
278
+ # # get "/articles"
279
+ # request.media_type # => "application/x-www-form-urlencoded"
280
280
  def media_type
281
281
  content_mime_type&.to_s
282
282
  end
@@ -287,7 +287,7 @@ module ActionDispatch
287
287
  super.to_i
288
288
  end
289
289
 
290
- # Returns true if the +X-Requested-With+ header contains "XMLHttpRequest"
290
+ # Returns true if the `X-Requested-With` header contains "XMLHttpRequest"
291
291
  # (case-insensitive), which may need to be manually added depending on the
292
292
  # choice of JavaScript libraries and frameworks.
293
293
  def xml_http_request?
@@ -295,13 +295,13 @@ module ActionDispatch
295
295
  end
296
296
  alias :xhr? :xml_http_request?
297
297
 
298
- # Returns the IP address of client as a +String+.
298
+ # Returns the IP address of client as a `String`.
299
299
  def ip
300
300
  @ip ||= super
301
301
  end
302
302
 
303
- # Returns the IP address of client as a +String+,
304
- # usually set by the RemoteIp middleware.
303
+ # Returns the IP address of client as a `String`, usually set by the RemoteIp
304
+ # middleware.
305
305
  def remote_ip
306
306
  @remote_ip ||= (get_header("action_dispatch.remote_ip") || ip).to_s
307
307
  end
@@ -313,12 +313,14 @@ module ActionDispatch
313
313
 
314
314
  ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id" # :nodoc:
315
315
 
316
- # Returns the unique request id, which is based on either the +X-Request-Id+ header that can
317
- # be generated by a firewall, load balancer, or web server, or by the RequestId middleware
318
- # (which sets the +action_dispatch.request_id+ environment variable).
316
+ # Returns the unique request id, which is based on either the `X-Request-Id`
317
+ # header that can be generated by a firewall, load balancer, or web server, or
318
+ # by the RequestId middleware (which sets the `action_dispatch.request_id`
319
+ # environment variable).
319
320
  #
320
- # This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
321
- # This relies on the Rack variable set by the ActionDispatch::RequestId middleware.
321
+ # This unique ID is useful for tracing a request from end-to-end as part of
322
+ # logging or debugging. This relies on the Rack variable set by the
323
+ # ActionDispatch::RequestId middleware.
322
324
  def request_id
323
325
  get_header ACTION_DISPATCH_REQUEST_ID
324
326
  end
@@ -334,12 +336,11 @@ module ActionDispatch
334
336
  (get_header("SERVER_SOFTWARE") && /^([a-zA-Z]+)/ =~ get_header("SERVER_SOFTWARE")) ? $1.downcase : nil
335
337
  end
336
338
 
337
- # Read the request \body. This is useful for web services that need to
338
- # work with raw requests directly.
339
+ # Read the request body. This is useful for web services that need to work with
340
+ # raw requests directly.
339
341
  def raw_post
340
342
  unless has_header? "RAW_POST_DATA"
341
343
  set_header("RAW_POST_DATA", read_body_stream)
342
- body_stream.rewind if body_stream.respond_to?(:rewind)
343
344
  end
344
345
  get_header "RAW_POST_DATA"
345
346
  end
@@ -355,14 +356,13 @@ module ActionDispatch
355
356
  end
356
357
  end
357
358
 
358
- # Determine whether the request body contains form-data by checking
359
- # the request +Content-Type+ for one of the media-types:
360
- # +application/x-www-form-urlencoded+ or +multipart/form-data+. The
361
- # list of form-data media types can be modified through the
362
- # +FORM_DATA_MEDIA_TYPES+ array.
359
+ # Determine whether the request body contains form-data by checking the request
360
+ # `Content-Type` for one of the media-types: `application/x-www-form-urlencoded`
361
+ # or `multipart/form-data`. The list of form-data media types can be modified
362
+ # through the `FORM_DATA_MEDIA_TYPES` array.
363
363
  #
364
- # A request body is not assumed to contain form-data when no
365
- # +Content-Type+ header is provided and the request_method is POST.
364
+ # A request body is not assumed to contain form-data when no `Content-Type`
365
+ # header is provided and the request_method is POST.
366
366
  def form_data?
367
367
  FORM_DATA_MEDIA_TYPES.include?(media_type)
368
368
  end
@@ -415,8 +415,8 @@ module ActionDispatch
415
415
  end
416
416
  alias :request_parameters :POST
417
417
 
418
- # Returns the authorization header regardless of whether it was specified directly or through one of the
419
- # proxy alternatives.
418
+ # Returns the authorization header regardless of whether it was specified
419
+ # directly or through one of the proxy alternatives.
420
420
  def authorization
421
421
  get_header("HTTP_AUTHORIZATION") ||
422
422
  get_header("X-HTTP_AUTHORIZATION") ||
@@ -456,7 +456,7 @@ module ActionDispatch
456
456
  private
457
457
  def check_method(name)
458
458
  if name
459
- HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")
459
+ HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(locale: false)}")
460
460
  end
461
461
 
462
462
  name
@@ -467,9 +467,29 @@ module ActionDispatch
467
467
  end
468
468
 
469
469
  def read_body_stream
470
- body_stream.rewind if body_stream.respond_to?(:rewind)
471
- return body_stream.read if headers.key?("Transfer-Encoding") # Read body stream until EOF if "Transfer-Encoding" is present
472
- body_stream.read(content_length)
470
+ if body_stream
471
+ reset_stream(body_stream) do
472
+ if headers.key?("Transfer-Encoding")
473
+ body_stream.read # Read body stream until EOF if "Transfer-Encoding" is present
474
+ else
475
+ body_stream.read(content_length)
476
+ end
477
+ end
478
+ end
479
+ end
480
+
481
+ def reset_stream(body_stream)
482
+ if body_stream.respond_to?(:rewind)
483
+ body_stream.rewind
484
+
485
+ content = yield
486
+
487
+ body_stream.rewind
488
+
489
+ content
490
+ else
491
+ yield
492
+ end
473
493
  end
474
494
  end
475
495
  end