actionpack 7.2.1.1 → 8.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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]
|