actionpack 7.2.3 → 8.0.4

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +172 -119
  3. data/lib/abstract_controller/rendering.rb +0 -1
  4. data/lib/action_controller/base.rb +1 -1
  5. data/lib/action_controller/form_builder.rb +3 -3
  6. data/lib/action_controller/metal/allow_browser.rb +11 -1
  7. data/lib/action_controller/metal/conditional_get.rb +5 -1
  8. data/lib/action_controller/metal/data_streaming.rb +4 -2
  9. data/lib/action_controller/metal/instrumentation.rb +1 -2
  10. data/lib/action_controller/metal/live.rb +16 -9
  11. data/lib/action_controller/metal/rate_limiting.rb +13 -4
  12. data/lib/action_controller/metal/renderers.rb +2 -3
  13. data/lib/action_controller/metal/streaming.rb +5 -84
  14. data/lib/action_controller/metal/strong_parameters.rb +277 -89
  15. data/lib/action_controller/railtie.rb +1 -7
  16. data/lib/action_controller/test_case.rb +12 -2
  17. data/lib/action_dispatch/http/cache.rb +27 -10
  18. data/lib/action_dispatch/http/content_security_policy.rb +14 -1
  19. data/lib/action_dispatch/http/param_builder.rb +186 -0
  20. data/lib/action_dispatch/http/param_error.rb +26 -0
  21. data/lib/action_dispatch/http/permissions_policy.rb +2 -0
  22. data/lib/action_dispatch/http/query_parser.rb +53 -0
  23. data/lib/action_dispatch/http/request.rb +60 -16
  24. data/lib/action_dispatch/http/response.rb +34 -13
  25. data/lib/action_dispatch/journey/parser.rb +99 -196
  26. data/lib/action_dispatch/journey/scanner.rb +44 -42
  27. data/lib/action_dispatch/middleware/cookies.rb +4 -2
  28. data/lib/action_dispatch/middleware/debug_exceptions.rb +16 -3
  29. data/lib/action_dispatch/middleware/debug_view.rb +0 -5
  30. data/lib/action_dispatch/middleware/exception_wrapper.rb +0 -6
  31. data/lib/action_dispatch/middleware/request_id.rb +2 -1
  32. data/lib/action_dispatch/middleware/ssl.rb +13 -3
  33. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +0 -3
  34. data/lib/action_dispatch/railtie.rb +8 -0
  35. data/lib/action_dispatch/request/session.rb +1 -0
  36. data/lib/action_dispatch/request/utils.rb +9 -3
  37. data/lib/action_dispatch/routing/inspector.rb +1 -1
  38. data/lib/action_dispatch/routing/mapper.rb +91 -62
  39. data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -2
  40. data/lib/action_dispatch/routing/route_set.rb +21 -10
  41. data/lib/action_dispatch/routing/routes_proxy.rb +1 -0
  42. data/lib/action_dispatch/system_testing/browser.rb +12 -21
  43. data/lib/action_dispatch/testing/assertions/response.rb +12 -2
  44. data/lib/action_dispatch/testing/assertions/routing.rb +16 -12
  45. data/lib/action_dispatch/testing/integration.rb +14 -4
  46. data/lib/action_dispatch.rb +6 -4
  47. data/lib/action_pack/gem_version.rb +3 -3
  48. metadata +14 -47
  49. data/lib/action_dispatch/journey/parser.y +0 -50
  50. data/lib/action_dispatch/journey/parser_extras.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e774a50c3ffb6c9c4f387e2bf3632992f1979e8b050cdd481e0ff41fc289f4d4
4
- data.tar.gz: b433f1b952b2cca2850d40a668bbf4196a72dd1981ede7714348607cb79fadb1
3
+ metadata.gz: 1fea6ba071295e83d6e5781df7582dfe27d5f3757183dfac91bf650b090e445e
4
+ data.tar.gz: ffcc0f47bbc76b7491946fbd0cf039d3d61201e9e389417dba5d6b2fd5b66a77
5
5
  SHA512:
