ActiveBlog

Server Sent Events with Aura and Node.js
by Jamie Paton

Jamie Paton, July 10, 2013
SSE with Aura.js

Recently, I uncovered a relatively fresh and unique HTML5 feature: Server Sent Events (SSE). Here are the main design characteristics:

  • Everything happens over the HTTP, no special protocol required
  • Keeps an open 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. The auto reconnection is ideal for flaky connections, like mobile devices.

If you are familiar with Websockets, you should understand that they are the superior implementation for scalable bidirectional push communication, and not a competing technology for SSE. SSE can be viewed as more of a “lightweight” alternative, for when using WebSockets is not a viable option, most likely due to server limitations. 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 applications, for RW app/pages you’ll have to revert to POST'ing or long polling server writes.

  • Depending on the server configuration, the connection may be timed out frequently or freed 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 have 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:

module.exports.writeSSEHead = function (req, res, cb) {
      res.writeHead(200, {
          "Content-Type": "text/event-stream",
          "Cache-Control": "no-cache",
          "Connection": "keep-alive"
      });
      res.write("retry: 10000\n");
      return cb(req, res);
  }
  

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. Next, 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. Straightforward 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. For a per-request rather than 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 this:

app.get('/stream/date', function(req, res) {
  
      var event = 'date';
  
      sse.writeSSEHead(req, res, function(req, res) {
        sse.writeSSEData(req, res, event, new Date(), function(req, res) {
  
          intervalx = setInterval(function() {
            sse.writeSSEData(req, res, event, new Date());
          }, 1000);
  
          req.connection.addListener("close", function() {
              clearInterval(intervalx);
          });
        });
      });
  });
  

We then use a new EventSource (MDN) native browser object to connect to this endpoint (full code: here):

function connect() {
          source = new EventSource("stream/date");
  
          source.addEventListener("date", function(event) {
            self.updateDate(event.data);
          }, false);
  
          source.addEventListener("open", function(event) {
            button.text("Disconnect");
            button.click( function(event) {
              source.close();
              button.text("Connect");
              button.click(connect);
              status.text("Connection closed!");
            });
            button_cont.removeClass("warning");
            status.text("Connection open!");
          }, false);
  
   [...]
  

The EventSource parameter is the endpoint resource relative to 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: data
  data: '"2013-07-07T07:04:55.162Z"'
  

…and on each update we simply update our HTML element with the new element. Simples!

SSE Demo App

Aura.js

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

<div data-aura-widget="sse-date"></div>
  

You can view the demo app running on Stackato, or the source on Github.

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

Subscribe to ActiveState Blogs by Email

Category: stackato
About the Author: RSS

I work the DevOps role here at ActiveState. Is nice!