Skip to content

Commit 35a4946

Browse files
authored
Merge pull request rails#52094 from ioquatix/rack-3-streaming
Utilize Rack 3 streaming.
2 parents 599023d + ed68af0 commit 35a4946

File tree

2 files changed

+12
-105
lines changed

2 files changed

+12
-105
lines changed

actionpack/lib/action_controller/metal/streaming.rb

Lines changed: 5 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -165,95 +165,16 @@ module ActionController # :nodoc:
165165
#
166166
# ## Web server support
167167
#
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.
208169
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-
238170
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-
253171
# Call render_body if we are streaming instead of usual `render`.
254172
def _render_template(options)
255173
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)
257178
else
258179
super
259180
end

actionpack/test/controller/new_base/render_streaming_test.rb

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,31 +44,27 @@ def explicit_cache
4444
end
4545

4646
class StreamingTest < Rack::TestCase
47-
def get(path, headers: { "SERVER_PROTOCOL" => "HTTP/1.1", "HTTP_VERSION" => "HTTP/1.1" })
48-
super
49-
end
50-
5147
test "rendering with streaming enabled at the class level" do
5248
get "/render_streaming/basic/hello_world"
53-
assert_body "b\r\nHello world\r\nb\r\n, I'm here!\r\n0\r\n\r\n"
49+
assert_body "Hello world, I'm here!"
5450
assert_streaming!
5551
end
5652

5753
test "rendering with streaming given to render" do
5854
get "/render_streaming/basic/explicit"
59-
assert_body "b\r\nHello world\r\nb\r\n, I'm here!\r\n0\r\n\r\n"
55+
assert_body "Hello world, I'm here!"
6056
assert_streaming!
6157
end
6258

6359
test "rendering with streaming do not override explicit cache control given to render" do
6460
get "/render_streaming/basic/explicit_cache"
65-
assert_body "b\r\nHello world\r\nb\r\n, I'm here!\r\n0\r\n\r\n"
61+
assert_body "Hello world, I'm here!"
6662
assert_streaming! "private"
6763
end
6864

6965
test "rendering with streaming no layout" do
7066
get "/render_streaming/basic/no_layout"
71-
assert_body "b\r\nHello world\r\n0\r\n\r\n"
67+
assert_body "Hello world"
7268
assert_streaming!
7369
end
7470

@@ -79,13 +75,13 @@ def get(path, headers: { "SERVER_PROTOCOL" => "HTTP/1.1", "HTTP_VERSION" => "HTT
7975

8076
test "rendering with layout exception" do
8177
get "/render_streaming/basic/layout_exception"
82-
assert_body "d\r\n<body class=\"\r\n37\r\n\"><script>window.location = \"/500.html\"</script></html>\r\n0\r\n\r\n"
78+
assert_body "<body class=\"\"><script>window.location = \"/500.html\"</script></html>"
8379
assert_streaming!
8480
end
8581

8682
test "rendering with template exception" do
8783
get "/render_streaming/basic/template_exception"
88-
assert_body "37\r\n\"><script>window.location = \"/500.html\"</script></html>\r\n0\r\n\r\n"
84+
assert_body "\"><script>window.location = \"/500.html\"</script></html>"
8985
assert_streaming!
9086
end
9187

@@ -102,19 +98,9 @@ def get(path, headers: { "SERVER_PROTOCOL" => "HTTP/1.1", "HTTP_VERSION" => "HTT
10298
end
10399
end
104100

105-
test "do not stream on HTTP/1.0" do
106-
get "/render_streaming/basic/hello_world", headers: { "HTTP_VERSION" => "HTTP/1.0" }
107-
assert_body "Hello world, I'm here!"
108-
assert_status 200
109-
assert_equal "22", headers["Content-Length"]
110-
assert_nil headers["Transfer-Encoding"]
111-
end
112-
113101
def assert_streaming!(cache = "no-cache")
114102
assert_status 200
115-
assert_nil headers["Content-Length"]
116-
assert_equal "chunked", headers["Transfer-Encoding"]
117-
assert_equal cache, headers["Cache-Control"]
103+
assert_equal cache, headers["cache-control"]
118104
end
119105
end
120106
end

0 commit comments

Comments
 (0)