6
- metadata.gz: 7c48de1742782bbad870035834911eb2bac273330d2d34317c2d670f02d762d9998f024491fe37e17b8628f4255844d66fec7522bb47e10668e0a775e160e6c4
7
- data.tar.gz: d72cf3db810cc51bfca8ff37af293e22a4089838ec00fb8cc44dce15d77255937a7221a90b6fdd2dc38f01803abd42757641b5fb665d6c121111bf3e548022f4
6
+ metadata.gz: 787632a979e611d3c63181e8cde8c4da8b1c7aabdcff0e8bed08bc675f0140cd0de24f8c867130e8db78512a2db2f6ad529c1fc28d5df76106c14b382e1c306a
7
+ data.tar.gz: 3171d0030c6cff57a0797a88d6b3250c9c074f150d2ca44b577e020d913158eaaa80fd860dd8e42982bc36b32e9aa7c7630f760ac451615358d1f6be478fd53d
data/CHANGELOG.md CHANGED
@@ -1,9 +1,28 @@
1
- ## Rails 7.2.3 (October 28, 2025) ##
1
+ ## Rails 8.0.4 (October 28, 2025) ##
2
2
 
3
3
  * Submit test requests using `as: :html` with `Content-Type: x-www-form-urlencoded`
4
4
 
5
5
  *Sean Doyle*
6
6
 
7
+
8
+ ## Rails 8.0.3 (September 22, 2025) ##
9
+
10
+ * URL helpers for engines mounted at the application root handle `SCRIPT_NAME` correctly.
11
+
12
+ Fixed an issue where `SCRIPT_NAME` is not applied to paths generated for routes in an engine
13
+ mounted at "/".
14
+
15
+ *Mike Dalessio*
16
+
17
+ * Fix `Rails.application.reload_routes!` from clearing almost all routes.
18
+
19
+ When calling `Rails.application.reload_routes!` inside a middleware of
20
+ a Rake task, it was possible under certain conditions that all routes would be cleared.
21
+ If ran inside a middleware, this would result in getting a 404 on most page you visit.
22
+ This issue was only happening in development.
23
+
24
+ *Edouard Chin*
25
+
7
26
  * Address `rack 3.2` deprecations warnings.
8
27
 
9
28
  ```
@@ -11,10 +30,14 @@
11
30
  Please use :unprocessable_content instead.
12
31
  ```
13
32
 
14
- Rails API will transparently convert one into the other for the forseable future.
33
+ Rails API will transparently convert one into the other for the foreseeable future.
15
34
 
16
35
  *Earlopain*, *Jean Boussier*
17
36
 
37
+ * Support hash-source in Content Security Policy.
38
+
39
+ *madogiwa*
40
+
18
41
  * Always return empty body for HEAD requests in `PublicExceptions` and
19
42
  `DebugExceptions`.
20
43
 
@@ -22,218 +45,248 @@
22
45
 
23
46
  *Hartley McGuire*
24
47
 
25
- * Fix `url_for` to handle `:path_params` gracefully when it's not a `Hash`.
26
48
 
27
- Prevents various security scanners from causing exceptions.
49
+ ## Rails 8.0.2.1 (August 13, 2025) ##
28
50
 
29
- *Martin Emde*
30
-
31
- * Fix `ActionDispatch::Executor` to unwrap exceptions like other error reporting middlewares.
51
+ * No changes.
32
52
 
33
- *Jean Boussier*
53
+ ## Rails 8.0.2 (March 12, 2025) ##
34
54
 
35
- * Fix NoMethodError when a non-string CSRF token is passed through headers.
55
+ * Improve `with_routing` test helper to not rebuild the middleware stack.
36
56
 
37
- *Ryan Heneise*
57
+ Otherwise some middleware configuration could be lost.
38
58
 
39
- * Fix invalid response when rescuing `ActionController::Redirecting::UnsafeRedirectError` in a controller.
59
+ *Édouard Chin*
40
60
 
41
- *Alex Ghiculescu*
61
+ * Add resource name to the `ArgumentError` that's raised when invalid `:only` or `:except` options are given to `#resource` or `#resources`
42
62
 
63
+ This makes it easier to locate the source of the problem, especially for routes drawn by gems.
43
64
 
