actionpack 7.2.2.1 → 8.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +83 -139
- data/lib/abstract_controller/helpers.rb +0 -2
- data/lib/action_controller/metal/allow_browser.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +5 -1
- data/lib/action_controller/metal/http_authentication.rb +4 -1
- data/lib/action_controller/metal/instrumentation.rb +1 -2
- data/lib/action_controller/metal/live.rb +10 -2
- 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 +2 -0
- data/lib/action_dispatch/http/cache.rb +27 -10
- data/lib/action_dispatch/http/content_security_policy.rb +13 -25
- 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/request_id.rb +2 -1
- data/lib/action_dispatch/middleware/ssl.rb +13 -3
- data/lib/action_dispatch/railtie.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +1 -1
- data/lib/action_dispatch/routing/mapper.rb +25 -17
- data/lib/action_dispatch/routing/route_set.rb +18 -6
- data/lib/action_dispatch/system_testing/browser.rb +12 -21
- data/lib/action_dispatch.rb +0 -4
- data/lib/action_pack/gem_version.rb +4 -4
- metadata +13 -35
- 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,191 +1,135 @@
|
|
1
|
-
## Rails
|
2
|
-
|
3
|
-
* Add validation to content security policies to disallow spaces and semicolons.
|
4
|
-
Developers should use multiple arguments, and different directive methods instead.
|
5
|
-
|
6
|
-
[CVE-2024-54133]
|
7
|
-
|
8
|
-
*Gannon McGibbon*
|
9
|
-
|
10
|
-
|
11
|
-
## Rails 7.2.2 (October 30, 2024) ##
|
1
|
+
## Rails 8.0.0.beta1 (September 26, 2024) ##
|
12
2
|
|
13
3
|
* Fix non-GET requests not updating cookies in `ActionController::TestCase`.
|
14
4
|
|
15
5
|
*Jon Moss*, *Hartley McGuire*
|
16
6
|
|
7
|
+
* Update `ActionController::Live` to use a thread-pool to reuse threads across requests.
|
8
|
+
|
9
|
+
*Adam Renberg Tamm*
|
17
10
|
|
18
|
-
|
11
|
+
* Introduce safer, more explicit params handling method with `params#expect` such that
|
12
|
+
`params.expect(table: [ :attr ])` replaces `params.require(:table).permit(:attr)`
|
19
13
|
|
20
|
-
|
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.
|
21
17
|
|
18
|
+
```ruby
|
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
|
27
|
+
```
|
22
28
|
|
23
|
-
|
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.
|
24
34
|
|
25
|
-
|
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.
|
26
38
|
|
27
|
-
|
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
|
28
43
|
|
29
|
-
|
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.
|
30
47
|
|
31
|
-
|
48
|
+
```ruby
|
49
|
+
# Before
|
50
|
+
User.find(params.require(:id)) # allows an array, altering behavior
|
32
51
|
|
33
|
-
|
52
|
+
# After
|
53
|
+
User.find(params.expect(:id)) # expect only returns non-blank permitted scalars (excludes Hash, Array, nil, "", etc)
|
54
|
+
```
|
34
55
|
|
35
|
-
*
|
56
|
+
*Martin Emde*
|
36
57
|
|
58
|
+
* System Testing: Disable Chrome's search engine choice by default in system tests.
|
37
59
|
|
38
|
-
|
60
|
+
*glaszig*
|
39
61
|
|
40
62
|
* Fix `Request#raw_post` raising `NoMethodError` when `rack.input` is `nil`.
|
41
63
|
|
42
64
|
*Hartley McGuire*
|
43
65
|
|
66
|
+
* Remove `racc` dependency by manually writing `ActionDispatch::Journey::Scanner`.
|
44
67
|
|
45
|
-
|
46
|
-
|
47
|
-
* Allow bots to ignore `allow_browser`.
|
48
|
-
|
49
|
-
*Matthew Nguyen*
|
50
|
-
|
51
|
-
* Include the HTTP Permissions-Policy on non-HTML Content-Types
|
52
|
-
[CVE-2024-28103]
|
53
|
-
|
54
|
-
*Aaron Patterson*, *Zack Deveau*
|
55
|
-
|
56
|
-
* Fix `Mime::Type.parse` handling type parameters for HTTP Accept headers.
|
57
|
-
|
58
|
-
*Taylor Chaparro*
|
59
|
-
|
60
|
-
* Fix the error page that is displayed when a view template is missing to account for nested controller paths in the
|
61
|
-
suggested correct location for the missing template.
|
62
|
-
|
63
|
-
*Joshua Young*
|
64
|
-
|
65
|
-
* Add `save_and_open_page` helper to `IntegrationTest`.
|
66
|
-
|
67
|
-
`save_and_open_page` is a helpful helper to keep a short feedback loop when working on system tests.
|
68
|
-
A similar helper with matching signature has been added to integration tests.
|
69
|
-
|
70
|
-
*Joé Dupuis*
|
71
|
-
|
72
|
-
* Fix a regression in 7.1.3 passing a `to:` option without a controller when the controller is already defined by a scope.
|
73
|
-
|
74
|
-
```ruby
|
75
|
-
Rails.application.routes.draw do
|
76
|
-
controller :home do
|
77
|
-
get "recent", to: "recent_posts"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
```
|
81
|
-
|
82
|
-
*Étienne Barrié*
|
83
|
-
|
84
|
-
* Request Forgery takes relative paths into account.
|
68
|
+
*Gannon McGibbon*
|
85
69
|
|
86
|
-
|
70
|
+
* Speed up `ActionDispatch::Routing::Mapper::Scope#[]` by merging frame hashes.
|
87
71
|
|
88
|
-
*
|
72
|
+
*Gannon McGibbon*
|
89
73
|
|
90
|
-
|
74
|
+
* Allow bots to ignore `allow_browser`.
|
91
75
|
|
92
|
-
*
|
76
|
+
*Matthew Nguyen*
|
93
77
|
|
94
|
-
|
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.
|
95
80
|
|
96
81
|
```ruby
|
97
|
-
|
98
|
-
|
99
|
-
allow_browser versions: :modern
|
100
|
-
end
|
101
|
-
|
102
|
-
class ApplicationController < ActionController::Base
|
103
|
-
# 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+.
|
104
|
-
allow_browser versions: { safari: 16.4, firefox: 121, ie: false }
|
105
|
-
end
|
106
|
-
|
107
|
-
class MessagesController < ApplicationController
|
108
|
-
# In addition to the browsers blocked by ApplicationController, also block Opera below 104 and Chrome below 119 for the show action.
|
109
|
-
allow_browser versions: { opera: 104, chrome: 119 }, only: :show
|
110
|
-
end
|
111
|
-
```
|
82
|
+
# Before
|
83
|
+
get "/users", "/other_path", to: "users#index"
|
112
84
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
```ruby
|
118
|
-
class SessionsController < ApplicationController
|
119
|
-
rate_limit to: 10, within: 3.minutes, only: :create
|
120
|
-
end
|
121
|
-
|
122
|
-
class SignupsController < ApplicationController
|
123
|
-
rate_limit to: 1000, within: 10.seconds,
|
124
|
-
by: -> { request.domain }, with: -> { redirect_to busy_controller_url, alert: "Too many signups!" }, only: :new
|
125
|
-
end
|
85
|
+
# After
|
86
|
+
get "/users", to: "users#index"
|
87
|
+
get "/other_path", to: "users#index"
|
126
88
|
```
|
127
89
|
|
128
|
-
*DHH*, *Jean Boussier*
|
129
|
-
|
130
|
-
* Add `image/svg+xml` to the compressible content types of `ActionDispatch::Static`.
|
131
|
-
|
132
|
-
*Georg Ledermann*
|
133
|
-
|
134
|
-
* Add instrumentation for `ActionController::Live#send_stream`.
|
135
|
-
|
136
|
-
Allows subscribing to `send_stream` events. The event payload contains the filename, disposition, and type.
|
137
|
-
|
138
|
-
*Hannah Ramadan*
|
139
|
-
|
140
|
-
* Add support for `with_routing` test helper in `ActionDispatch::IntegrationTest`.
|
141
|
-
|
142
90
|
*Gannon McGibbon*
|
143
91
|
|
144
|
-
*
|
145
|
-
|
146
|
-
*Rafael Mendonça França*
|
147
|
-
|
148
|
-
* Remove deprecated `speaker`, `vibrate`, and `vr` permissions policy directives.
|
149
|
-
|
150
|
-
*Rafael Mendonça França*
|
151
|
-
|
152
|
-
* Remove deprecated `Rails.application.config.action_dispatch.return_only_request_media_type_on_content_type`.
|
92
|
+
* Make `http_cache_forever` use `immutable: true`
|
153
93
|
|
154
|
-
*
|
94
|
+
*Nate Matykiewicz*
|
155
95
|
|
156
|
-
*
|
96
|
+
* Add `config.action_dispatch.strict_freshness`.
|
157
97
|
|
158
|
-
|
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.
|
159
100
|
|
160
|
-
|
101
|
+
Defaults to `false` to maintain compatibility with previous versions of Rails, but is enabled as part of
|
102
|
+
Rails 8.0 defaults.
|
161
103
|
|
162
|
-
*
|
104
|
+
*heka1024*
|
163
105
|
|
164
|
-
*
|
106
|
+
* Support `immutable` directive in Cache-Control
|
165
107
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
108
|
+
```ruby
|
109
|
+
expires_in 1.minute, public: true, immutable: true
|
110
|
+
# Cache-Control: public, max-age=60, immutable
|
111
|
+
```
|
170
112
|
|
171
|
-
*
|
113
|
+
*heka1024*
|
172
114
|
|
173
|
-
* Add `
|
115
|
+
* Add `:wasm_unsafe_eval` mapping for `content_security_policy`
|
174
116
|
|
175
|
-
|
176
|
-
|
117
|
+
```ruby
|
118
|
+
# Before
|
119
|
+
policy.script_src "'wasm-unsafe-eval'"
|
177
120
|
|
178
|
-
|
121
|
+
# After
|
122
|
+
policy.script_src :wasm_unsafe_eval
|
123
|
+
```
|
179
124
|
|
180
|
-
*
|
125
|
+
*Joe Haig*
|
181
126
|
|
182
|
-
|
183
|
-
The result would be like this:
|
127
|
+
* Add `display_capture` and `keyboard_map` in `permissions_policy`
|
184
128
|
|
185
|
-
|
129
|
+
*Cyril Blaecke*
|
186
130
|
|
187
|
-
|
131
|
+
* Add `connect` route helper.
|
188
132
|
|
189
|
-
*
|
133
|
+
*Samuel Williams*
|
190
134
|
|
191
|
-
Please check [7-
|
135
|
+
Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/actionpack/CHANGELOG.md) for previous changes.
|
@@ -104,7 +104,6 @@ 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
|
-
#
|
108
107
|
# class ApplicationController < ActionController::Base
|
109
108
|
# helper_method :current_user, :logged_in?
|
110
109
|
#
|
@@ -119,7 +118,6 @@ module AbstractController
|
|
119
118
|
# end
|
120
119
|
#
|
121
120
|
# In a view:
|
122
|
-
#
|
123
121
|
# <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
|
124
122
|
#
|
125
123
|
# #### Parameters
|
@@ -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,
|
@@ -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
|
@@ -307,6 +307,10 @@ module ActionController
|
|
307
307
|
error = e
|
308
308
|
end
|
309
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
|
+
|
310
314
|
@_response.commit!
|
311
315
|
end
|
312
316
|
end
|
@@ -371,11 +375,15 @@ module ActionController
|
|
371
375
|
# data from the response bodies. Nobody should call this method except in Rails
|
372
376
|
# internals. Seriously!
|
373
377
|
def new_controller_thread # :nodoc:
|
374
|
-
|
378
|
+
ActionController::Live.live_thread_pool_executor.post do
|
375
379
|
t2 = Thread.current
|
376
380
|
t2.abort_on_exception = true
|
377
381
|
yield
|
378
|
-
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def self.live_thread_pool_executor
|
386
|
+
@live_thread_pool_executor ||= Concurrent::CachedThreadPool.new(name: "action_controller.live")
|
379
387
|
end
|
380
388
|
|
381
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
|