actionpack 7.1.5.1 → 8.1.2

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 (177) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +308 -523
  3. data/README.rdoc +1 -1
  4. data/lib/abstract_controller/asset_paths.rb +6 -2
  5. data/lib/abstract_controller/base.rb +104 -105
  6. data/lib/abstract_controller/caching/fragments.rb +50 -53
  7. data/lib/abstract_controller/caching.rb +8 -3
  8. data/lib/abstract_controller/callbacks.rb +70 -62
  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 +4 -1
  14. data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
  15. data/lib/abstract_controller/rendering.rb +13 -13
  16. data/lib/abstract_controller/translation.rb +12 -13
  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 +76 -72
  21. data/lib/action_controller/base.rb +199 -126
  22. data/lib/action_controller/caching.rb +16 -14
  23. data/lib/action_controller/deprecator.rb +2 -0
  24. data/lib/action_controller/form_builder.rb +21 -18
  25. data/lib/action_controller/log_subscriber.rb +23 -2
  26. data/lib/action_controller/metal/allow_browser.rb +133 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
  28. data/lib/action_controller/metal/conditional_get.rb +217 -175
  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 +72 -63
  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 +16 -9
  36. data/lib/action_controller/metal/flash.rb +13 -14
  37. data/lib/action_controller/metal/head.rb +15 -11
  38. data/lib/action_controller/metal/helpers.rb +63 -55
  39. data/lib/action_controller/metal/http_authentication.rb +209 -201
  40. data/lib/action_controller/metal/implicit_render.rb +17 -15
  41. data/lib/action_controller/metal/instrumentation.rb +16 -14
  42. data/lib/action_controller/metal/live.rb +177 -128
  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 +22 -12
  48. data/lib/action_controller/metal/rate_limiting.rb +92 -0
  49. data/lib/action_controller/metal/redirecting.rb +213 -94
  50. data/lib/action_controller/metal/renderers.rb +78 -57
  51. data/lib/action_controller/metal/rendering.rb +111 -77
  52. data/lib/action_controller/metal/request_forgery_protection.rb +182 -143
  53. data/lib/action_controller/metal/rescue.rb +20 -9
  54. data/lib/action_controller/metal/streaming.rb +118 -195
  55. data/lib/action_controller/metal/strong_parameters.rb +720 -530
  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 +36 -15
  60. data/lib/action_controller/railties/helpers.rb +2 -0
  61. data/lib/action_controller/renderer.rb +41 -36
  62. data/lib/action_controller/structured_event_subscriber.rb +116 -0
  63. data/lib/action_controller/template_assertions.rb +4 -2
  64. data/lib/action_controller/test_case.rb +160 -131
  65. data/lib/action_controller.rb +5 -1
  66. data/lib/action_dispatch/constants.rb +8 -0
  67. data/lib/action_dispatch/deprecator.rb +2 -0
  68. data/lib/action_dispatch/http/cache.rb +163 -35
  69. data/lib/action_dispatch/http/content_disposition.rb +2 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +54 -39
  71. data/lib/action_dispatch/http/filter_parameters.rb +14 -8
  72. data/lib/action_dispatch/http/filter_redirect.rb +22 -1
  73. data/lib/action_dispatch/http/headers.rb +22 -22
  74. data/lib/action_dispatch/http/mime_negotiation.rb +89 -41
  75. data/lib/action_dispatch/http/mime_type.rb +25 -21
  76. data/lib/action_dispatch/http/mime_types.rb +3 -0
  77. data/lib/action_dispatch/http/param_builder.rb +187 -0
  78. data/lib/action_dispatch/http/param_error.rb +26 -0
  79. data/lib/action_dispatch/http/parameters.rb +14 -12
  80. data/lib/action_dispatch/http/permissions_policy.rb +25 -36
  81. data/lib/action_dispatch/http/query_parser.rb +55 -0
  82. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  83. data/lib/action_dispatch/http/request.rb +141 -92
  84. data/lib/action_dispatch/http/response.rb +137 -77
  85. data/lib/action_dispatch/http/upload.rb +18 -16
  86. data/lib/action_dispatch/http/url.rb +187 -89
  87. data/lib/action_dispatch/journey/formatter.rb +21 -9
  88. data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
  89. data/lib/action_dispatch/journey/gtg/simulator.rb +34 -11
  90. data/lib/action_dispatch/journey/gtg/transition_table.rb +47 -53
  91. data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
  92. data/lib/action_dispatch/journey/nodes/node.rb +8 -6
  93. data/lib/action_dispatch/journey/parser.rb +99 -195
  94. data/lib/action_dispatch/journey/path/pattern.rb +4 -1
  95. data/lib/action_dispatch/journey/route.rb +54 -38
  96. data/lib/action_dispatch/journey/router/utils.rb +22 -27
  97. data/lib/action_dispatch/journey/router.rb +63 -83
  98. data/lib/action_dispatch/journey/routes.rb +11 -2
  99. data/lib/action_dispatch/journey/scanner.rb +46 -42
  100. data/lib/action_dispatch/journey/visitors.rb +57 -23
  101. data/lib/action_dispatch/journey/visualizer/fsm.js +4 -6
  102. data/lib/action_dispatch/journey.rb +2 -0
  103. data/lib/action_dispatch/log_subscriber.rb +7 -1
  104. data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
  105. data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
  106. data/lib/action_dispatch/middleware/callbacks.rb +3 -1
  107. data/lib/action_dispatch/middleware/cookies.rb +125 -106
  108. data/lib/action_dispatch/middleware/debug_exceptions.rb +37 -8
  109. data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
  110. data/lib/action_dispatch/middleware/debug_view.rb +13 -5
  111. data/lib/action_dispatch/middleware/exception_wrapper.rb +18 -23
  112. data/lib/action_dispatch/middleware/executor.rb +19 -4
  113. data/lib/action_dispatch/middleware/flash.rb +63 -51
  114. data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
  115. data/lib/action_dispatch/middleware/public_exceptions.rb +14 -12
  116. data/lib/action_dispatch/middleware/reloader.rb +5 -3
  117. data/lib/action_dispatch/middleware/remote_ip.rb +87 -77
  118. data/lib/action_dispatch/middleware/request_id.rb +16 -10
  119. data/lib/action_dispatch/middleware/server_timing.rb +4 -2
  120. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
  121. data/lib/action_dispatch/middleware/session/cache_store.rb +30 -8
  122. data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
  123. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
  124. data/lib/action_dispatch/middleware/show_exceptions.rb +16 -16
  125. data/lib/action_dispatch/middleware/ssl.rb +53 -40
  126. data/lib/action_dispatch/middleware/stack.rb +11 -10
  127. data/lib/action_dispatch/middleware/static.rb +33 -31
  128. data/lib/action_dispatch/middleware/templates/rescues/_copy_button.html.erb +1 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +3 -5
  130. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +9 -5
  131. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +1 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +4 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +3 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +50 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -0
  141. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
  142. data/lib/action_dispatch/railtie.rb +23 -3
  143. data/lib/action_dispatch/request/session.rb +24 -21
  144. data/lib/action_dispatch/request/utils.rb +11 -3
  145. data/lib/action_dispatch/routing/endpoint.rb +2 -0
  146. data/lib/action_dispatch/routing/inspector.rb +85 -60
  147. data/lib/action_dispatch/routing/mapper.rb +1031 -851
  148. data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
  149. data/lib/action_dispatch/routing/redirection.rb +47 -39
  150. data/lib/action_dispatch/routing/route_set.rb +79 -56
  151. data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
  152. data/lib/action_dispatch/routing/url_for.rb +130 -125
  153. data/lib/action_dispatch/routing.rb +150 -148
  154. data/lib/action_dispatch/structured_event_subscriber.rb +20 -0
  155. data/lib/action_dispatch/system_test_case.rb +91 -81
  156. data/lib/action_dispatch/system_testing/browser.rb +16 -23
  157. data/lib/action_dispatch/system_testing/driver.rb +2 -0
  158. data/lib/action_dispatch/system_testing/server.rb +2 -0
  159. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +34 -23
  160. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
  161. data/lib/action_dispatch/testing/assertion_response.rb +9 -7
  162. data/lib/action_dispatch/testing/assertions/response.rb +52 -25
  163. data/lib/action_dispatch/testing/assertions/routing.rb +168 -87
  164. data/lib/action_dispatch/testing/assertions.rb +2 -0
  165. data/lib/action_dispatch/testing/integration.rb +233 -223
  166. data/lib/action_dispatch/testing/request_encoder.rb +11 -9
  167. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  168. data/lib/action_dispatch/testing/test_process.rb +11 -8
  169. data/lib/action_dispatch/testing/test_request.rb +3 -1
  170. data/lib/action_dispatch/testing/test_response.rb +27 -26
  171. data/lib/action_dispatch.rb +36 -32
  172. data/lib/action_pack/gem_version.rb +6 -4
  173. data/lib/action_pack/version.rb +3 -1
  174. data/lib/action_pack.rb +17 -16
  175. metadata +36 -32
  176. data/lib/action_dispatch/journey/parser.y +0 -50
  177. data/lib/action_dispatch/journey/parser_extras.rb +0 -31