44
- ## Rails 7.2.2.2 (August 13, 2025) ##
65
+ Before:
66
+ ```
67
+ :only and :except must include only [:index, :create, :new, :show, :update, :destroy, :edit], but also included [:foo, :bar]
68
+ ```
45
69
 
46
- * No changes.
70
+ After:
71
+ ```
72
+ Route `resources :products` - :only and :except must include only [:index, :create, :new, :show, :update, :destroy, :edit], but also included [:foo, :bar]
73
+ ```
47
74
 
75
+ *Jeremy Green*
48
76
 
49
- ## Rails 7.2.2.1 (December 10, 2024) ##
77
+ * Fix `url_for` to handle `:path_params` gracefully when it's not a `Hash`.
50
78
 
51
- * Add validation to content security policies to disallow spaces and semicolons.
52
- Developers should use multiple arguments, and different directive methods instead.
79
+ Prevents various security scanners from causing exceptions.
53
80
 
54
- [CVE-2024-54133]
81
+ *Martin Emde*
55
82
 
56
- *Gannon McGibbon*
83
+ * Fix `ActionDispatch::Executor` to unwrap exceptions like other error reporting middlewares.
57
84
 
85
+ *Jean Boussier*
58
86
 
59
- ## Rails 7.2.2 (October 30, 2024) ##
60
87
 
61
- * Fix non-GET requests not updating cookies in `ActionController::TestCase`.
88
+ ## Rails 8.0.1 (December 13, 2024) ##
62
89
 
63
- *Jon Moss*, *Hartley McGuire*
90
+ * Add `ActionDispatch::Request::Session#store` method to conform Rack spec.
64
91
 
92
+ *Yaroslav*
65
93
 
66
- ## Rails 7.2.1.2 (October 23, 2024) ##
67
94
 
68
- * No changes.
95
+ ## Rails 8.0.0.1 (December 10, 2024) ##
69
96
 
97
+ * Add validation to content security policies to disallow spaces and semicolons.
98
+ Developers should use multiple arguments, and different directive methods instead.
70
99
 
71
- ## Rails 7.2.1.1 (October 15, 2024) ##
100
+ [CVE-2024-54133]
72
101
 
73
- * Avoid regex backtracking in HTTP Token authentication
102
+ *Gannon McGibbon*
74
103
 
75
- [CVE-2024-47887]
76
104
 
77
- *John Hawthorn*
105
+ ## Rails 8.0.0 (November 07, 2024) ##
78
106
 
79
- * Avoid regex backtracking in query parameter filtering
107
+ * No changes.
80
108
 
81
- [CVE-2024-41128]
82
109
 
83
- *John Hawthorn*
110
+ ## Rails 8.0.0.rc2 (October 30, 2024) ##
84
111
 
112
+ * Fix routes with `::` in the path.
85
113
 
86
- ## Rails 7.2.1 (August 22, 2024) ##
114
+ *Rafael Mendonça França*
87
115
 
88
- * Fix `Request#raw_post` raising `NoMethodError` when `rack.input` is `nil`.
116
+ * Maintain Rack 2 parameter parsing behaviour.
89
117
 
90
- *Hartley McGuire*
118
+ *Matthew Draper*
91
119
 
92
120
 
93
- ## Rails 7.2.0 (August 09, 2024) ##
121
+ ## Rails 8.0.0.rc1 (October 19, 2024) ##
94
122
 
95
- * Allow bots to ignore `allow_browser`.
123
+ * Remove `Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality`.
96
124
 
97
- *Matthew Nguyen*
125
+ *Rafael Mendonça França*
98
126
 
99
- * Include the HTTP Permissions-Policy on non-HTML Content-Types
100
- [CVE-2024-28103]
127
+ * Improve `ActionController::TestCase` to expose a binary encoded `request.body`.
101
128
 
102
- *Aaron Patterson*, *Zack Deveau*
129
+ The rack spec clearly states:
103
130
 
104
- * Fix `Mime::Type.parse` handling type parameters for HTTP Accept headers.
131
+ > The input stream is an IO-like object which contains the raw HTTP POST data.
132
+ > When applicable, its external encoding must be “ASCII-8BIT” and it must be opened in binary mode.
105
133
 
