Announcing faye-websocket, a standards-compliant WebSocket library

While announcing last week’s release of Faye 0.7, I mentioned a couple of things: first, Faye now exposes its WebSocket APIs for everyone to use, and second that I planned to extract some components in order to slim Faye down for the 0.8 release.

Well the first step in that process is now done, and faye-websocket packages are now available for Node and Ruby, installable using npm install faye-websocket and gem install faye-websocket respectively. This process has already shrunk the Faye codebase by over 2,300 LOC but I suppose you’re still wondering why we need another WebSocket library.

It was never my intention to turn Faye into a plain-WebSocket project. I just implemented enough of the protocol to get Faye working, and left it at that. But recently I’ve taken a look around at the other packages for both Node and Ruby, and it turns out pretty much all of them suffer from at least one of the following problems:

The first is a simple matter of maintenance; I could contribute patches to projects to make them work. And it turns out you can’t take a piecemeal approach to implementing wire protocols, you need to deal with whatever might come your way. This is the Internet, after all. I found several libraries where sending them totally valid WebSocket data causes them to crash, simply because they are incomplete.

The second is somewhat trickier, depending on the nature of the project. Some projects are simple WebSocket implementations. Some are transport abstractions aiming to provide a socket-like interface on top of other protocols. And some are full-blown messaging systems with semantics over and above those provided by WebSockets. Faye falls into the latter camp, although its WebSocket code has always been decoupled from the rest of the codebase.

The third is harder to fix, though. If you change the API for a project, you affect all its users. Its author probably has reasons for the API they picked, reasons you weren’t privy to when examining potential use cases. Patches that change the user-facing API of a library are much less likely to be accepted.

But the problem posed by non-standard APIs is, to me, quite serious. The Web Standards movement came about because web developers wanted to write portable code, and have a reasonable expectation of that code working when delivered to users. The WebSocket API is part of that same effort, to provide Web authors with standard interfaces to code against in order to produce portable software. As I have ranted about at length before, ignorance of code portability constitutes a missed opportunity for anyone developing Node programs.

Every time somebody invents a new API for a standard piece of Web infrastructure, they make everyone else’s code less portable. The aim with faye-websocket is to keep the standard WebSocket API so that code developed for the browser can be reused on the server. For example, the Faye pub/sub messaging client uses a ‘transport’ object to send messages to the server; there’s a transport based on XMLHttpRequest, one based on JSON-P, one based on Node’s HTTP library, and one based on WebSockets. If using an HTTP transport, one must pick whether to use XMLHttpRequest or Node HTTP, based on the environment. With WebSockets, we can reuse the same transport class on the server and in the browser, because Faye’s WebSockets have the same API you get in the browser. This means less code to write, and more code you can automatically test from the command line.

And this doesn’t just apply to WebSocket clients. After the handshake, the WebSocket protocol is symmetric: both peers can send and receive messages with the same protocol operating in both directions. It seems to me that any code based on WebSockets ought to be equally happy handling a server-side connection or a client-side one, since both ends of the connection have exactly the same capabilities. For this reason, faye-websocket wraps server-side connections with the standard API:

var WebSocket = require('faye-websocket'),
    http      = require('http'),
    server    = http.createServer();

server.addListener('upgrade', function(request, socket, head) {
  var ws = new WebSocket(request, socket, head);
  
  ws.onmessage = function(event) {
    ws.send(event.data);
  };
  
  ws.onclose = function(event) {
    console.log('close', event.code, event.reason);
    ws = null;
  };
});

server.listen(8000);

Server-side connections transparently select between draft-75, draft-76 or hybi-07-compatible protocols and handle text, binary, ping/pong, close and fragmented messages. The client is hybi-08, but has been tested up to version 17.

Of course, nothing is ever perfect on the first release but faye-websocket does have one of the most complete protocol implementations around, and I’d like to see more implementations adopt the standard API. The standard interfaces might not be to your taste, but they benefit the ecosystem by giving everyone a predictable playing field. If you maintain a WebSocket project, please test it using the Autobahn test suite (here are Faye’s server and client results) and consider exposing the standard API to your users.

Faye 0.7: new event APIs and an open WebSocket stack

I’m very excited to announce the release of Faye 0.7, available now through gem and npm. This release focuses on two main areas: new event APIs for hooking into the framework, and a polished stand-alone WebSocket implementation.

Let’s deal with the new APIs first. The main one is an API for listening to what’s going on inside the engine. People have historically used extensions for various sorts of monitoring but there are certain events extensions can’t catch. For example, when a client session ends due to inactivity rather than because the client sent a /meta/disconnect message, there’s no way to detect that. Well now you can:

var bayeux = new Faye.NodeAdapter({mount: '/faye', timeout: 45});

bayeux.bind('disconnect', function(clientId) {
  // event listener logic
});

There’s a complete API for listening to all the major events in Faye’s pub/sub model, and I encourage you to use it over extensions if all you want is a little monitoring.

The second new event API is on the client side. Faye will always attempt to reconnect for you if the network connection is dropped, but often it’s useful to signal the connection loss to the user. You can now do this using these events on the client:

client.bind('transport:down', function() {
  // Fires when the connection is lost
});
client.bind('transport:up', function() {
  // Fires when the connection is established
});

You don’t need to reconnect the client yourself, these events are just there to notify you of what’s going on. And to give you even more feedback, publish() now supports the same Deferrable API as subscribe().

Now onto the WebSocket changes. The first of which is that Faye now includes a WebSocket client, and will use this instead of HTTP for server-side clients. The server-side HTTP transports now support cookies, and all transports support SSL. The WebSocket stack has received a lot of bug fixes based on the Autobahn test suite, and is now one of the most complete WebSocket implementations available for Node or Ruby. (Want proof? Here’s the server and client test report.)

Faye’s WebSocket stack also has the advantage of having been reasonably decoupled from the rest of the codebase since it was first introduced in version 0.5, and now I’ve taken the decision to open it up for everyone to use. It makes adding WebSocket support to existing Node and Rack apps very easy indeed; you can drop sockets right into your existing application and they expose the same API you use in the browser:

var http = require('http'),
    faye = require('faye');

var server = http.createServer();

server.addListener('upgrade', function(request, socket, head) {
  var ws = new faye.WebSocket(request, socket, head);

  ws.onmessage = function(event) {
    ws.send(event.data);
  };

  ws.onclose = function(event) {
    console.log('close', event.code, event.reason);
    ws = null;
  };
});

server.listen(8000);

The sockets implement the standard WebSocket API on both the server- and client-side. They handle text and binary data, and transparently handle ping/pong, close, and fragmented messages for you. The server supports a wide range of protocol versions and the client should be able to talk to any modern WebSocket server.

If, however, you don’t want to use WebSockets for your Faye client, you can easily switch them off:

client.disable('websocket');

I think the WebSocket tools are important because while there are plenty of messaging frameworks and transport abstractions around (e.g. libraries that present the WebSocket API on top of other wire transports), the state of pure WebSocket implementations seems to be somewhat lacking at the moment. Hopefully this can improve the situation as we move towards broader socket support across the web.

So that’s about it for the 0.7 release. Work is already underway on the 0.8 release, and the current plan is to focus on refactoring. The Faye distribution contains several components, such as the WebSocket code and the Redis backend, that can be broken off into separate projects. Indeed, work is already underway on extracting the WebSocket code for Ruby. As usual, this release should be backward compatible but if you have problems don’t hesitate to post on the mailing list.