actionpack 7.2.1.1 → 8.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +108 -100
- data/lib/abstract_controller/helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +0 -1
- data/lib/action_controller/api.rb +1 -0
- data/lib/action_controller/form_builder.rb +3 -3
- data/lib/action_controller/metal/allow_browser.rb +12 -2
- data/lib/action_controller/metal/conditional_get.rb +6 -3
- data/lib/action_controller/metal/http_authentication.rb +6 -3
- data/lib/action_controller/metal/instrumentation.rb +1 -2
- data/lib/action_controller/metal/live.rb +19 -8
- data/lib/action_controller/metal/rate_limiting.rb +13 -4
- data/lib/action_controller/metal/renderers.rb +2 -3
- data/lib/action_controller/metal/streaming.rb +5 -84
- data/lib/action_controller/metal/strong_parameters.rb +274 -88
- data/lib/action_controller/railtie.rb +1 -7
- data/lib/action_controller/test_case.rb +6 -5
- data/lib/action_dispatch/http/cache.rb +27 -10
- data/lib/action_dispatch/http/content_security_policy.rb +5 -8
- data/lib/action_dispatch/http/filter_parameters.rb +4 -9
- data/lib/action_dispatch/http/filter_redirect.rb +2 -9
- data/lib/action_dispatch/http/param_builder.rb +163 -0
- data/lib/action_dispatch/http/param_error.rb +26 -0
- data/lib/action_dispatch/http/permissions_policy.rb +2 -0
- data/lib/action_dispatch/http/query_parser.rb +31 -0
- data/lib/action_dispatch/http/request.rb +60 -16
- data/lib/action_dispatch/journey/parser.rb +99 -196
- data/lib/action_dispatch/journey/scanner.rb +40 -42
- data/lib/action_dispatch/middleware/cookies.rb +4 -2
- data/lib/action_dispatch/middleware/debug_exceptions.rb +16 -3
- data/lib/action_dispatch/middleware/debug_view.rb +0 -5
- data/lib/action_dispatch/middleware/exception_wrapper.rb +0 -6
- data/lib/action_dispatch/middleware/remote_ip.rb +5 -6
- data/lib/action_dispatch/middleware/request_id.rb +2 -1
- data/lib/action_dispatch/middleware/ssl.rb +14 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +0 -3
- data/lib/action_dispatch/railtie.rb +2 -0
- data/lib/action_dispatch/request/utils.rb +9 -3
- data/lib/action_dispatch/routing/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +91 -62
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -2
- data/lib/action_dispatch/routing/route_set.rb +20 -8
- data/lib/action_dispatch/system_testing/browser.rb +12 -21
- data/lib/action_dispatch/testing/assertions/response.rb +12 -2
- data/lib/action_dispatch/testing/assertions/routing.rb +4 -4
- data/lib/action_dispatch/testing/integration.rb +11 -1
- data/lib/action_dispatch.rb +6 -0
- data/lib/action_pack/gem_version.rb +4 -4
- metadata +15 -34
- data/lib/action_dispatch/journey/parser.y +0 -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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbf30070e8c7658bcda189e9c07184b10bfdeb1ae28ae9b7c6e354189e96eac6
|
4
|
+
data.tar.gz: c21576442cf2c2e3ef1cf7e36f5e412349b6f933c651cca641a9afe48229cd96
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d09731d99912ded6338f7a0fcc0d98706efbf4721f26d35edcede064e240d607f6ddc5ed43a979ebe3c8d6c1e9b90347d84725c6b504c91c79fb9821edca478
|
7
|
+
data.tar.gz: 5b981e0db05e7d35cda56797acbf513050fd099bb3778ec2247012e1a338dec2b1fe608a6653a4dbe54c621de63f576588b98b8d77b172f9456b78e2e9a7cd9b
|
data/CHANGELOG.md
CHANGED
@@ -1,164 +1,172 @@
|
|
1
|
-
## Rails
|
1
|
+
## Rails 8.0.0.rc1 (October 19, 2024) ##
|
2
2
|
|
3
|
-
*
|
3
|
+
* Remove `Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality`.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
* Avoid regex backtracking in query parameter filtering
|
5
|
+
*Rafael Mendonça França*
|
8
6
|
|
9
|
-
|
7
|
+
* Improve `ActionController::TestCase` to expose a binary encoded `request.body`.
|
10
8
|
|
11
|
-
|
9
|
+
The rack spec clearly states:
|
12
10
|
|
13
|
-
|
11
|
+
> The input stream is an IO-like object which contains the raw HTTP POST data.
|
12
|
+
> When applicable, its external encoding must be “ASCII-8BIT” and it must be opened in binary mode.
|
14
13
|
|
15
|
-
|
14
|
+
Until now its encoding was generally UTF-8, which doesn't accurately reflect production
|
15
|
+
behavior.
|
16
16
|
|
17
|
+
*Jean Boussier*
|
17
18
|
|
18
|
-
|
19
|
+
* Update `ActionController::AllowBrowser` to support passing method names to `:block`
|
19
20
|
|
20
|
-
|
21
|
+
```ruby
|
22
|
+
class ApplicationController < ActionController::Base
|
23
|
+
allow_browser versions: :modern, block: :handle_outdated_browser
|
21
24
|
|
22
|
-
|
25
|
+
private
|
26
|
+
def handle_outdated_browser
|
27
|
+
render file: Rails.root.join("public/custom-error.html"), status: :not_acceptable
|
28
|
+
end
|
29
|
+
end
|
30
|
+
```
|
23
31
|
|
24
|
-
*
|
25
|
-
[CVE-2024-28103]
|
32
|
+
*Sean Doyle*
|
26
33
|
|
27
|
-
|
34
|
+
* Raise an `ArgumentError` when invalid `:only` or `:except` options are passed into `#resource` and `#resources`.
|
28
35
|
|
29
|
-
*
|
36
|
+
*Joshua Young*
|
30
37
|
|
31
|
-
|
38
|
+
## Rails 8.0.0.beta1 (September 26, 2024) ##
|
32
39
|
|
33
|
-
* Fix
|
34
|
-
suggested correct location for the missing template.
|
40
|
+
* Fix non-GET requests not updating cookies in `ActionController::TestCase`.
|
35
41
|
|
36
|
-
*
|
42
|
+
*Jon Moss*, *Hartley McGuire*
|
37
43
|
|
38
|
-
*
|
44
|
+
* Update `ActionController::Live` to use a thread-pool to reuse threads across requests.
|
39
45
|
|
40
|
-
|
41
|
-
A similar helper with matching signature has been added to integration tests.
|
46
|
+
*Adam Renberg Tamm*
|
42
47
|
|
43
|
-
|
48
|
+
* Introduce safer, more explicit params handling method with `params#expect` such that
|
49
|
+
`params.expect(table: [ :attr ])` replaces `params.require(:table).permit(:attr)`
|
44
50
|
|
45
|
-
|
51
|
+
Ensures params are filtered with consideration for the expected
|
52
|
+
types of values, improving handling of params and avoiding ignorable
|
53
|
+
errors caused by params tampering.
|
46
54
|
|
47
55
|
```ruby
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
56
|
+
# If the url is altered to ?person=hacked
|
57
|
+
# Before
|
58
|
+
params.require(:person).permit(:name, :age, pets: [:name])
|
59
|
+
# raises NoMethodError, causing a 500 and potential error reporting
|
60
|
+
|
61
|
+
# After
|
62
|
+
params.expect(person: [ :name, :age, pets: [[:name]] ])
|
63
|
+
# raises ActionController::ParameterMissing, correctly returning a 400 error
|
53
64
|
```
|
54
65
|
|
55
|
-
|
66
|
+
You may also notice the new double array `[[:name]]`. In order to
|
67
|
+
declare when a param is expected to be an array of parameter hashes,
|
68
|
+
this new double array syntax is used to explicitly declare an array.
|
69
|
+
`expect` requires you to declare expected arrays in this way, and will
|
70
|
+
ignore arrays that are passed when, for example, `pet: [:name]` is used.
|
56
71
|
|
57
|
-
|
72
|
+
In order to preserve compatibility, `permit` does not adopt the new
|
73
|
+
double array syntax and is therefore more permissive about unexpected
|
74
|
+
types. Using `expect` everywhere is recommended.
|
58
75
|
|
59
|
-
|
76
|
+
We suggest replacing `params.require(:person).permit(:name, :age)`
|
77
|
+
with the direct replacement `params.expect(person: [:name, :age])`
|
78
|
+
to prevent external users from manipulating params to trigger 500
|
79
|
+
errors. A 400 error will be returned instead, using public/400.html
|
60
80
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
* Add `allow_browser` to set minimum browser versions for the application.
|
66
|
-
|
67
|
-
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".
|
81
|
+
Usage of `params.require(:id)` should likewise be replaced with
|
82
|
+
`params.expect(:id)` which is designed to ensure that `params[:id]`
|
83
|
+
is a scalar and not an array or hash, also requiring the param.
|
68
84
|
|
69
85
|
```ruby
|
70
|
-
|
71
|
-
|
72
|
-
allow_browser versions: :modern
|
73
|
-
end
|
74
|
-
|
75
|
-
class ApplicationController < ActionController::Base
|
76
|
-
# 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+.
|
77
|
-
allow_browser versions: { safari: 16.4, firefox: 121, ie: false }
|
78
|
-
end
|
86
|
+
# Before
|
87
|
+
User.find(params.require(:id)) # allows an array, altering behavior
|
79
88
|
|
80
|
-
|
81
|
-
|
82
|
-
allow_browser versions: { opera: 104, chrome: 119 }, only: :show
|
83
|
-
end
|
89
|
+
# After
|
90
|
+
User.find(params.expect(:id)) # expect only returns non-blank permitted scalars (excludes Hash, Array, nil, "", etc)
|
84
91
|
```
|
85
92
|
|
86
|
-
*
|
87
|
-
|
88
|
-
* Add rate limiting API.
|
89
|
-
|
90
|
-
```ruby
|
91
|
-
class SessionsController < ApplicationController
|
92
|
-
rate_limit to: 10, within: 3.minutes, only: :create
|
93
|
-
end
|
94
|
-
|
95
|
-
class SignupsController < ApplicationController
|
96
|
-
rate_limit to: 1000, within: 10.seconds,
|
97
|
-
by: -> { request.domain }, with: -> { redirect_to busy_controller_url, alert: "Too many signups!" }, only: :new
|
98
|
-
end
|
99
|
-
```
|
93
|
+
*Martin Emde*
|
100
94
|
|
101
|
-
|
95
|
+
* System Testing: Disable Chrome's search engine choice by default in system tests.
|
102
96
|
|
103
|
-
*
|
97
|
+
*glaszig*
|
104
98
|
|
105
|
-
|
99
|
+
* Fix `Request#raw_post` raising `NoMethodError` when `rack.input` is `nil`.
|
106
100
|
|
107
|
-
*
|
101
|
+
*Hartley McGuire*
|
108
102
|
|
109
|
-
|
103
|
+
* Remove `racc` dependency by manually writing `ActionDispatch::Journey::Scanner`.
|
110
104
|
|
111
|
-
*
|
105
|
+
*Gannon McGibbon*
|
112
106
|
|
113
|
-
*
|
107
|
+
* Speed up `ActionDispatch::Routing::Mapper::Scope#[]` by merging frame hashes.
|
114
108
|
|
115
109
|
*Gannon McGibbon*
|
116
110
|
|
117
|
-
*
|
111
|
+
* Allow bots to ignore `allow_browser`.
|
118
112
|
|
119
|
-
*
|
113
|
+
*Matthew Nguyen*
|
120
114
|
|
121
|
-
*
|
115
|
+
* Deprecate drawing routes with multiple paths to make routing faster.
|
116
|
+
You may use `with_options` or a loop to make drawing multiple paths easier.
|
122
117
|
|
123
|
-
|
118
|
+
```ruby
|
119
|
+
# Before
|
120
|
+
get "/users", "/other_path", to: "users#index"
|
124
121
|
|
125
|
-
|
122
|
+
# After
|
123
|
+
get "/users", to: "users#index"
|
124
|
+
get "/other_path", to: "users#index"
|
125
|
+
```
|
126
126
|
|
127
|
-
*
|
127
|
+
*Gannon McGibbon*
|
128
128
|
|
129
|
-
*
|
129
|
+
* Make `http_cache_forever` use `immutable: true`
|
130
130
|
|
131
|
-
*
|
131
|
+
*Nate Matykiewicz*
|
132
132
|
|
133
|
-
*
|
133
|
+
* Add `config.action_dispatch.strict_freshness`.
|
134
134
|
|
135
|
-
|
135
|
+
When set to `true`, the `ETag` header takes precedence over the `Last-Modified` header when both are present,
|
136
|
+
as specified by RFC 7232, Section 6.
|
136
137
|
|
137
|
-
|
138
|
+
Defaults to `false` to maintain compatibility with previous versions of Rails, but is enabled as part of
|
139
|
+
Rails 8.0 defaults.
|
138
140
|
|
139
|
-
*
|
141
|
+
*heka1024*
|
142
|
+
|
143
|
+
* Support `immutable` directive in Cache-Control
|
140
144
|
|
141
|
-
|
142
|
-
|
145
|
+
```ruby
|
146
|
+
expires_in 1.minute, public: true, immutable: true
|
147
|
+
# Cache-Control: public, max-age=60, immutable
|
148
|
+
```
|
143
149
|
|
144
|
-
*
|
150
|
+
*heka1024*
|
145
151
|
|
146
|
-
* Add `
|
152
|
+
* Add `:wasm_unsafe_eval` mapping for `content_security_policy`
|
147
153
|
|
148
|
-
|
149
|
-
|
154
|
+
```ruby
|
155
|
+
# Before
|
156
|
+
policy.script_src "'wasm-unsafe-eval'"
|
150
157
|
|
151
|
-
|
158
|
+
# After
|
159
|
+
policy.script_src :wasm_unsafe_eval
|
160
|
+
```
|
152
161
|
|
153
|
-
*
|
162
|
+
*Joe Haig*
|
154
163
|
|
155
|
-
|
156
|
-
The result would be like this:
|
164
|
+
* Add `display_capture` and `keyboard_map` in `permissions_policy`
|
157
165
|
|
158
|
-
|
166
|
+
*Cyril Blaecke*
|
159
167
|
|
160
|
-
|
168
|
+
* Add `connect` route helper.
|
161
169
|
|
162
|
-
*
|
170
|
+
*Samuel Williams*
|
163
171
|
|
164
|
-
Please check [7-
|
172
|
+
Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/actionpack/CHANGELOG.md) for previous changes.
|
@@ -104,6 +104,7 @@ module AbstractController
|
|
104
104
|
# Declare a controller method as a helper. For example, the following
|
105
105
|
# makes the `current_user` and `logged_in?` controller methods available
|
106
106
|
# to the view:
|
107
|
+
#
|
107
108
|
# class ApplicationController < ActionController::Base
|
108
109
|
# helper_method :current_user, :logged_in?
|
109
110
|
#
|
@@ -118,6 +119,7 @@ module AbstractController
|
|
118
119
|
# end
|
119
120
|
#
|
120
121
|
# In a view:
|
122
|
+
#
|
121
123
|
# <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
|
122
124
|
#
|
123
125
|
# #### Parameters
|
@@ -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
|
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
|
-
# <%=
|
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,12 +65,12 @@ 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
|
62
72
|
|
63
|
-
class BrowserBlocker
|
73
|
+
class BrowserBlocker # :nodoc:
|
64
74
|
SETS = {
|
65
75
|
modern: { safari: 17.2, chrome: 120, firefox: 121, opera: 106, ie: false }
|
66
76
|
}
|
@@ -76,8 +76,7 @@ module ActionController
|
|
76
76
|
# `:cache_control`
|
77
77
|
# : When given, will overwrite an existing `Cache-Control` header. For a list
|
78
78
|
# of `Cache-Control` directives, see the [article on
|
79
|
-
# MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-
|
80
|
-
# ol).
|
79
|
+
# MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control).
|
81
80
|
#
|
82
81
|
# `:template`
|
83
82
|
# : By default, the template digest for the current controller/action is
|
@@ -260,6 +259,9 @@ module ActionController
|
|
260
259
|
# `:stale_if_error`
|
261
260
|
# : Sets the value of the `stale-if-error` directive.
|
262
261
|
#
|
262
|
+
# `:immutable`
|
263
|
+
# : If true, adds the `immutable` directive.
|
264
|
+
#
|
263
265
|
#
|
264
266
|
# Any additional key-value pairs are concatenated as directives. For a list of
|
265
267
|
# supported `Cache-Control` directives, see the [article on
|
@@ -293,6 +295,7 @@ module ActionController
|
|
293
295
|
must_revalidate: options.delete(:must_revalidate),
|
294
296
|
stale_while_revalidate: options.delete(:stale_while_revalidate),
|
295
297
|
stale_if_error: options.delete(:stale_if_error),
|
298
|
+
immutable: options.delete(:immutable),
|
296
299
|
)
|
297
300
|
options.delete(:private)
|
298
301
|
|
@@ -316,7 +319,7 @@ module ActionController
|
|
316
319
|
# user's web browser. To allow proxies to cache the response, set `true` to
|
317
320
|
# indicate that they can serve the cached response to all users.
|
318
321
|
def http_cache_forever(public: false)
|
319
|
-
expires_in 100.years, public: public
|
322
|
+
expires_in 100.years, public: public, immutable: true
|
320
323
|
|
321
324
|
yield if stale?(etag: request.fullpath,
|
322
325
|
last_modified: Time.new(2011, 1, 1).utc,
|
@@ -211,7 +211,7 @@ module ActionController
|
|
211
211
|
end
|
212
212
|
end
|
213
213
|
|
214
|
-
# Returns
|
214
|
+
# Returns true on a valid response, false otherwise.
|
215
215
|
def authenticate(request, realm, &password_procedure)
|
216
216
|
request.authorization && validate_digest_response(request, realm, &password_procedure)
|
217
217
|
end
|
@@ -431,7 +431,7 @@ module ActionController
|
|
431
431
|
module ControllerMethods
|
432
432
|
# Authenticate using an HTTP Bearer token, or otherwise render an HTTP header
|
433
433
|
# requesting the client to send a Bearer token. For the authentication to be
|
434
|
-
# considered successful, `login_procedure`
|
434
|
+
# considered successful, `login_procedure` must not return a false value.
|
435
435
|
# Typically, the authenticated user is returned.
|
436
436
|
#
|
437
437
|
# See ActionController::HttpAuthentication::Token for example usage.
|
@@ -513,11 +513,14 @@ module ActionController
|
|
513
513
|
array_params.each { |param| (param[1] || +"").gsub! %r/^"|"$/, "" }
|
514
514
|
end
|
515
515
|
|
516
|
+
WHITESPACED_AUTHN_PAIR_DELIMITERS = /\s*#{AUTHN_PAIR_DELIMITERS}\s*/
|
517
|
+
private_constant :WHITESPACED_AUTHN_PAIR_DELIMITERS
|
518
|
+
|
516
519
|
# This method takes an authorization body and splits up the key-value pairs by
|
517
520
|
# the standardized `:`, `;`, or `\t` delimiters defined in
|
518
521
|
# `AUTHN_PAIR_DELIMITERS`.
|
519
522
|
def raw_params(auth)
|
520
|
-
_raw_params = auth.sub(TOKEN_REGEX, "").split(
|
523
|
+
_raw_params = auth.sub(TOKEN_REGEX, "").split(WHITESPACED_AUTHN_PAIR_DELIMITERS)
|
521
524
|
_raw_params.reject!(&:empty?)
|
522
525
|
|
523
526
|
if !_raw_params.first&.start_with?(TOKEN_KEY)
|
@@ -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.
|
31
|
+
ActiveSupport::Benchmark.realtime(:float_millisecond) { render_output = super }
|
33
32
|
end
|
34
33
|
render_output
|
35
34
|
end
|
@@ -77,12 +77,15 @@ module ActionController
|
|
77
77
|
# Writing an object will convert it into standard SSE format with whatever
|
78
78
|
# options you have configured. You may choose to set the following options:
|
79
79
|
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
80
|
+
# `:event`
|
81
|
+
# : If specified, an event with this name will be dispatched on the browser.
|
82
|
+
#
|
83
|
+
# `:retry`
|
84
|
+
# : The reconnection time in milliseconds used when attempting to send the event.
|
85
|
+
#
|
86
|
+
# `:id`
|
87
|
+
# : If the connection dies while sending an SSE to the browser, then the
|
88
|
+
# server will receive a `Last-Event-ID` header with value equal to `id`.
|
86
89
|
#
|
87
90
|
# After setting an option in the constructor of the SSE object, all future SSEs
|
88
91
|
# sent across the stream will use those options unless overridden.
|
@@ -304,6 +307,10 @@ module ActionController
|
|
304
307
|
error = e
|
305
308
|
end
|
306
309
|
ensure
|
310
|
+
# Ensure we clean up any thread locals we copied so that the thread can reused.
|
311
|
+
ActiveSupport::IsolatedExecutionState.clear
|
312
|
+
locals.each { |k, _| t2[k] = nil }
|
313
|
+
|
307
314
|
@_response.commit!
|
308
315
|
end
|
309
316
|
end
|
@@ -368,11 +375,15 @@ module ActionController
|
|
368
375
|
# data from the response bodies. Nobody should call this method except in Rails
|
369
376
|
# internals. Seriously!
|
370
377
|
def new_controller_thread # :nodoc:
|
371
|
-
|
378
|
+
ActionController::Live.live_thread_pool_executor.post do
|
372
379
|
t2 = Thread.current
|
373
380
|
t2.abort_on_exception = true
|
374
381
|
yield
|
375
|
-
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def self.live_thread_pool_executor
|
386
|
+
@live_thread_pool_executor ||= Concurrent::CachedThreadPool.new(name: "action_controller.live")
|
376
387
|
end
|
377
388
|
|
378
389
|
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
|
-
|
48
|
-
|
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
|
-
|
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)
|
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
# :markup: markdown
|
4
4
|
|
5
|
-
require "set"
|
6
|
-
|
7
5
|
module ActionController
|
8
6
|
# See Renderers.add
|
9
7
|
def self.add_renderer(key, &block)
|
@@ -154,7 +152,8 @@ module ActionController
|
|
154
152
|
end
|
155
153
|
|
156
154
|
add :json do |json, options|
|
157
|
-
|
155
|
+
json_options = options.except(:callback, :content_type, :status)
|
156
|
+
json = json.to_json(json_options) unless json.kind_of?(String)
|
158
157
|
|
159
158
|
if options[:callback].present?
|
160
159
|
if media_type.nil? || media_type == Mime[:json]
|