106
- *Taylor Chaparro*
134
+ Until now its encoding was generally UTF-8, which doesn't accurately reflect production
135
+ behavior.
107
136
 
108
- * Fix the error page that is displayed when a view template is missing to account for nested controller paths in the
109
- suggested correct location for the missing template.
137
+ *Jean Boussier*
110
138
 
111
- *Joshua Young*
139
+ * Update `ActionController::AllowBrowser` to support passing method names to `:block`
112
140
 
113
- * Add `save_and_open_page` helper to `IntegrationTest`.
141
+ ```ruby
142
+ class ApplicationController < ActionController::Base
143
+ allow_browser versions: :modern, block: :handle_outdated_browser
114
144
 
115
- `save_and_open_page` is a helpful helper to keep a short feedback loop when working on system tests.
116
- A similar helper with matching signature has been added to integration tests.
145
+ private
146
+ def handle_outdated_browser
147
+ render file: Rails.root.join("public/custom-error.html"), status: :not_acceptable
148
+ end
149
+ end
150
+ ```
117
151
 
118
- *Joé Dupuis*
152
+ *Sean Doyle*
119
153
 
120
- * Fix a regression in 7.1.3 passing a `to:` option without a controller when the controller is already defined by a scope.
154
+ * Raise an `ArgumentError` when invalid `:only` or `:except` options are passed into `#resource` and `#resources`.
121
155
 
122
- ```ruby
123
- Rails.application.routes.draw do
124
- controller :home do
125
- get "recent", to: "recent_posts"
126
- end
127
- end
128
- ```
156
+ *Joshua Young*
129
157
 
130
- *Étienne Barrié*
158
+ ## Rails 8.0.0.beta1 (September 26, 2024) ##
131
159
 
132
- * Request Forgery takes relative paths into account.
160
+ * Fix non-GET requests not updating cookies in `ActionController::TestCase`.
133
161
 
134
- *Stefan Wienert*
162
+ *Jon Moss*, *Hartley McGuire*
135
163
 
136
- * Add ".test" as a default allowed host in development to ensure smooth golden-path setup with puma.dev.
164
+ * Update `ActionController::Live` to use a thread-pool to reuse threads across requests.
137
165
 
138
- *DHH*
166
+ *Adam Renberg Tamm*
139
167
 
140
- * Add `allow_browser` to set minimum browser versions for the application.
168
+ * Introduce safer, more explicit params handling method with `params#expect` such that
169
+ `params.expect(table: [ :attr ])` replaces `params.require(:table).permit(:attr)`
141
170
 
142
- A browser that's blocked will by default be served the file in `public/406-unsupported-browser.html` with a HTTP status code of "406 Not Acceptable".
171
+ Ensures params are filtered with consideration for the expected
172
+ types of values, improving handling of params and avoiding ignorable
173
+ errors caused by params tampering.
143
174
 
144
175
  ```ruby
145
- class ApplicationController < ActionController::Base
146
- # Allow only browsers natively supporting webp images, web push, badges, import maps, CSS nesting + :has
147
- allow_browser versions: :modern
148
- end
176
+ # If the url is altered to ?person=hacked
177
+ # Before
178
+ params.require(:person).permit(:name, :age, pets: [:name])
179
+ # raises NoMethodError, causing a 500 and potential error reporting
180
+
181
+ # After
182
+ params.expect(person: [ :name, :age, pets: [[:name]] ])
183
+ # raises ActionController::ParameterMissing, correctly returning a 400 error
184
+ ```
149
185
 
150
- class ApplicationController < ActionController::Base
151
- # All versions of Chrome and Opera will be allowed, but no versions of "internet explorer" (ie). Safari needs to be 16.4+ and Firefox 121+.
152
- allow_browser versions: { safari: 16.4, firefox: 121, ie: false }
153
- end
186
+ You may also notice the new double array `[[:name]]`. In order to
187
+ declare when a param is expected to be an array of parameter hashes,
188
+ this new double array syntax is used to explicitly declare an array.
189
+ `expect` requires you to declare expected arrays in this way, and will
190
+ ignore arrays that are passed when, for example, `pet: [:name]` is used.
154
191
 
