actionpack 7.2.1.1 → 8.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +86 -115
- data/lib/action_controller/api.rb +1 -0
- 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 -1
- data/lib/action_controller/metal/streaming.rb +5 -84
- data/lib/action_controller/metal/strong_parameters.rb +274 -73
- data/lib/action_controller/railtie.rb +1 -1
- data/lib/action_controller/test_case.rb +4 -3
- data/lib/action_dispatch/http/cache.rb +27 -10
- data/lib/action_dispatch/http/content_security_policy.rb +1 -0
- 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/permissions_policy.rb +2 -0
- data/lib/action_dispatch/http/request.rb +4 -2
- 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/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/railtie.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +26 -17
- data/lib/action_dispatch/routing/route_set.rb +18 -6
- data/lib/action_dispatch/system_testing/browser.rb +12 -21
- data/lib/action_pack/gem_version.rb +4 -4
- metadata +12 -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: 4392b60cf6c4af7ed943daf2c4e438440689b8f869a859510b1e0e5808204cf2
|
4
|
+
data.tar.gz: ee9993b25b397af527f1730dc439a0425a81d4b03afb148e310dac8862003261
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2063ad6ca3243b066226af6a1d1ad616d808c48a18f80df374d02b064ad4839a971e4eff804321621d910111829b0c1130b5a05f5a44afabb380ec9f7d5985b7
|
7
|
+
data.tar.gz: f3f1e1ce84c47654f69cf3f3ada2afa48e412dac1b7a5abd10c4d0fc34b66b7574f2454410c323cfa26a000005da585815db8a27dc3f1e9c2a71a504b0f3021d
|
data/CHANGELOG.md
CHANGED
@@ -1,164 +1,135 @@
|
|
1
|
-
## Rails
|
1
|
+
## Rails 8.0.0.beta1 (September 26, 2024) ##
|
2
2
|
|
3
|
-
*
|
3
|
+
* Fix non-GET requests not updating cookies in `ActionController::TestCase`.
|
4
4
|
|
5
|
-
|
5
|
+
*Jon Moss*, *Hartley McGuire*
|
6
6
|
|
7
|
-
*
|
7
|
+
* Update `ActionController::Live` to use a thread-pool to reuse threads across requests.
|
8
8
|
|
9
|
-
|
9
|
+
*Adam Renberg Tamm*
|
10
10
|
|
11
|
-
|
11
|
+
* Introduce safer, more explicit params handling method with `params#expect` such that
|
12
|
+
`params.expect(table: [ :attr ])` replaces `params.require(:table).permit(:attr)`
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
## Rails 7.2.0 (August 09, 2024) ##
|
19
|
-
|
20
|
-
* Allow bots to ignore `allow_browser`.
|
21
|
-
|
22
|
-
*Matthew Nguyen*
|
23
|
-
|
24
|
-
* Include the HTTP Permissions-Policy on non-HTML Content-Types
|
25
|
-
[CVE-2024-28103]
|
26
|
-
|
27
|
-
*Aaron Patterson*, *Zack Deveau*
|
28
|
-
|
29
|
-
* Fix `Mime::Type.parse` handling type parameters for HTTP Accept headers.
|
30
|
-
|
31
|
-
*Taylor Chaparro*
|
32
|
-
|
33
|
-
* Fix the error page that is displayed when a view template is missing to account for nested controller paths in the
|
34
|
-
suggested correct location for the missing template.
|
35
|
-
|
36
|
-
*Joshua Young*
|
37
|
-
|
38
|
-
* Add `save_and_open_page` helper to `IntegrationTest`.
|
39
|
-
|
40
|
-
`save_and_open_page` is a helpful helper to keep a short feedback loop when working on system tests.
|
41
|
-
A similar helper with matching signature has been added to integration tests.
|
42
|
-
|
43
|
-
*Joé Dupuis*
|
44
|
-
|
45
|
-
* Fix a regression in 7.1.3 passing a `to:` option without a controller when the controller is already defined by a scope.
|
14
|
+
Ensures params are filtered with consideration for the expected
|
15
|
+
types of values, improving handling of params and avoiding ignorable
|
16
|
+
errors caused by params tampering.
|
46
17
|
|
47
18
|
```ruby
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
19
|
+
# If the url is altered to ?person=hacked
|
20
|
+
# Before
|
21
|
+
params.require(:person).permit(:name, :age, pets: [:name])
|
22
|
+
# raises NoMethodError, causing a 500 and potential error reporting
|
23
|
+
|
24
|
+
# After
|
25
|
+
params.expect(person: [ :name, :age, pets: [[:name]] ])
|
26
|
+
# raises ActionController::ParameterMissing, correctly returning a 400 error
|
53
27
|
```
|
54
28
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
29
|
+
You may also notice the new double array `[[:name]]`. In order to
|
30
|
+
declare when a param is expected to be an array of parameter hashes,
|
31
|
+
this new double array syntax is used to explicitly declare an array.
|
32
|
+
`expect` requires you to declare expected arrays in this way, and will
|
33
|
+
ignore arrays that are passed when, for example, `pet: [:name]` is used.
|
60
34
|
|
61
|
-
|
35
|
+
In order to preserve compatibility, `permit` does not adopt the new
|
36
|
+
double array syntax and is therefore more permissive about unexpected
|
37
|
+
types. Using `expect` everywhere is recommended.
|
62
38
|
|
63
|
-
|
39
|
+
We suggest replacing `params.require(:person).permit(:name, :age)`
|
40
|
+
with the direct replacement `params.expect(person: [:name, :age])`
|
41
|
+
to prevent external users from manipulating params to trigger 500
|
42
|
+
errors. A 400 error will be returned instead, using public/400.html
|
64
43
|
|
65
|
-
|
66
|
-
|
67
|
-
|
44
|
+
Usage of `params.require(:id)` should likewise be replaced with
|
45
|
+
`params.expect(:id)` which is designed to ensure that `params[:id]`
|
46
|
+
is a scalar and not an array or hash, also requiring the param.
|
68
47
|
|
69
48
|
```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
|
79
|
-
|
80
|
-
class MessagesController < ApplicationController
|
81
|
-
# In addition to the browsers blocked by ApplicationController, also block Opera below 104 and Chrome below 119 for the show action.
|
82
|
-
allow_browser versions: { opera: 104, chrome: 119 }, only: :show
|
83
|
-
end
|
84
|
-
```
|
85
|
-
|
86
|
-
*DHH*
|
49
|
+
# Before
|
50
|
+
User.find(params.require(:id)) # allows an array, altering behavior
|
87
51
|
|
88
|
-
|
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
|
52
|
+
# After
|
53
|
+
User.find(params.expect(:id)) # expect only returns non-blank permitted scalars (excludes Hash, Array, nil, "", etc)
|
99
54
|
```
|
100
55
|
|
101
|
-
*
|
56
|
+
*Martin Emde*
|
102
57
|
|
103
|
-
*
|
58
|
+
* System Testing: Disable Chrome's search engine choice by default in system tests.
|
104
59
|
|
105
|
-
*
|
60
|
+
*glaszig*
|
106
61
|
|
107
|
-
*
|
62
|
+
* Fix `Request#raw_post` raising `NoMethodError` when `rack.input` is `nil`.
|
108
63
|
|
109
|
-
|
64
|
+
*Hartley McGuire*
|
110
65
|
|
111
|
-
|
66
|
+
* Remove `racc` dependency by manually writing `ActionDispatch::Journey::Scanner`.
|
112
67
|
|
113
|
-
*
|
68
|
+
*Gannon McGibbon*
|
69
|
+
|
70
|
+
* Speed up `ActionDispatch::Routing::Mapper::Scope#[]` by merging frame hashes.
|
114
71
|
|
115
72
|
*Gannon McGibbon*
|
116
73
|
|
117
|
-
*
|
74
|
+
* Allow bots to ignore `allow_browser`.
|
75
|
+
|
76
|
+
*Matthew Nguyen*
|
118
77
|
|
119
|
-
|
78
|
+
* Deprecate drawing routes with multiple paths to make routing faster.
|
79
|
+
You may use `with_options` or a loop to make drawing multiple paths easier.
|
120
80
|
|
121
|
-
|
81
|
+
```ruby
|
82
|
+
# Before
|
83
|
+
get "/users", "/other_path", to: "users#index"
|
122
84
|
|
123
|
-
|
85
|
+
# After
|
86
|
+
get "/users", to: "users#index"
|
87
|
+
get "/other_path", to: "users#index"
|
88
|
+
```
|
124
89
|
|
125
|
-
*
|
90
|
+
*Gannon McGibbon*
|
126
91
|
|
127
|
-
|
92
|
+
* Make `http_cache_forever` use `immutable: true`
|
128
93
|
|
129
|
-
*
|
94
|
+
*Nate Matykiewicz*
|
130
95
|
|
131
|
-
|
96
|
+
* Add `config.action_dispatch.strict_freshness`.
|
132
97
|
|
133
|
-
|
98
|
+
When set to `true`, the `ETag` header takes precedence over the `Last-Modified` header when both are present,
|
99
|
+
as specified by RFC 7232, Section 6.
|
134
100
|
|
135
|
-
|
101
|
+
Defaults to `false` to maintain compatibility with previous versions of Rails, but is enabled as part of
|
102
|
+
Rails 8.0 defaults.
|
136
103
|
|
137
|
-
*
|
104
|
+
*heka1024*
|
138
105
|
|
139
|
-
|
106
|
+
* Support `immutable` directive in Cache-Control
|
140
107
|
|
141
|
-
|
142
|
-
|
108
|
+
```ruby
|
109
|
+
expires_in 1.minute, public: true, immutable: true
|
110
|
+
# Cache-Control: public, max-age=60, immutable
|
111
|
+
```
|
143
112
|
|
144
|
-
*
|
113
|
+
*heka1024*
|
145
114
|
|
146
|
-
* Add `
|
115
|
+
* Add `:wasm_unsafe_eval` mapping for `content_security_policy`
|
147
116
|
|
148
|
-
|
149
|
-
|
117
|
+
```ruby
|
118
|
+
# Before
|
119
|
+
policy.script_src "'wasm-unsafe-eval'"
|
150
120
|
|
151
|
-
|
121
|
+
# After
|
122
|
+
policy.script_src :wasm_unsafe_eval
|
123
|
+
```
|
152
124
|
|
153
|
-
*
|
125
|
+
*Joe Haig*
|
154
126
|
|
155
|
-
|
156
|
-
The result would be like this:
|
127
|
+
* Add `display_capture` and `keyboard_map` in `permissions_policy`
|
157
128
|
|
158
|
-
|
129
|
+
*Cyril Blaecke*
|
159
130
|
|
160
|
-
|
131
|
+
* Add `connect` route helper.
|
161
132
|
|
162
|
-
*
|
133
|
+
*Samuel Williams*
|
163
134
|
|
164
|
-
Please check [7-
|
135
|
+
Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/actionpack/CHANGELOG.md) for previous changes.
|
@@ -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 and 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)
|
@@ -154,7 +154,8 @@ module ActionController
|
|
154
154
|
end
|
155
155
|
|
156
156
|
add :json do |json, options|
|
157
|
-
|
157
|
+
json_options = options.except(:callback, :content_type, :status)
|
158
|
+
json = json.to_json(json_options) unless json.kind_of?(String)
|
158
159
|
|
159
160
|
if options[:callback].present?
|
160
161
|
if media_type.nil? || media_type == Mime[:json]
|
@@ -165,95 +165,16 @@ module ActionController # :nodoc:
|
|
165
165
|
#
|
166
166
|
# ## Web server support
|
167
167
|
#
|
168
|
-
#
|
169
|
-
# instructions for each of them.
|
170
|
-
#
|
171
|
-
# #### Unicorn
|
172
|
-
#
|
173
|
-
# Unicorn supports streaming but it needs to be configured. For this, you need
|
174
|
-
# to create a config file as follow:
|
175
|
-
#
|
176
|
-
# # unicorn.config.rb
|
177
|
-
# listen 3000, tcp_nopush: false
|
178
|
-
#
|
179
|
-
# And use it on initialization:
|
180
|
-
#
|
181
|
-
# unicorn_rails --config-file unicorn.config.rb
|
182
|
-
#
|
183
|
-
# You may also want to configure other parameters like `:tcp_nodelay`.
|
184
|
-
#
|
185
|
-
# For more information, please check the
|
186
|
-
# [documentation](https://bogomips.org/unicorn/Unicorn/Configurator.html#method-
|
187
|
-
# i-listen).
|
188
|
-
#
|
189
|
-
# If you are using Unicorn with NGINX, you may need to tweak NGINX. Streaming
|
190
|
-
# should work out of the box on Rainbows.
|
191
|
-
#
|
192
|
-
# #### Passenger
|
193
|
-
#
|
194
|
-
# Phusion Passenger with NGINX, offers two streaming mechanisms out of the box.
|
195
|
-
#
|
196
|
-
# 1. NGINX response buffering mechanism which is dependent on the value of
|
197
|
-
# `passenger_buffer_response` option (default is "off").
|
198
|
-
# 2. Passenger buffering system which is always 'on' irrespective of the value
|
199
|
-
# of `passenger_buffer_response`.
|
200
|
-
#
|
201
|
-
#
|
202
|
-
# When `passenger_buffer_response` is turned "on", then streaming would be done
|
203
|
-
# at the NGINX level which waits until the application is done sending the
|
204
|
-
# response back to the client.
|
205
|
-
#
|
206
|
-
# For more information, please check the [documentation]
|
207
|
-
# (https://www.phusionpassenger.com/docs/references/config_reference/nginx/#passenger_buffer_response).
|
168
|
+
# Rack 3+ compatible servers all support streaming.
|
208
169
|
module Streaming
|
209
|
-
class Body # :nodoc:
|
210
|
-
TERM = "\r\n"
|
211
|
-
TAIL = "0#{TERM}"
|
212
|
-
|
213
|
-
# Store the response body to be chunked.
|
214
|
-
def initialize(body)
|
215
|
-
@body = body
|
216
|
-
end
|
217
|
-
|
218
|
-
# For each element yielded by the response body, yield the element in chunked
|
219
|
-
# encoding.
|
220
|
-
def each(&block)
|
221
|
-
term = TERM
|
222
|
-
@body.each do |chunk|
|
223
|
-
size = chunk.bytesize
|
224
|
-
next if size == 0
|
225
|
-
|
226
|
-
yield [size.to_s(16), term, chunk.b, term].join
|
227
|
-
end
|
228
|
-
yield TAIL
|
229
|
-
yield term
|
230
|
-
end
|
231
|
-
|
232
|
-
# Close the response body if the response body supports it.
|
233
|
-
def close
|
234
|
-
@body.close if @body.respond_to?(:close)
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
170
|
private
|
239
|
-
# Set proper cache control and transfer encoding when streaming
|
240
|
-
def _process_options(options)
|
241
|
-
super
|
242
|
-
if options[:stream]
|
243
|
-
if request.version == "HTTP/1.0"
|
244
|
-
options.delete(:stream)
|
245
|
-
else
|
246
|
-
headers["Cache-Control"] ||= "no-cache"
|
247
|
-
headers["Transfer-Encoding"] = "chunked"
|
248
|
-
headers.delete("Content-Length")
|
249
|
-
end
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
171
|
# Call render_body if we are streaming instead of usual `render`.
|
254
172
|
def _render_template(options)
|
255
173
|
if options.delete(:stream)
|
256
|
-
|
174
|
+
# It shoudn't be necessary to set this.
|
175
|
+
headers["cache-control"] ||= "no-cache"
|
176
|
+
|
177
|
+
view_renderer.render_body(view_context, options)
|
257
178
|
else
|
258
179
|
super
|
259
180
|
end
|