lib/action_controller/metal/streaming.rb in actionpack-7.1.5 vs lib/action_controller/metal/streaming.rb in actionpack-7.2.0.beta1
- old
+ new
@@ -1,222 +1,224 @@
# frozen_string_literal: true
+# :markup: markdown
+
module ActionController # :nodoc:
- # = Action Controller \Streaming
+ # # Action Controller Streaming
#
# Allows views to be streamed back to the client as they are rendered.
#
- # By default, \Rails renders views by first rendering the template
- # and then the layout. The response is sent to the client after the whole
- # template is rendered, all queries are made, and the layout is processed.
+ # By default, Rails renders views by first rendering the template and then the
+ # layout. The response is sent to the client after the whole template is
+ # rendered, all queries are made, and the layout is processed.
#
- # \Streaming inverts the rendering flow by rendering the layout first and
+ # Streaming inverts the rendering flow by rendering the layout first and
# subsequently each part of the layout as they are processed. This allows the
- # header of the HTML (which is usually in the layout) to be streamed back
- # to client very quickly, enabling JavaScripts and stylesheets to be loaded
- # earlier than usual.
+ # header of the HTML (which is usually in the layout) to be streamed back to
+ # client very quickly, enabling JavaScripts and stylesheets to be loaded earlier
+ # than usual.
#
- # Several Rack middlewares may not work and you need to be careful when streaming.
- # This is covered in more detail below, see the Streaming@Middlewares section.
+ # Several Rack middlewares may not work and you need to be careful when
+ # streaming. This is covered in more detail below, see the Streaming@Middlewares
+ # section.
#
- # \Streaming can be added to a given template easily, all you need to do is
- # to pass the +:stream+ option to +render+.
+ # Streaming can be added to a given template easily, all you need to do is to
+ # pass the `:stream` option to `render`.
#
- # class PostsController
- # def index
- # @posts = Post.all
- # render stream: true
+ # class PostsController
+ # def index
+ # @posts = Post.all
+ # render stream: true
+ # end
# end
- # end
#
- # == When to use streaming
+ # ## When to use streaming
#
- # \Streaming may be considered to be overkill for lightweight actions like
- # +new+ or +edit+. The real benefit of streaming is on expensive actions
- # that, for example, do a lot of queries on the database.
+ # Streaming may be considered to be overkill for lightweight actions like `new`
+ # or `edit`. The real benefit of streaming is on expensive actions that, for
+ # example, do a lot of queries on the database.
#
- # In such actions, you want to delay queries execution as much as you can.
- # For example, imagine the following +dashboard+ action:
+ # In such actions, you want to delay queries execution as much as you can. For
+ # example, imagine the following `dashboard` action:
#
- # def dashboard
- # @posts = Post.all
- # @pages = Page.all
- # @articles = Article.all
- # end
+ # def dashboard
+ # @posts = Post.all
+ # @pages = Page.all
+ # @articles = Article.all
+ # end
#
# Most of the queries here are happening in the controller. In order to benefit
# from streaming you would want to rewrite it as:
#
- # def dashboard
- # # Allow lazy execution of the queries
- # @posts = Post.all
- # @pages = Page.all
- # @articles = Article.all
- # render stream: true
- # end
+ # def dashboard
+ # # Allow lazy execution of the queries
+ # @posts = Post.all
+ # @pages = Page.all
+ # @articles = Article.all
+ # render stream: true
+ # end
#
- # Notice that +:stream+ only works with templates. \Rendering +:json+
- # or +:xml+ with +:stream+ won't work.
+ # Notice that `:stream` only works with templates. Rendering `:json` or `:xml`
+ # with `:stream` won't work.
#
- # == Communication between layout and template
+ # ## Communication between layout and template
#
- # When streaming, rendering happens top-down instead of inside-out.
- # \Rails starts with the layout, and the template is rendered later,
- # when its +yield+ is reached.
+ # When streaming, rendering happens top-down instead of inside-out. Rails starts
+ # with the layout, and the template is rendered later, when its `yield` is
+ # reached.
#
- # This means that, if your application currently relies on instance
- # variables set in the template to be used in the layout, they won't
- # work once you move to streaming. The proper way to communicate
- # between layout and template, regardless of whether you use streaming
- # or not, is by using +content_for+, +provide+, and +yield+.
+ # This means that, if your application currently relies on instance variables
+ # set in the template to be used in the layout, they won't work once you move to
+ # streaming. The proper way to communicate between layout and template,
+ # regardless of whether you use streaming or not, is by using `content_for`,
+ # `provide`, and `yield`.
#
- # Take a simple example where the layout expects the template to tell
- # which title to use:
+ # Take a simple example where the layout expects the template to tell which
+ # title to use:
#
- # <html>
- # <head><title><%= yield :title %></title></head>
- # <body><%= yield %></body>
- # </html>
+ # <html>
+ # <head><title><%= yield :title %></title></head>
+ # <body><%= yield %></body>
+ # </html>
#
- # You would use +content_for+ in your template to specify the title:
+ # You would use `content_for` in your template to specify the title:
#
- # <%= content_for :title, "Main" %>
- # Hello
+ # <%= content_for :title, "Main" %>
+ # Hello
#
# And the final result would be:
#
- # <html>
- # <head><title>Main</title></head>
- # <body>Hello</body>
- # </html>
+ # <html>
+ # <head><title>Main</title></head>
+ # <body>Hello</body>
+ # </html>
#
- # However, if +content_for+ is called several times, the final result
- # would have all calls concatenated. For instance, if we have the following
- # template:
+ # However, if `content_for` is called several times, the final result would have
+ # all calls concatenated. For instance, if we have the following template:
#
- # <%= content_for :title, "Main" %>
- # Hello
- # <%= content_for :title, " page" %>
+ # <%= content_for :title, "Main" %>
+ # Hello
+ # <%= content_for :title, " page" %>
#
# The final result would be:
#
- # <html>
- # <head><title>Main page</title></head>
- # <body>Hello</body>
- # </html>
+ # <html>
+ # <head><title>Main page</title></head>
+ # <body>Hello</body>
+ # </html>
#
- # This means that, if you have <code>yield :title</code> in your layout
- # and you want to use streaming, you would have to render the whole template
- # (and eventually trigger all queries) before streaming the title and all
- # assets, which defeats the purpose of streaming. Alternatively, you can use
- # a helper called +provide+ that does the same as +content_for+ but tells the
- # layout to stop searching for other entries and continue rendering.
+ # This means that, if you have `yield :title` in your layout and you want to use
+ # streaming, you would have to render the whole template (and eventually trigger
+ # all queries) before streaming the title and all assets, which defeats the
+ # purpose of streaming. Alternatively, you can use a helper called `provide`
+ # that does the same as `content_for` but tells the layout to stop searching for
+ # other entries and continue rendering.
#
- # For instance, the template above using +provide+ would be:
+ # For instance, the template above using `provide` would be:
#
- # <%= provide :title, "Main" %>
- # Hello
- # <%= content_for :title, " page" %>
+ # <%= provide :title, "Main" %>
+ # Hello
+ # <%= content_for :title, " page" %>
#
# Resulting in:
#
- # <html>
- # <head><title>Main</title></head>
- # <body>Hello</body>
- # </html>
+ # <html>
+ # <head><title>Main</title></head>
+ # <body>Hello</body>
+ # </html>
#
- # That said, when streaming, you need to properly check your templates
- # and choose when to use +provide+ and +content_for+.
+ # That said, when streaming, you need to properly check your templates and
+ # choose when to use `provide` and `content_for`.
#
# See also ActionView::Helpers::CaptureHelper for more information.
#
- # == Headers, cookies, session, and flash
+ # ## Headers, cookies, session, and flash
#
- # When streaming, the HTTP headers are sent to the client right before
- # it renders the first line. This means that, modifying headers, cookies,
- # session or flash after the template starts rendering will not propagate
- # to the client.
+ # When streaming, the HTTP headers are sent to the client right before it
+ # renders the first line. This means that, modifying headers, cookies, session
+ # or flash after the template starts rendering will not propagate to the client.
#
- # == Middlewares
+ # ## Middlewares
#
- # Middlewares that need to manipulate the body won't work with streaming.
- # You should disable those middlewares whenever streaming in development
- # or production. For instance, +Rack::Bug+ won't work when streaming as it
- # needs to inject contents in the HTML body.
+ # Middlewares that need to manipulate the body won't work with streaming. You
+ # should disable those middlewares whenever streaming in development or
+ # production. For instance, `Rack::Bug` won't work when streaming as it needs to
+ # inject contents in the HTML body.
#
- # Also +Rack::Cache+ won't work with streaming as it does not support
- # streaming bodies yet. Whenever streaming +Cache-Control+ is automatically
- # set to "no-cache".
+ # Also `Rack::Cache` won't work with streaming as it does not support streaming
+ # bodies yet. Whenever streaming `Cache-Control` is automatically set to
+ # "no-cache".
#
- # == Errors
+ # ## Errors
#
# When it comes to streaming, exceptions get a bit more complicated. This
- # happens because part of the template was already rendered and streamed to
- # the client, making it impossible to render a whole exception page.
+ # happens because part of the template was already rendered and streamed to the
+ # client, making it impossible to render a whole exception page.
#
- # Currently, when an exception happens in development or production, \Rails
- # will automatically stream to the client:
+ # Currently, when an exception happens in development or production, Rails will
+ # automatically stream to the client:
#
- # "><script>window.location = "/500.html"</script></html>
+ # "><script>window.location = "/500.html"</script></html>
#
- # The first two characters (<tt>"></tt>) are required in case the exception
- # happens while rendering attributes for a given tag. You can check the real
- # cause for the exception in your logger.
+ # The first two characters (`">`) are required in case the exception happens
+ # while rendering attributes for a given tag. You can check the real cause for
+ # the exception in your logger.
#
- # == Web server support
+ # ## Web server support
#
- # Not all web servers support streaming out-of-the-box. You need to check
- # the instructions for each of them.
+ # Not all web servers support streaming out-of-the-box. You need to check the
+ # instructions for each of them.
#
- # ==== Unicorn
+ # #### Unicorn
#
- # Unicorn supports streaming but it needs to be configured. For this, you
- # need to create a config file as follow:
+ # Unicorn supports streaming but it needs to be configured. For this, you need
+ # to create a config file as follow:
#
- # # unicorn.config.rb
- # listen 3000, tcp_nopush: false
+ # # unicorn.config.rb
+ # listen 3000, tcp_nopush: false
#
# And use it on initialization:
#
- # unicorn_rails --config-file unicorn.config.rb
+ # unicorn_rails --config-file unicorn.config.rb
#
- # You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
+ # You may also want to configure other parameters like `:tcp_nodelay`.
#
# For more information, please check the
- # {documentation}[https://bogomips.org/unicorn/Unicorn/Configurator.html#method-i-listen].
+ # [documentation](https://bogomips.org/unicorn/Unicorn/Configurator.html#method-
+ # i-listen).
#
- # If you are using Unicorn with NGINX, you may need to tweak NGINX.
- # \Streaming should work out of the box on Rainbows.
+ # If you are using Unicorn with NGINX, you may need to tweak NGINX. Streaming
+ # should work out of the box on Rainbows.
#
- # ==== Passenger
+ # #### Passenger
#
# Phusion Passenger with NGINX, offers two streaming mechanisms out of the box.
#
- # 1. NGINX response buffering mechanism which is dependent on the value of
- # +passenger_buffer_response+ option (default is "off").
- # 2. Passenger buffering system which is always 'on' irrespective of the value
- # of +passenger_buffer_response+.
+ # 1. NGINX response buffering mechanism which is dependent on the value of
+ # `passenger_buffer_response` option (default is "off").
+ # 2. Passenger buffering system which is always 'on' irrespective of the value
+ # of `passenger_buffer_response`.
#
- # When +passenger_buffer_response+ is turned "on", then streaming would be
- # done at the NGINX level which waits until the application is done sending
- # the response back to the client.
#
- # For more information, please check the
- # {documentation}[https://www.phusionpassenger.com/docs/references/config_reference/nginx/#passenger_buffer_response].
+ # When `passenger_buffer_response` is turned "on", then streaming would be done
+ # at the NGINX level which waits until the application is done sending the
+ # response back to the client.
#
+ # For more information, please check the [documentation]
+ # (https://www.phusionpassenger.com/docs/references/config_reference/nginx/#passenger_buffer_response).
module Streaming
class Body # :nodoc:
TERM = "\r\n"
TAIL = "0#{TERM}"
# Store the response body to be chunked.
def initialize(body)
@body = body
end
- # For each element yielded by the response body, yield
- # the element in chunked encoding.
+ # For each element yielded by the response body, yield the element in chunked
+ # encoding.
def each(&block)
term = TERM
@body.each do |chunk|
size = chunk.bytesize
next if size == 0
@@ -246,10 +248,10 @@
headers.delete("Content-Length")
end
end
end
- # Call render_body if we are streaming instead of usual +render+.
+ # Call render_body if we are streaming instead of usual `render`.
def _render_template(options)
if options.delete(:stream)
Body.new view_renderer.render_body(view_context, options)
else
super