@@ -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)
@@ -84,12 +86,14 @@ module ActionDispatch # :nodoc:
84
86
  ambient_light_sensor: "ambient-light-sensor",
85
87
  autoplay: "autoplay",
86
88
  camera: "camera",
89
+ display_capture: "display-capture",
87
90
  encrypted_media: "encrypted-media",
88
91
  fullscreen: "fullscreen",
89
92
  geolocation: "geolocation",
90
93
  gyroscope: "gyroscope",
91
94
  hid: "hid",
92
95
  idle_detection: "idle-detection",
96
+ keyboard_map: "keyboard-map",
93
97
  magnetometer: "magnetometer",
94
98
  microphone: "microphone",
95
99
  midi: "midi",
@@ -125,25 +129,6 @@ module ActionDispatch # :nodoc:
125
129
  end
126
130
  end
127
131
 
128
- %w[speaker vibrate vr].each do |directive|
129
- define_method(directive) do |*sources|
130
- ActionDispatch.deprecator.warn(<<~MSG)
131
- The `#{directive}` permissions policy directive is deprecated
132
- and will be removed in Rails 7.2.
133
-
134
- There is no browser support for this directive, and no plan
135
- for browser support in the future. You can just remove this
136
- directive from your application.
137
- MSG
138
-
139
- if sources.first
140
- @directives[directive] = apply_mappings(sources)
141
- else
142
- @directives.delete(directive)
143
- end
144
- end
145
- end
146
-
147
132
  def build(context = nil)