155
- class MessagesController < ApplicationController
156
- # In addition to the browsers blocked by ApplicationController, also block Opera below 104 and Chrome below 119 for the show action.
157
- allow_browser versions: { opera: 104, chrome: 119 }, only: :show
158
- end
159
- ```
192
+ In order to preserve compatibility, `permit` does not adopt the new
193
+ double array syntax and is therefore more permissive about unexpected
194
+ types. Using `expect` everywhere is recommended.
160
195
 
161
- *DHH*
196
+ We suggest replacing `params.require(:person).permit(:name, :age)`
197
+ with the direct replacement `params.expect(person: [:name, :age])`
198
+ to prevent external users from manipulating params to trigger 500
199
+ errors. A 400 error will be returned instead, using public/400.html
162
200
 
163
- * Add rate limiting API.
201
+ Usage of `params.require(:id)` should likewise be replaced with
202
+ `params.expect(:id)` which is designed to ensure that `params[:id]`
203
+ is a scalar and not an array or hash, also requiring the param.
164
204
 
165
205
  ```ruby
166
- class SessionsController < ApplicationController
167
- rate_limit to: 10, within: 3.minutes, only: :create
168
- end
206
+ # Before
207
+ User.find(params.require(:id)) # allows an array, altering behavior
169
208
 
170
- class SignupsController < ApplicationController
171
- rate_limit to: 1000, within: 10.seconds,
172
- by: -> { request.domain }, with: -> { redirect_to busy_controller_url, alert: "Too many signups!" }, only: :new
173
- end
209
+ # After
210
+ User.find(params.expect(:id)) # expect only returns non-blank permitted scalars (excludes Hash, Array, nil, "", etc)
174
211
  ```
175
212
 
176
- *DHH*, *Jean Boussier*
213
+ *Martin Emde*
177
214
 
178
- * Add `image/svg+xml` to the compressible content types of `ActionDispatch::Static`.
215
+ * System Testing: Disable Chrome's search engine choice by default in system tests.
179
216
 
180
- *Georg Ledermann*
217
+ *glaszig*
181
218
 
182
- * Add instrumentation for `ActionController::Live#send_stream`.
219
+ * Fix `Request#raw_post` raising `NoMethodError` when `rack.input` is `nil`.
220
+
221
+ *Hartley McGuire*
183
222
 
184
- Allows subscribing to `send_stream` events. The event payload contains the filename, disposition, and type.
223
+ * Remove `racc` dependency by manually writing `ActionDispatch::Journey::Scanner`.
185
224
 
186
- *Hannah Ramadan*
225
+ *Gannon McGibbon*
187
226
 
188
- * Add support for `with_routing` test helper in `ActionDispatch::IntegrationTest`.
227
+ * Speed up `ActionDispatch::Routing::Mapper::Scope#[]` by merging frame hashes.
189
228
 
190
229
  *Gannon McGibbon*
191
230
 
192
- * Remove deprecated support to set `Rails.application.config.action_dispatch.show_exceptions` to `true` and `false`.
231
+ * Allow bots to ignore `allow_browser`.
193
232
 
194
- *Rafael Mendonça França*
233
+ *Matthew Nguyen*
195
234
 
196
- * Remove deprecated `speaker`, `vibrate`, and `vr` permissions policy directives.
235
+ * Deprecate drawing routes with multiple paths to make routing faster.
236
+ You may use `with_options` or a loop to make drawing multiple paths easier.
197
237
 
198
- *Rafael Mendonça França*
238
+ ```ruby
239
+ # Before
240
+ get "/users", "/other_path", to: "users#index"
199
241
 
200
- * Remove deprecated `Rails.application.config.action_dispatch.return_only_request_media_type_on_content_type`.
242
+ # After
243
+ get "/users", to: "users#index"
244
+ get "/other_path", to: "users#index"
245
+ ```
201
246
 
202
- *Rafael Mendonça França*
247
+ *Gannon McGibbon*
203
248
 
204
- * Deprecate `Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality`.
249
+ * Make `http_cache_forever` use `immutable: true`
205
250
 
206
- *Rafael Mendonça França*
251
+ *Nate Matykiewicz*
207
252
 
