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!'