148
133
  build_directives(context).compact.join("; ")
149
134
  end
@@ -201,4 +186,8 @@ module ActionDispatch # :nodoc:
201
186
  end
202
187
  end
203
188
  end
189
+
190
+ ActiveSupport.on_load(:action_dispatch_request) do
191
+ include ActionDispatch::PermissionsPolicy::Request
192
+ end
204
193
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "rack"
5
+
6
+ module ActionDispatch
7
+ class QueryParser
8
+ DEFAULT_SEP = /& */n
9
+ COMMON_SEP = { ";" => /; */n, ";," => /[;,] */n, "&" => /& */n, "&;" => /[&;] */n }
10
+
11
+ def self.strict_query_string_separator
12
+ ActionDispatch.deprecator.warn <<~MSG
13
+ The `strict_query_string_separator` configuration is deprecated have no effect and will be removed in Rails 8.2.
14
+ MSG
15
+ @strict_query_string_separator
16
+ end
17
+
18
+ def self.strict_query_string_separator=(value)
19
+ ActionDispatch.deprecator.warn <<~MSG
20
+ The `strict_query_string_separator` configuration is deprecated have no effect and will be removed in Rails 8.2.
21
+ MSG
22
+ @strict_query_string_separator = value
23
+ end
24
+
25
+ #--
26
+ # Note this departs from WHATWG's specified parsing algorithm by
27
+ # giving a nil value for keys that do not use '='. Callers that need
28
+ # the standard's interpretation can use `v.to_s`.
29
+ def self.each_pair(s, separator = nil)
30
+ return enum_for(:each_pair, s, separator) unless block_given?
31
+
32
+ s ||= ""
33
+
34
+ splitter =
35
+ if separator
36
+ COMMON_SEP[separator] || /[#{separator}] */n
37
+ else
38
+ DEFAULT_SEP
39
+ end
40
+
41
+ s.split(splitter).each do |part|
42
+ next if part.empty?
43
+
44
+ k, v = part.split("=", 2)
45
+
46
+ k = URI.decode_www_form_component(k)
47
+ v &&= URI.decode_www_form_component(v)
48
+
49
+ yield k, v
50
+ end
51
+
52
+ nil
53
+ end
54
+ end
55
+ 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"
@@ -23,7 +25,6 @@ module ActionDispatch
23
25
  include ActionDispatch::Http::FilterParameters
24
26
  include ActionDispatch::Http::URL
25
27
  include ActionDispatch::ContentSecurityPolicy::Request
26
- include ActionDispatch::PermissionsPolicy::Request
27
28
  include Rack::Request::Env
28
29
 
29
30
  autoload :Session, "action_dispatch/request/session"
@@ -53,12 +54,17 @@ module ActionDispatch
53
54
  METHOD
54
55
  end
55
56
 
57
+ TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING" # :nodoc:
58
+
56
59
  def self.empty
57
60
  new({})
58
61
  end
59
62
 
60
63
  def initialize(env)
61
64
  super
65
+
66
+ @rack_request = Rack::Request.new(env)
67
+
62
68
  @method = nil
63
69
  @request_method = nil
64
70
  @remote_ip = nil
@@ -67,6 +73,8 @@ module ActionDispatch
67
73
  @ip = nil
68
74
  end
69
75
 
76
+ attr_reader :rack_request
77
+
70
78
  def commit_cookie_jar! # :nodoc:
71
79
  end
72
80
 
@@ -102,26 +110,26 @@ module ActionDispatch
102
110
 
103
111
  # Returns true if the request has a header matching the given key parameter.
104
112
  #
105
- # request.key? :ip_spoofing_check # => true
113
+ # request.key? :ip_spoofing_check # => true
106
114
  def key?(key)
107
115
  has_header? key
108
116
  end
109
117
 
110
- # HTTP methods from {RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1}[https://www.ietf.org/rfc/rfc2616.txt]
118
+ # HTTP methods from [RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1](https://www.ietf.org/rfc/rfc2616.txt)
111
119
  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]