208
- * Remove deprecated comparison between `ActionController::Parameters` and `Hash`.
253
+ * Add `config.action_dispatch.strict_freshness`.
209
254
 
210
- *Rafael Mendonça França*
255
+ When set to `true`, the `ETag` header takes precedence over the `Last-Modified` header when both are present,
256
+ as specified by RFC 7232, Section 6.
211
257
 
212
- * Remove deprecated constant `AbstractController::Helpers::MissingHelperError`.
258
+ Defaults to `false` to maintain compatibility with previous versions of Rails, but is enabled as part of
259
+ Rails 8.0 defaults.
213
260
 
214
- *Rafael Mendonça França*
261
+ *heka1024*
215
262
 
216
- * Fix a race condition that could cause a `Text file busy - chromedriver`
217
- error with parallel system tests.
263
+ * Support `immutable` directive in Cache-Control
218
264
 
219
- *Matt Brictson*
265
+ ```ruby
266
+ expires_in 1.minute, public: true, immutable: true
267
+ # Cache-Control: public, max-age=60, immutable
268
+ ```
220
269
 
221
- * Add `racc` as a dependency since it will become a bundled gem in Ruby 3.4.0
270
+ *heka1024*
222
271
 
223
- *Hartley McGuire*
224
- * Remove deprecated constant `ActionDispatch::IllegalStateError`.
272
+ * Add `:wasm_unsafe_eval` mapping for `content_security_policy`
225
273
 
226
- *Rafael Mendonça França*
274
+ ```ruby
275
+ # Before
276
+ policy.script_src "'wasm-unsafe-eval'"
277
+
278
+ # After
279
+ policy.script_src :wasm_unsafe_eval
280
+ ```
227
281
 
228
- * Add parameter filter capability for redirect locations.
282
+ *Joe Haig*
229
283
 
230
- It uses the `config.filter_parameters` to match what needs to be filtered.
231
- The result would be like this:
284
+ * Add `display_capture` and `keyboard_map` in `permissions_policy`
232
285
 
233
- Redirected to http://secret.foo.bar?username=roque&password=[FILTERED]
286
+ *Cyril Blaecke*
234
287
 
235
- Fixes #14055.
288
+ * Add `connect` route helper.
236
289
 
237
- *Roque Pinel*, *Trevor Turk*, *tonytonyjan*
290
+ *Samuel Williams*
238
291
 
239
- Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/actionpack/CHANGELOG.md) for previous changes.
292
+ Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/actionpack/CHANGELOG.md) for previous changes.
@@ -5,7 +5,6 @@
5
5
  require "abstract_controller/error"
6
6
  require "action_view"
7
7
  require "action_view/view_paths"
8
- require "set"
9
8
 
10
9
  module AbstractController
11
10
  class DoubleRenderError < Error
@@ -266,7 +266,7 @@ module ActionController
266
266
  ParamsWrapper
267
267
  ]
268
268
 
269
- # Note: Documenting these severely degrates the performance of rdoc
269
+ # Note: Documenting these severely degrades the performance of rdoc
270
270
  # :stopdoc:
271
271
  include AbstractController::Rendering
272
272
  include AbstractController::Translation
@@ -22,10 +22,10 @@ module ActionController
22
22
  # default_form_builder AdminFormBuilder
23
23
  # end
24
24
  #
25
- # Then in the view any form using `form_for` will be an instance of the
26
- # specified form builder:
25
+ # Then in the view any form using `form_with` or `form_for` will be an
26
+ # instance of the specified form builder:
27
27
  #
28
- # <%= form_for(@instance) do |builder| %>
28
+ # <%= form_with(model: @instance) do |builder| %>
29
29
  # <%= builder.special_field(:name) %>
30
30
  # <% end %>
31
31
  module FormBuilder
@@ -36,6 +36,16 @@ module ActionController # :nodoc:
36
36
  # end
37
37
  #
38
38
  # class ApplicationController < ActionController::Base
