9-xa5g
Last Updated: February 25, 2016
·
2.2K
· krob

Server sent events

Summary: Server sent events are a simple way to push data from the server to a browser application in moden web browser (i.e., not IE).

If you want to push data from the server down to a modern web browser, a simpler alternative to WebSockets and socket.io that is built in to Chrome, Safari and Firefox is server side events. It won't allow clients to push back up, but you can use standard HTTP POSTs for that.

There's a great article about this on Google's HTML5 Rocks site: http://www.html5rocks.com/en/tutorials/eventsource/basics/

SSE uses a simple text-based format for data exchange and provides the familiar event listener interface in the browser. Code samples are in CoffeeScript because it's awesome. :)

# Listen to the '/listen' route on the server
source = new EventSource '/listen'
source.addEventListener 'textMessage', (e) ->
  $('BODY').append "<DIV>#{e.data}</DIV>"
, false

The callback is fired with an event object that contains the event payload (e.data) as well as an event id if you need to track that. You can also send a JSON string as the data as well.

The server-side implementation doesn't require anything complicated. It just keeps the request socket open and writes each data in a simple text format. Events are delimited by two newline characters. So here's an example of what event data might look like over the wire, if you were pushing haikus to the client:

id: 1001
event: haikuText
data: server pushed events?
data: forget about WebSockets
data: SSE's easy

id: 1002
data: writing tech blog post
data: need compelling examples
data: don't write a haiku

id: 1003
...

Here there are three events (with the third truncated), each separated by two newlines. Simple.

At the time of writing, it doesn't look like Web Inspector will show this traffic (the response body is always blank), so that can be surprising when you got to inspect.

And for the sake of completeness, here's a quick and dirty server implementation in Node.js:

http = require 'http'
events = require 'events'
fs = require 'fs'

lastId = 0
emitter = new events.EventEmitter()
server = http.createServer (request, response) ->
  if request.url is '/' then return indexRoute request, response
  if request.url is '/listen' then return listenRoute request, response
  if request.url is '/speak' then return speakRoute request, response
  response.writeHead 404
  response.end 'not found'

# You can imagine how HTML file this look, a textbox and
# a listener to the server EventSource.
indexRoute = (request, response) ->
  html = fs.readFileSync './sse.html'
  response.end html

speakRoute = (request, response) ->
  console.log '/speak'
  buffer = []
  request.on 'data', (d) ->
    buffer.push d.toString()
  request.on 'end', ->
    event = serverEvent ++lastId, 'text', JSON.parse(buffer.join '')
    emitter.emit 'text', event
    response.writeHead 202
    response.end 'Received text message.'

# Make a server-side event string
serverEvent = (id, event, data) ->
  "event: #{event}\n" +
  "id: #{id}\n" +
  "data: #{JSON.stringify data}\n\n"

listenRoute = (request, response) ->
  console.log '/listen'
  request.socket.setTimeout Infinity
  response.writeHead 200,
    'Content-Type': 'text/event-stream'
    'Cache-Control': 'no-cache'
    'Connection': 'keep-alive'
  response.write '\n'
  response.on 'close', ->
    emitter.removeListener 'text', response.write
    console.log 'closed!'

  emitter.on 'text', (event) ->
    console.log 'sending data!'
    console.log event
    response.write event

server.listen 4444
console.log 'Up!'