120
+ # HTTP methods from [RFC 2518: HTTP Extensions for Distributed Authoring -- WEBDAV](https://www.ietf.org/rfc/rfc2518.txt)
113
121
  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]
122
+ # HTTP methods from [RFC 3253: Versioning Extensions to WebDAV](https://www.ietf.org/rfc/rfc3253.txt)
115
123
  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]
124
+ # HTTP methods from [RFC 3648: WebDAV Ordered Collections Protocol](https://www.ietf.org/rfc/rfc3648.txt)
117
125
  RFC3648 = %w(ORDERPATCH)
118
- # HTTP methods from {RFC 3744: WebDAV Access Control Protocol}[https://www.ietf.org/rfc/rfc3744.txt]
126
+ # HTTP methods from [RFC 3744: WebDAV Access Control Protocol](https://www.ietf.org/rfc/rfc3744.txt)
119
127
  RFC3744 = %w(ACL)
120
- # HTTP methods from {RFC 5323: WebDAV SEARCH}[https://www.ietf.org/rfc/rfc5323.txt]
128
+ # HTTP methods from [RFC 5323: WebDAV SEARCH](https://www.ietf.org/rfc/rfc5323.txt)
121
129
  RFC5323 = %w(SEARCH)
122
- # HTTP methods from {RFC 4791: Calendaring Extensions to WebDAV}[https://www.ietf.org/rfc/rfc4791.txt]
130
+ # HTTP methods from [RFC 4791: Calendaring Extensions to WebDAV](https://www.ietf.org/rfc/rfc4791.txt)
123
131
  RFC4791 = %w(MKCALENDAR)
