HOWTO? streaming response to the browser

I have an external process that takes a while and emits lines. I’d like to send those lines back to the browser as they happen.

I can’t work out how to do this.

I’ve got this so far, and whilst I can see the lines being processed new line: x - All the turbo updates are just delivered at the end.

I’ve been hunting for a “flush” operation on the response.stream but can’t find anything…

Thanks in advance.

# show.erb at /show
<%= link_to "DO IT", "/stream", data: {turbo_stream: true} %>
<div id="output">
</div>
# Controller at /stream
def stream
    response.headers["Content-Type"] = "text/vnd.turbo-stream.html"

    begin
      # Start the external program
      IO.popen(["ruby", "-e", "$stdout.sync = true", "-e", "(1..5).each { |l| puts(l); sleep 1 }"], "r") do |program_output|
        program_output.each_line do |line|
          pp "new line: " + line
          # Stream each line as a Turbo Stream update
          response.stream.write turbo_stream.append(
            "output", # Target DOM ID in the view
            "<div>#{ERB::Util.html_escape(line.chomp)}</div>".html_safe
          )
        end
      end
    rescue => e
      response.stream.write turbo_stream.append(
        "output",
        "<div class='error'>Error: #{ERB::Util.html_escape(e.message)}</div>".html_safe
      )
    ensure
      # Close the stream
      response.stream.close
    end
  end
end

what do you mean by flush

Hi,

Well, on most components like application servers and web servers, they buffer the responses (so that they may calculate content-length and ETag for example)

But I’d like to acheive the same as “text/event-stream” where each event that is generated is sent down to the client - on the existing HTTP transfer connection - as it is available.

In many languages, IO-bound components often have a flush function to override any internal buffering.

I don’t know what terms are used for the components that underpin rails (rack, puma etc) so flush might not be the correct term here.

Thanks

1 Like