Andy
Andy

Reputation: 10988

Handling event-stream connections in a Sinatra app

There is a great example of a chat app using Server-Sent Events by Konstantin Haase. I am trying to run it and have a problem with callbacks (I use Sinatra 1.3.2 and browse with Chrome 16). They simply do not run (e.g. after page reload), and therefore the number of connections is growing.

Also, connection is closed in 30-60 sec unless one sets a periodic timer to send empty data, as suggested by Konstantin elsewhere.

Can you replicate it? If yes, is it possible to fix these issues somehow? WebSockets work seamlessly in this respect...

# ruby
get '/stream', provides: 'text/event-stream' do
  stream :keep_open do |out|
    EventMachine::PeriodicTimer.new(20) { out << "data: \n\n" } # added
    settings.connections << out
    puts settings.connections.count # added
    out.callback { puts 'closed'; settings.connections.delete(out) } # modified
  end
end

# javascript
  var es = new EventSource('/stream');
  es.onmessage = function(e) { if (e.data != '') $('#chat').append(e.data + "\n") }; // modified

Upvotes: 1

Views: 2676

Answers (2)

Andy
Andy

Reputation: 10988

This was a bug in Sinatra https://github.com/sinatra/sinatra/issues/446

Upvotes: 3

bioneuralnet
bioneuralnet

Reputation: 5301

Neat bit of code. But you're right, WebSockets would address these problems. I think there are two problems here:

1) Your browser, the Web server, or a proxy in-between may shut down your connection after a period of time, idle or not. Your suggestion of a periodic timer sending empty data will help, but is no guarantee.

2) As far as I know, there's no built-in way to tell if/when one of these connections is still working. To keep your list of connections from growing, you're going to have to keep track of when each connection was last "used" (maybe the client should ping occasionally, and you would store this datetime.) Then add a periodic timer to check for and kill "stale" connections.

An easier, though perhaps uglier option, is to store the creation time of each connection, and kill it off after n minutes. The client should be smart enough to reconnect.

I know that takes some of the simplicity out of the code. As neat as the example is, I think it's a better candidate for WebSockets.

Upvotes: 2

Related Questions