124
- # HTTP methods from {RFC 5789: PATCH Method for HTTP}[https://www.ietf.org/rfc/rfc5789.txt]
132
+ # HTTP methods from [RFC 5789: PATCH Method for HTTP](https://www.ietf.org/rfc/rfc5789.txt)
125
133
  RFC5789 = %w(PATCH)
126
134
 
127
135
  HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789
@@ -130,31 +138,36 @@ module ActionDispatch
130
138
 
131
139
  # Populate the HTTP method lookup cache.
132
140
  HTTP_METHODS.each { |method|
133
- HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
141
+ HTTP_METHOD_LOOKUP[method] = method.downcase.tap { |m| m.tr!("-", "_") }.to_sym
134
142
  }
135
143
 
136
144
  alias raw_request_method request_method # :nodoc:
137
145
 
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.
146
+ # Returns the HTTP method that the application should see. In the case where the
147
+ # method was overridden by a middleware (for instance, if a HEAD request was
148
+ # converted to a GET, or if a _method parameter was used to determine the method
149
+ # the application should use), this method returns the overridden value, not the
150
+ # original.
144
151
  def request_method
145
152
  @request_method ||= check_method(super)
146
153
  end
147
154
 
148
- # Returns the URI pattern of the matched route for the request,
149
- # using the same format as `bin/rails routes`:
155
+ # Returns the URI pattern of the matched route for the request, using the same
156
+ # format as `bin/rails routes`:
150
157
  #
151
- # request.route_uri_pattern # => "/:controller(/:action(/:id))(.:format)"
158
+ # request.route_uri_pattern # => "/:controller(/:action(/:id))(.:format)"
152
159
  def route_uri_pattern
153
- get_header("action_dispatch.route_uri_pattern")
160
+ unless pattern = get_header("action_dispatch.route_uri_pattern")
161
+ route = get_header("action_dispatch.route")
162
+ return if route.nil?
163
+ pattern = route.path.spec.to_s
164
+ set_header("action_dispatch.route_uri_pattern", pattern)
165
+ end
166
+ pattern
154
167
  end
155
168
 
156
- def route_uri_pattern=(pattern) # :nodoc:
157
- set_header("action_dispatch.route_uri_pattern", pattern)
169
+ def route=(route) # :nodoc:
170
+ @env["action_dispatch.route"] = route
158
171
  end
159
172
 
160
173
  def routes # :nodoc:
@@ -196,12 +209,11 @@ module ActionDispatch
196
209
  HTTP_METHOD_LOOKUP[request_method]
197
210
  end
198
211
 
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.
212
+ # Returns the original value of the environment's REQUEST_METHOD, even if it was
213
+ # overridden by middleware. See #request_method for more information.
202
214
  #
203
- # For debugging purposes, when called with arguments this method will
204
- # fall back to Object#method
215
+ # For debugging purposes, when called with arguments this method will fall back
216
+ # to Object#method
205
217
  def method(*args)
206
218
  if args.empty?