39
+ # # Allow only browsers natively supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has
40
+ # allow_browser versions: :modern, block: :handle_outdated_browser
41
+ #
42
+ # private
43
+ # def handle_outdated_browser
44
+ # render file: Rails.root.join("public/custom-error.html"), status: :not_acceptable
45
+ # end
46
+ # end
47
+ #
48
+ # class ApplicationController < ActionController::Base
39
49
  # # All versions of Chrome and Opera will be allowed, but no versions of "internet explorer" (ie). Safari needs to be 16.4+ and Firefox 121+.
40
50
  # allow_browser versions: { safari: 16.4, firefox: 121, ie: false }
41
51
  # end
@@ -55,7 +65,7 @@ module ActionController # :nodoc:
55
65
 
56
66
  if BrowserBlocker.new(request, versions: versions).blocked?
57
67
  ActiveSupport::Notifications.instrument("browser_block.action_controller", request: request, versions: versions) do
58
- instance_exec(&block)
68
+ block.is_a?(Symbol) ? send(block) : instance_exec(&block)
59
69
  end
60
70
  end
61
71
  end
@@ -259,6 +259,9 @@ module ActionController
259
259
  # `:stale_if_error`
260
260
  # : Sets the value of the `stale-if-error` directive.
261
261
  #
262
+ # `:immutable`
263
+ # : If true, adds the `immutable` directive.
264
+ #
262
265
  #
263
266
  # Any additional key-value pairs are concatenated as directives. For a list of
264
267
  # supported `Cache-Control` directives, see the [article on
@@ -292,6 +295,7 @@ module ActionController
292
295
  must_revalidate: options.delete(:must_revalidate),
293
296
  stale_while_revalidate: options.delete(:stale_while_revalidate),
294
297
  stale_if_error: options.delete(:stale_if_error),
298
+ immutable: options.delete(:immutable),
295
299
  )
296
300
  options.delete(:private)
297
301
 
@@ -315,7 +319,7 @@ module ActionController
315
319
  # user's web browser. To allow proxies to cache the response, set `true` to
316
320
  # indicate that they can serve the cached response to all users.
317
321
  def http_cache_forever(public: false)
318
- expires_in 100.years, public: public
322
+ expires_in 100.years, public: public, immutable: true
319
323
 
