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.

Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +83 -139
  3. data/lib/abstract_controller/helpers.rb +0 -2
  4. data/lib/action_controller/metal/allow_browser.rb +1 -1
  5. data/lib/action_controller/metal/conditional_get.rb +5 -1
  6. data/lib/action_controller/metal/http_authentication.rb +4 -1
  7. data/lib/action_controller/metal/instrumentation.rb +1 -2
  8. data/lib/action_controller/metal/live.rb +10 -2
  9. data/lib/action_controller/metal/rate_limiting.rb +13 -4
  10. data/lib/action_controller/metal/renderers.rb +2 -1
  11. data/lib/action_controller/metal/streaming.rb +5 -84
  12. data/lib/action_controller/metal/strong_parameters.rb +274 -73
  13. data/lib/action_controller/railtie.rb +1 -1
  14. data/lib/action_controller/test_case.rb +2 -0
  15. data/lib/action_dispatch/http/cache.rb +27 -10
  16. data/lib/action_dispatch/http/content_security_policy.rb +13 -25
  17. data/lib/action_dispatch/http/filter_parameters.rb +4 -9
  18. data/lib/action_dispatch/http/filter_redirect.rb +2 -9
  19. data/lib/action_dispatch/http/permissions_policy.rb +2 -0
  20. data/lib/action_dispatch/http/request.rb +4 -2
  21. data/lib/action_dispatch/journey/parser.rb +99 -196
  22. data/lib/action_dispatch/journey/scanner.rb +40 -42
  23. data/lib/action_dispatch/middleware/cookies.rb +4 -2
  24. data/lib/action_dispatch/middleware/debug_exceptions.rb +16 -3
  25. data/lib/action_dispatch/middleware/request_id.rb +2 -1
  26. data/lib/action_dispatch/middleware/ssl.rb +13 -3
  27. data/lib/action_dispatch/railtie.rb +2 -0
  28. data/lib/action_dispatch/routing/inspector.rb +1 -1
  29. data/lib/action_dispatch/routing/mapper.rb +25 -17
  30. data/lib/action_dispatch/routing/route_set.rb +18 -6
  31. data/lib/action_dispatch/system_testing/browser.rb +12 -21
  32. data/lib/action_dispatch.rb +0 -4
  33. data/lib/action_pack/gem_version.rb +4 -4
  34. metadata +13 -35
  35. data/lib/action_dispatch/journey/parser.y +0 -50
  36. 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: e2f850764c42d33756dafc52b3a241cd1264cf780ef17f52b9b3b0a8b1c3d98e
4
- data.tar.gz: 7febf80d5ab5a57de20b9658daaa10fb21216b30590837e66ceb43cb6cdfe38f
3
+ metadata.gz: 4392b60cf6c4af7ed943daf2c4e438440689b8f869a859510b1e0e5808204cf2
4
+ data.tar.gz: ee9993b25b397af527f1730dc439a0425a81d4b03afb148e310dac8862003261
5
5
  SHA512:
6
- metadata.gz: 6cd119f952b01a8fdf78c1a3c364bf5e681b6b0de52758a1830b935362bc7c0c9950d371bd6b6667e49dc49e8b9f98d0f60b06781a155bcf752be705e19c875f
7
- data.tar.gz: 15339819a72191cd86e77924f9a108ec6c9f7bcc7f3169ba2127bc1ccdefc2a7fdb98609689a7271faf467664df7841f163610445294dbe8eed08c48c431aa01
6
+ metadata.gz: 2063ad6ca3243b066226af6a1d1ad616d808c48a18f80df374d02b064ad4839a971e4eff804321621d910111829b0c1130b5a05f5a44afabb380ec9f7d5985b7
7
+ data.tar.gz: f3f1e1ce84c47654f69cf3f3ada2afa48e412dac1b7a5abd10c4d0fc34b66b7574f2454410c323cfa26a000005da585815db8a27dc3f1e9c2a71a504b0f3021d
data/CHANGELOG.md CHANGED
@@ -1,191 +1,135 @@
1
- ## Rails 7.2.2.1 (December 10, 2024) ##
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
- ## Rails 7.2.1.2 (October 23, 2024) ##
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
- * No changes.
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
- ## Rails 7.2.1.1 (October 15, 2024) ##
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
- * Avoid regex backtracking in HTTP Token authentication
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
- [CVE-2024-47887]
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
- *John Hawthorn*
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
- * Avoid regex backtracking in query parameter filtering
48
+ ```ruby
49
+ # Before
50
+ User.find(params.require(:id)) # allows an array, altering behavior
32
51
 
33
- [CVE-2024-41128]
52
+ # After
53
+ User.find(params.expect(:id)) # expect only returns non-blank permitted scalars (excludes Hash, Array, nil, "", etc)
54
+ ```
34
55
 