207
219
  @method ||= check_method(
@@ -221,74 +233,73 @@ module ActionDispatch
221
233
 
222
234
  # Provides access to the request's HTTP headers, for example:
223
235
  #
224
- # request.headers["Content-Type"] # => "text/plain"
236
+ # request.headers["Content-Type"] # => "text/plain"
225
237
  def headers
226
238
  @headers ||= Http::Headers.new(self)
227
239
  end
228
240
 
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.
241
+ # Early Hints is an HTTP/2 status code that indicates hints to help a client
242
+ # start making preparations for processing the final response.
231
243
  #
232
- # If the env contains +rack.early_hints+ then the server accepts HTTP2 push for
244
+ # If the env contains `rack.early_hints` then the server accepts HTTP2 push for
233
245
  # link headers.
234
246
  #
235
- # The +send_early_hints+ method accepts a hash of links as follows:
247
+ # The `send_early_hints` method accepts a hash of links as follows:
236
248
  #
237
- # send_early_hints("link" => "</style.css>; rel=preload; as=style,</script.js>; rel=preload")
249
+ # send_early_hints("link" => "</style.css>; rel=preload; as=style,</script.js>; rel=preload")
238
250
  #
239
- # If you are using +javascript_include_tag+ or +stylesheet_link_tag+ the
240
- # Early Hints headers are included by default if supported.
251
+ # If you are using {javascript_include_tag}[rdoc-ref:ActionView::Helpers::AssetTagHelper#javascript_include_tag]
252
+ # or {stylesheet_link_tag}[rdoc-ref:ActionView::Helpers::AssetTagHelper#stylesheet_link_tag]
253
+ # the Early Hints headers are included by default if supported.
241
254
  def send_early_hints(links)
242
- return unless env["rack.early_hints"]
243
-
244
- env["rack.early_hints"].call(links)
255
+ env["rack.early_hints"]&.call(links)
245
256
  end
246
257
 
247
- # Returns a +String+ with the last requested path including their params.
258
+ # Returns a `String` with the last requested path including their params.
248
259
  #
249
- # # get '/foo'
250
- # request.original_fullpath # => '/foo'
260
+ # # get '/foo'
261
+ # request.original_fullpath # => '/foo'
251
262
  #
252
- # # get '/foo?bar'
253
- # request.original_fullpath # => '/foo?bar'
263
+ # # get '/foo?bar'
264
+ # request.original_fullpath # => '/foo?bar'
254
265
  def original_fullpath
255
266
  @original_fullpath ||= (get_header("ORIGINAL_FULLPATH") || fullpath)
256
267
  end
257
268
 
258
- # Returns the +String+ full path including params of the last URL requested.
269
+ # Returns the `String` full path including params of the last URL requested.
259
270
  #
260
- # # get "/articles"
261
- # request.fullpath # => "/articles"
271
+ # # get "/articles"
272
+ # request.fullpath # => "/articles"
262
273
  #
263
- # # get "/articles?page=2"
264
- # request.fullpath # => "/articles?page=2"
274
+ # # get "/articles?page=2"
275
+ # request.fullpath # => "/articles?page=2"
265
276
  def fullpath
266
277
  @fullpath ||= super
267
278
  end
268
279
 
269
- # Returns the original request URL as a +String+.
280
+ # Returns the original request URL as a `String`.
270
281
  #
271
- # # get "/articles?page=2"
272
- # request.original_url # => "http://www.example.com/articles?page=2"
282
+ # # get "/articles?page=2"
283
+ # request.original_url # => "http://www.example.com/articles?page=2"
273
284
  def original_url
274
285
  base_url + original_fullpath
275
286
  end
276
287
 
277
- # The +String+ MIME type of the request.
288
+ # The `String` MIME type of the request.
278
289
  #
279
- # # get "/articles"
280
- # request.media_type # => "application/x-www-form-urlencoded"
290
+ # # get "/articles"
291
+ # request.media_type # => "application/x-www-form-urlencoded"
281
292
  def media_type
282
293
  content_mime_type&.to_s
283
294
  end
284
295
 
285
296
  # Returns the content length of the request as an integer.
286
297
  def content_length
287
- return raw_post.bytesize if headers.key?("Transfer-Encoding")
298
+ return raw_post.bytesize if has_header?(TRANSFER_ENCODING)
288
299
  super.to_i
289
300
  end
290
301
 
291
- # Returns true if the +X-Requested-With+ header contains "XMLHttpRequest"
302
+ # Returns true if the `X-Requested-With` header contains "XMLHttpRequest"
292
303
  # (case-insensitive), which may need to be manually added depending on the
293
304
  # choice of JavaScript libraries and frameworks.
294
305
  def xml_http_request?
@@ -296,13 +307,13 @@ module ActionDispatch
296
307
  end
297
308
  alias :xhr? :xml_http_request?
298
309
 
299
- # Returns the IP address of client as a +String+.
310
+ # Returns the IP address of client as a `String`.
300
311
  def ip
301
312
  @ip ||= super
302
313
  end
303
314
 
304
- # Returns the IP address of client as a +String+,
305
- # usually set by the RemoteIp middleware.
315
+ # Returns the IP address of client as a `String`, usually set by the RemoteIp
316
+ # middleware.
306
317
  def remote_ip
307
318
  @remote_ip ||= (get_header("action_dispatch.remote_ip") || ip).to_s
308
319
  end
@@ -314,12 +325,14 @@ module ActionDispatch
314
325
 
315
326
  ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id" # :nodoc:
316
327
 
317
- # Returns the unique request id, which is based on either the +X-Request-Id+ header that can
318
- # be generated by a firewall, load balancer, or web server, or by the RequestId middleware
319
- # (which sets the +action_dispatch.request_id+ environment variable).
328
+ # Returns the unique request id, which is based on either the `X-Request-Id`
329
+ # header that can be generated by a firewall, load balancer, or web server, or
330
+ # by the RequestId middleware (which sets the `action_dispatch.request_id`
331
+ # environment variable).
320
332
  #
321
- # This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
322
- # This relies on the Rack variable set by the ActionDispatch::RequestId middleware.
333
+ # This unique ID is useful for tracing a request from end-to-end as part of
334
+ # logging or debugging. This relies on the Rack variable set by the
335
+ # ActionDispatch::RequestId middleware.
323
336
  def request_id
324
337
  get_header ACTION_DISPATCH_REQUEST_ID
325
338
  end
@@ -335,8 +348,8 @@ module ActionDispatch
335
348
  (get_header("SERVER_SOFTWARE") && /^([a-zA-Z]+)/ =~ get_header("SERVER_SOFTWARE")) ? $1.downcase : nil
336
349
  end
337
350
 
338
- # Read the request \body. This is useful for web services that need to
339
- # work with raw requests directly.
351
+ # Read the request body. This is useful for web services that need to work with
352
+ # raw requests directly.
340
353
  def raw_post
341
354
  unless has_header? "RAW_POST_DATA"
342
355
  set_header("RAW_POST_DATA", read_body_stream)
@@ -355,14 +368,13 @@ module ActionDispatch
355
368
  end
356
369
  end
357
370
 
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.
371
+ # Determine whether the request body contains form-data by checking the request
372
+ # `Content-Type` for one of the media-types: `application/x-www-form-urlencoded`
373
+ # or `multipart/form-data`. The list of form-data media types can be modified
374
+ # through the `FORM_DATA_MEDIA_TYPES` array.
363
375
  #
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.
376
+ # A request body is not assumed to contain form-data when no `Content-Type`
377
+ # header is provided and the request_method is POST.
366
378
  def form_data?
367
379
  FORM_DATA_MEDIA_TYPES.include?(media_type)
368
380
  end
@@ -387,15 +399,12 @@ module ActionDispatch
387
399
  # Override Rack's GET method to support indifferent access.
388
400
  def GET
389
401
  fetch_header("action_dispatch.request.query_parameters") do |k|
390
- rack_query_params = super || {}
391
- controller = path_parameters[:controller]
392
- action = path_parameters[:action]
393
- rack_query_params = Request::Utils.set_binary_encoding(self, rack_query_params, controller, action)
394
- # Check for non UTF-8 parameter values, which would cause errors later
395
- Request::Utils.check_param_encoding(rack_query_params)
396
- set_header k, Request::Utils.normalize_encode_params(rack_query_params)
402
+ encoding_template = Request::Utils::CustomParamEncoder.action_encoding_template(self, path_parameters[:controller], path_parameters[:action])
403
+ rack_query_params = ActionDispatch::ParamBuilder.from_query_string(rack_request.query_string, encoding_template: encoding_template)
404
+
405
+ set_header k, rack_query_params
397
406
  end
398
- rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError, Rack::QueryParser::ParamsTooDeepError => e
407
+ rescue ActionDispatch::ParamError => e
399
408
  raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}")
