1

I'm trying to use Live Streaming in Rails 4.0.1 in one project but I see problems...

I have this action:

def realtime_push
    response.headers['Content-Type'] = 'text/event-stream'

    sse = SSE.new(response.stream)

    d = Domain.find(params[:domain_id])

    begin
      loop do
        backlinks = d.backlinks.page(params[:page]).per(10)
        pagination = render_to_string(:partial => 'backlinks/pagination', :layout => false, :locals => { :backlinks => backlinks })
        sse.write({ :html => pagination }, :event => 'pagination')
        sleep 1
      end
    rescue IOError
      # When the client disconnects, we'll get an IOError on write
      logger.debug "DISCONNECTED"
    ensure
      sse.close
    end
end

When I start Puma and try to get updates:

curl http://localhost:3000/domains/16/backlinks/realtime_push

curl immediately returns with no output.

Curl headers:

HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-UA-Compatible: chrome=1
Content-Type: text/event-stream
Cache-Control: no-cache
X-Request-Id: 1a07be2f-de8d-4ca8-87d0-eee2787ea649
X-Runtime: 0.250782
Transfer-Encoding: chunked

and Puma log shows:

Started GET "/domains/16/backlinks/realtime_push" for 127.0.0.1 at 2013-11-08 12:22:30 +0100
  ActiveRecord::SchemaMigration Load (0.7ms)  SELECT "schema_migrations".* FROM "schema_migrations"
Processing by BacklinksController#realtime_push as */*
  Parameters: {"domain_id"=>"16"}
  Domain Load (1.9ms)  SELECT "domains".* FROM "domains" WHERE "domains"."id" = $1 LIMIT 1  [["id", "16"]]
   (3.3ms)  SELECT COUNT(*) FROM "backlinks" WHERE "backlinks"."domain_id" = $1  [["domain_id", 16]]
  Rendered backlinks/_pagination.haml (60.6ms)
   (0.6ms)  SELECT COUNT(*) FROM "backlinks" WHERE "backlinks"."domain_id" = $1  [["domain_id", 16]]
  Rendered backlinks/_pagination.haml (36.0ms)
   (0.8ms)  SELECT COUNT(*) FROM "backlinks" WHERE "backlinks"."domain_id" = $1  [["domain_id", 16]]
  Rendered backlinks/_pagination.haml (37.5ms)
   (0.8ms)  SELECT COUNT(*) FROM "backlinks" WHERE "backlinks"."domain_id" = $1  [["domain_id", 16]]
  Rendered backlinks/_pagination.haml (35.6ms)
   (0.7ms)  SELECT COUNT(*) FROM "backlinks" WHERE "backlinks"."domain_id" = $1  [["domain_id", 16]]
  Rendered backlinks/_pagination.haml (38.7ms)
   (0.7ms)  SELECT COUNT(*) FROM "backlinks" WHERE "backlinks"."domain_id" = $1  [["domain_id", 16]]
  Rendered backlinks/_pagination.haml (37.0ms)

So these things are strange:

  • curl returned no output
  • log says it rendered pagination 6 times
  • there was no "DISCONNECT" message in the log

Any ideas? If I comment out the two lines above sse.write and return some text instead of pagination contents, it works...

Here is the SSE class:

class SSE
  def initialize io
    @io = io
  end

  def write object, options = {}
    options.each do |k,v|
      @io.write "#{k}: #{v}\n"
    end
    @io.write "data: #{JSON.dump(object)}\n\n"
  end

  def close
    @io.close
  end
end
davidhq
  • 4,660
  • 6
  • 30
  • 40
  • 1
    What is the SSE class? It handles the main operation in this action, and yet you chose to not share it with us. What are the HTTP headers when you invoke curl with `-i`? Is the "Transfer-encoding" set to "chunked"? Are you sure that you started Puma as the webserver and not WEBrick? – mislav Nov 08 '13 at 16:43
  • SSE class is the same as in the "Is it live" article.. and the code works when I return simple text and comment out the two lines above sse.write.. as soon as I uncomment them even though I'm calling the sse.write in the exact same way, it stops sending data.. strange... I added the curl headers to the post... Yes, I'm sure it's Puma... – davidhq Nov 08 '13 at 19:01

1 Answers1

1

This is a bug in render_to_string.

Monkey patch to fix this (that actually doesn't fix the problem - see below):

def render_to_string(*)
  orig_stream = response.stream
  super
ensure
  if orig_stream
    response.instance_variable_set(:@stream, orig_stream)
  end
end

Source: http://blog.sorah.jp/2013/07/28/render_to_string-in-ac-live

UPDATE: this only appears to fix the problem... although it will cause the controller to actually send the data, the receiving end in JavaScript for some reason still won't get notified of events, see here: SSE (Server-sent events) not working

Community
  • 1
  • 1
davidhq
  • 4,660
  • 6
  • 30
  • 40