35
- *John Hawthorn*
56
+ *Martin Emde*
36
57
 
58
+ * System Testing: Disable Chrome's search engine choice by default in system tests.
37
59
 
38
- ## Rails 7.2.1 (August 22, 2024) ##
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
- ## Rails 7.2.0 (August 09, 2024) ##
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
- *Stefan Wienert*
70
+ * Speed up `ActionDispatch::Routing::Mapper::Scope#[]` by merging frame hashes.
87
71
 
88
- * Add ".test" as a default allowed host in development to ensure smooth golden-path setup with puma.dev.
72
+ *Gannon McGibbon*
89
73
 
90
- *DHH*
74
+ * Allow bots to ignore `allow_browser`.
91
75
 
92
- * Add `allow_browser` to set minimum browser versions for the application.
76
+ *Matthew Nguyen*
93
77
 
94
- 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".
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
- class ApplicationController < ActionController::Base
98
- # Allow only browsers natively supporting webp images, web push, badges, import maps, CSS nesting + :has
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
- *DHH*
114
-
115
- * Add rate limiting API.
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
- * Remove deprecated support to set `Rails.application.config.action_dispatch.show_exceptions` to `true` and `false`.
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
- *Rafael Mendonça França*
94
+ *Nate Matykiewicz*
155
95
 
156
- * Deprecate `Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality`.
96
+ * Add `config.action_dispatch.strict_freshness`.
157
97
 
158
- *Rafael Mendonça França*
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
- * Remove deprecated comparison between `ActionController::Parameters` and `Hash`.
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
- *Rafael Mendonça França*
104
+ *heka1024*
163
105
 
164
- * Remove deprecated constant `AbstractController::Helpers::MissingHelperError`.
106
+ * Support `immutable` directive in Cache-Control
165
107
 
166
- *Rafael Mendonça França*
167
-
168
- * Fix a race condition that could cause a `Text file busy - chromedriver`
169
- error with parallel system tests.
108
+ ```ruby
109
+ expires_in 1.minute, public: true, immutable: true
110
+ # Cache-Control: public, max-age=60, immutable
111
+ ```
170
112
 
171
- *Matt Brictson*
113
+ *heka1024*
172
114
 
173
- * Add `racc` as a dependency since it will become a bundled gem in Ruby 3.4.0
115
+ * Add `:wasm_unsafe_eval` mapping for `content_security_policy`
174
116
 
175
- *Hartley McGuire*
176
- * Remove deprecated constant `ActionDispatch::IllegalStateError`.
117
+ ```ruby
118
+ # Before
119
+ policy.script_src "'wasm-unsafe-eval'"
177
120
 
178
- *Rafael Mendonça França*
121
+ # After
122
+ policy.script_src :wasm_unsafe_eval
123
+ ```
179
124
 
180
- * Add parameter filter capability for redirect locations.
125
+ *Joe Haig*
181
126
 
182
- It uses the `config.filter_parameters` to match what needs to be filtered.
183
- The result would be like this:
127
+ * Add `display_capture` and `keyboard_map` in `permissions_policy`
184
128
 
185
- Redirected to http://secret.foo.bar?username=roque&password=[FILTERED]
129
+ *Cyril Blaecke*
186
130
 
187
- Fixes #14055.
131
+ * Add `connect` route helper.
188
132
 
189
- *Roque Pinel*, *Trevor Turk*, *tonytonyjan*
133
+ *Samuel Williams*
190
134
 
191
- Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/actionpack/CHANGELOG.md) for previous changes.
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
@@ -60,7 +60,7 @@ module ActionController # :nodoc:
60
60
  end
61
61
  end
62
62
 
63
- class BrowserBlocker # :nodoc:
63
+ class BrowserBlocker
64
64
  SETS = {
65
65
  modern: { safari: 17.2, chrome: 120, firefox: 121, opera: 106, ie: false }
66
66
  }
@@ -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(AUTHN_PAIR_DELIMITERS).map(&:strip)
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.ms { render_output = super }
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
- Thread.new {
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
- 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)
@@ -154,7 +154,8 @@ module ActionController
154
154
  end
155
155
 
156
156
  add :json do |json, options|
157
- json = json.to_json(options) unless json.kind_of?(String)
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
- # Not all web servers support streaming out-of-the-box. You need to check the
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
- Body.new view_renderer.render_body(view_context, options)
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