400
409
  end
401
410
  alias :query_parameters :GET
@@ -403,20 +412,56 @@ module ActionDispatch
403
412
  # Override Rack's POST method to support indifferent access.
404
413
  def POST
405
414
  fetch_header("action_dispatch.request.request_parameters") do
406
- pr = parse_formatted_parameters(params_parsers) do |params|
407
- super || {}
415
+ encoding_template = Request::Utils::CustomParamEncoder.action_encoding_template(self, path_parameters[:controller], path_parameters[:action])
416
+
417
+ param_list = nil
418
+ pr = parse_formatted_parameters(params_parsers) do
419
+ if param_list = request_parameters_list
420
+ ActionDispatch::ParamBuilder.from_pairs(param_list, encoding_template: encoding_template)
421
+ else
422
+ # We're not using a version of Rack that provides raw form
423
+ # pairs; we must use its hash (and thus post-process it below).
424
+ fallback_request_parameters
425
+ end
426
+ end
427
+
428
+ # If the request body was parsed by a custom parser like JSON
429
+ # (and thus the above block was not run), we need to
430
+ # post-process the result hash.
431
+ if param_list.nil?
432
+ pr = ActionDispatch::ParamBuilder.from_hash(pr, encoding_template: encoding_template)
408
433
  end
409
- pr = Request::Utils.set_binary_encoding(self, pr, path_parameters[:controller], path_parameters[:action])
410
- Request::Utils.check_param_encoding(pr)
411
- self.request_parameters = Request::Utils.normalize_encode_params(pr)
434
+
435
+ self.request_parameters = pr
412
436
  end