320
324
  yield if stale?(etag: request.fullpath,
321
325
  last_modified: Time.new(2011, 1, 1).utc,
@@ -28,7 +28,8 @@ module ActionController # :nodoc:
28
28
  # `send_file(params[:path])` allows a malicious user to download any file on
29
29
  # your server.
30
30
  #
31
- # Options:
31
+ # #### Options:
32
+ #
32
33
  # * `:filename` - suggests a filename for the browser to use. Defaults to
33
34
  # `File.basename(path)`.
34
35
  # * `:type` - specifies an HTTP content type. You can specify either a string
@@ -90,7 +91,8 @@ module ActionController # :nodoc:
90
91
  # inline data. You may also set the content type, the file name, and other
91
92
  # things.
92
93
  #
93
- # Options:
94
+ # #### Options:
95
+ #
94
96
  # * `:filename` - suggests a filename for the browser to use.
95
97
  # * `:type` - specifies an HTTP content type. Defaults to
96
98
  # `application/octet-stream`. You can specify either a string or a symbol
@@ -2,7 +2,6 @@
2
2
 
3
3
  # :markup: markdown
4
4
 
5
- require "benchmark"
6
5
  require "abstract_controller/logger"
7
6
 
8
7
  module ActionController
@@ -29,7 +28,7 @@ module ActionController
29
28
  def render(*)
30
29
  render_output = nil
31
30
  self.view_runtime = cleanup_view_runtime do
32
- Benchmark.ms { render_output = super }
31
+ ActiveSupport::Benchmark.realtime(:float_millisecond) { render_output = super }
33
32
  end
34
33
  render_output
35
34
  end
@@ -171,12 +171,6 @@ module ActionController
171
171
  @ignore_disconnect = false
172
172
  end
173
173
 
174
- # ActionDispatch::Response delegates #to_ary to the internal
175
- # ActionDispatch::Response::Buffer, defining #to_ary is an indicator that the
176
- # response body can be buffered and/or cached by Rack middlewares, this is not
177
- # the case for Live responses so we undefine it for this Buffer subclass.
178
- undef_method :to_ary
179
-
180
174
  def write(string)
181
175
  unless @response.committed?
182
176
  @response.headers["Cache-Control"] ||= "no-cache"
@@ -307,6 +301,9 @@ module ActionController
307
301
  error = e
308
302
  end
309
303
  ensure
304
+ ActiveSupport::IsolatedExecutionState.clear
305
+ clean_up_thread_locals(locals, t2)
306
+
310
307
  @_response.commit!
311
308
  end
312
309
  end
@@ -328,7 +325,8 @@ module ActionController
328
325
  # or other running data where you don't want the entire file buffered in memory
329
326
  # first. Similar to send_data, but where the data is generated live.
330
327
  #
331
- # Options:
328
+ # #### Options:
329
+ #
332
330
  # * `:filename` - suggests a filename for the browser to use.
333
331
  # * `:type` - specifies an HTTP content type. You can specify either a string
334
332
  # or a symbol for a registered type with `Mime::Type.register`, for example
@@ -371,11 +369,20 @@ module ActionController
371
369
  # data from the response bodies. Nobody should call this method except in Rails
372
370
  # internals. Seriously!
373
371
  def new_controller_thread # :nodoc:
374
- Thread.new {
372
+ ActionController::Live.live_thread_pool_executor.post do
375
373
  t2 = Thread.current
376
374
  t2.abort_on_exception = true
377
375
  yield
378
- }
376
+ end
377
+ end
378
+
379
+ # Ensure we clean up any thread locals we copied so that the thread can reused.
380
+ def clean_up_thread_locals(locals, thread) # :nodoc:
381
+ locals.each { |k, _| thread[k] = nil }
382
+ end
383
+
384
+ def self.live_thread_pool_executor
385
+ @live_thread_pool_executor ||= Concurrent::CachedThreadPool.new(name: "action_controller.live")
379
386
  end
380
387
 
381
388
  def log_error(exception)
@@ -29,6 +29,9 @@ module ActionController # :nodoc:
29
29
  # datastore as your general caches, you can pass a custom store in the `store`
30
30
  # parameter.
31
31
  #
32
+ # If you want to use multiple rate limits per controller, you need to give each of
33
+ # them an explicit name via the `name:` option.
34
+ #
32
35
  # Examples:
33
36
  #
34
37
  # class SessionsController < ApplicationController
@@ -44,14 +47,20 @@ module ActionController # :nodoc:
44
47
  # RATE_LIMIT_STORE = ActiveSupport::Cache::RedisCacheStore.new(url: ENV["REDIS_URL"])
45
48
  # rate_limit to: 10, within: 3.minutes, store: RATE_LIMIT_STORE
46
49
  # end
47
- def rate_limit(to:, within:, by: -> { request.remote_ip }, with: -> { head :too_many_requests }, store: cache_store, **options)
48
- before_action -> { rate_limiting(to: to, within: within, by: by, with: with, store: store) }, **options
50
+ #
51
+ # class SessionsController < ApplicationController
52
+ # rate_limit to: 3, within: 2.seconds, name: "short-term"
53
+ # rate_limit to: 10, within: 5.minutes, name: "long-term"
54
+ # end
55
+ def rate_limit(to:, within:, by: -> { request.remote_ip }, with: -> { head :too_many_requests }, store: cache_store, name: nil, **options)
56
+ before_action -> { rate_limiting(to: to, within: within, by: by, with: with, store: store, name: name) }, **options
49
57
  end
50
58
  end
51
59
 
52
60
  private
53
- def rate_limiting(to:, within:, by:, with:, store:)
54
- count = store.increment("rate-limit:#{controller_path}:#{instance_exec(&by)}", 1, expires_in: within)
61
+ def rate_limiting(to:, within:, by:, with:, store:, name:)
62
+ cache_key = ["rate-limit", controller_path, name, instance_exec(&by)].compact.join(":")
63
+ count = store.increment(cache_key, 1, expires_in: within)
55
64
  if count && count > to
56
65
  ActiveSupport::Notifications.instrument("rate_limit.action_controller", request: request) do
57
66
  instance_exec(&with)