module

# Action Controller Live

Mix this module into your controller, and all actions in that controller will be able to stream data to the client as it’s written.

class MyController < ActionController::Base
  include ActionController::Live

  def stream
    response.headers['Content-Type'] = 'text/event-stream'
    100.times {
      response.stream.write "hello world\n"
      sleep 1
    }
  ensure
    response.stream.close
  end
end

There are a few caveats with this module. You *cannot* write headers after the response has been committed (Response#committed? will return truthy). Calling `write` or `close` on the response stream will cause the response object to be committed. Make sure all headers are set before calling write or close on your stream.

You *must* call close on your stream when you’re finished, otherwise the socket may be left open forever.

The final caveat is that your actions are executed in a separate thread than the main thread. Make sure your actions are thread safe, and this shouldn’t be a problem (don’t share state across threads, etc).

Note that Rails includes `Rack::ETag` by default, which will buffer your response. As a result, streaming responses may not work properly with Rack 2.2.x, and you may need to implement workarounds in your application. You can either set the `ETag` or `Last-Modified` response headers or remove `Rack::ETag` from the middleware stack to address this issue.

Here’s an example of how you can set the `Last-Modified` header if your Rack version is 2.2.x:

def stream
  response.headers["Content-Type"] = "text/event-stream"
  response.headers["Last-Modified"] = Time.now.httpdate # Add this line if your Rack version is 2.2.x
  ...
end

Files

  • actionpack/lib/action_controller/metal/live.rb
  • actionpack/lib/action_controller/test_case.rb