413
- rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError, Rack::QueryParser::ParamsTooDeepError, EOFError => e
437
+ rescue ActionDispatch::ParamError, EOFError => e
414
438
  raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
415
439
  end
416
440
  alias :request_parameters :POST
417
441
 
418
- # Returns the authorization header regardless of whether it was specified directly or through one of the
419
- # proxy alternatives.
442
+ def request_parameters_list
443
+ # We don't use Rack's parse result, but we must call it so Rack
444
+ # can populate the rack.request.* keys we need.
445
+ rack_post = rack_request.POST
446
+
447
+ if form_pairs = get_header("rack.request.form_pairs")
448
+ # Multipart
449
+ form_pairs
450
+ elsif form_vars = get_header("rack.request.form_vars")
451
+ # URL-encoded
452
+ ActionDispatch::QueryParser.each_pair(form_vars)
453
+ elsif rack_post && !rack_post.empty?
454
+ # It was multipart, but Rack did not preserve a pair list
455
+ # (probably too old). Flat parameter list is not available.
456
+ nil
457
+ else
458
+ # No request body, or not a format Rack knows
459
+ []
460
+ end
461
+ end
462
+
463
+ # Returns the authorization header regardless of whether it was specified
464
+ # directly or through one of the proxy alternatives.
420
465
  def authorization
421
466
  get_header("HTTP_AUTHORIZATION") ||
422
467
  get_header("X-HTTP_AUTHORIZATION") ||
@@ -456,7 +501,7 @@ module ActionDispatch
456
501
  private
457
502
  def check_method(name)
458
503
  if name
459
- HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")
504
+ HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(locale: false)}")
460
505
  end
461
506
 
462
507
  name
@@ -469,7 +514,7 @@ module ActionDispatch
469
514
  def read_body_stream
470
515
  if body_stream
471
516
  reset_stream(body_stream) do
472
- if headers.key?("Transfer-Encoding")
517
+ if has_header?(TRANSFER_ENCODING)
473
518
  body_stream.read # Read body stream until EOF if "Transfer-Encoding" is present
474
519
  else
475
520
  body_stream.read(content_length)
@@ -491,6 +536,10 @@ module ActionDispatch
491
536
  yield
492
537
  end
493
538
  end
539
+
540
+ def fallback_request_parameters
541
+ rack_request.POST
542
+ end
494
543
  end
495
544
  end
496
545