Recently I uncovered a relatively fresh and unique HTML5 feature; Server Sent
Events.
Note: I will digress from the onset that if you are familiar with Websockets,
then you should understand that they are the superior implementation for
scalable bidirectional push communication, and not a competiting technology for
SSE.
SSE on the other hand can be viewed as more of a "lightweight" alternative,
for when using websockets is not a viable option, most likely due to server
limitations.
To form a base understanding of SSE, here are the main design characteristics:
Everything happens over the HTTP, no special protocol required
Keeps open a HTTP/S connection to the server
Communication is unidirectional (read only by the client)
Different event sources/channels can be fed down the same connection
Auto reconnection and event IDs come for free as part of the implementation.i
The auto-reconnection is ideal for flaky connections, like mobile devices.
There are some pros and cons of using SSE over websockets. Let's go over the
cons first:
Cons:
The data source is read only. While this lends itself to actually being more
scalable for RO type applications, for RW type app/pages you'll have to revert
to POST'ing or long polling type server writes.
Depending on the server configuration, the connection may be timed out
frequently or free'd up for other users accessing the server, but mitigated
by the auto-reconnection as per the EventSource spec.
Each SSE source maintains an open connection to the server. This takes away
server side resources from other users, such as memory, CPU, NOFILE
and lower max_connections type limits of the HTTP server (as opposed to
available kernel connections/ports).
Pros:
With that said SSE does hold advantages over Websockets in some scenarios:
Doesn't require any special protocol to be supported in the browser or server
SSE support can be polyfilled
(backported) for older browsers that don't yet implement it.
Integrated Cross-site/origin request support (via CORS)
SSE can ingeniously benefit from HTTPS with no extra configuration, unlike
websockets/WSS, which require certificate setup and implementation on top of
TLS.
Enough, shut up and show me the glory of SSE!
OK then, let us wield this technology for the betterment of the internets! If
you want to dive straight into the demo app then go here:
https://github.com/jdpaton/sse-dash
As prescribed, we'll look at implementing this in Node.js. Here's the first
excerpt you should get familiar with:
Nothing so unusual, but the first part of the SSE spec is here,
specifically the retry line, telling the client, that in the event a
disconnect, it should retry after ten seconds. Note the header is
text/event-stream so the browser knows it's SSE, we tell the browser not to
cache any of the responses (you don't want stale real time data do you? ) and
we make sure the connections is kept open by the browser/client
via Connection: keep-alive. Straight forward stuff.
Next up is writing a custom event with data:
module.exports.writeSSEData = function (req, res, event, data, cb) {
var id = (new Date()).toLocaleTimeString();
res.write("id: " + id + '\n');
res.write("event: " + event + "\n");
res.write("data: " + JSON.stringify(data) + "\n\n");
if (cb) {
return cb(req, res);
}
};
The first part here is making sure we send a unique ID with each request, we
just use a simple date based one here. The ID is useful so that if a client
reconnects later, it will send the Last-Event-ID
header in the response, so the server can decide on a delta of which to resume
push messages (not implemented in the demo app though).
Note in Apache, Nginx or as here for Node.js you should consider upping the
timeout for SSE requests. This is easily set
in Node 0.10.x, and for per-request instead of a global socket setting you can
do the following (20 min idle):
res.connection.setTimeout(1000 * 60 * 20)
Using these functions in an express.js app to echo the servers date every second
might look like the following:
The EventSource parameter is the endpoint resource relative the page path, or
a full URI. Note that SSE supports cross-domain requests out of the box, via
CORS.
The date event listener is listening for lines from the server like:
event: datadata: '"2013-07-07T07:04:55.162Z"'
and on each update we simply update our HTML element with the new element -
simples!
Note the client code examples here are all using Aura, a pretty cool
project mentored by @addyosmani. We can write
a Aura widget per SSE endpoint, and use it anywhere on the website without
having to do anything - just add a <div>! :
<divdata-aura-widget="sse-date"></div>
"Aura is an event-driven architecture for wrapping your code into reusable
widgets and extensions that can easily communicate with each other" - aurajs.com
I won't cover Aura in depth here, but the code in the demo app and on material
on their site should be enough to get you started. In pursuit of this project
you could combine widgets to create customizable dashboards whose components
can talk to each other with realtime SSE data, so lots of avenues to explore!
I'd highly recommending using Aura for projects like this,
so check it out!