WebSockets and Bayeux/CometD
There are two technologies which bring communication into browser-based applications at the moment; Bayeux (aka CometD) and more recently, WebSockets. Will one supersede the other, or are there sufficient differences for both to thrive?
WebSockets is a draft standard which is sponsored by Google, Apple and others at the WhatWG working group that is standardising HTML 5. As a result, HTML 5 capable browsers (Chrome, Safari) are starting to include built-in support for the WebSocket protocol.
Both protocols aim to allow web-based AJAX applications to communicate with other services via asynchronous messaging or socket-based connections, rather than having to roll your own communications layer on top of an existing application. This allows the design of an application to focus on the component parts, and hand off messages to the communication layer for delivery. In addition, both can set up long-running connections such that events can be delivered asynchronously subsequently to the application. This is nothing new: HTTP 1.1 supported connection pipelining (the ability to keep open a connection after each request, and the ability to send multiple requests before the first was acknowledged); and other protocols like IMAP supported the IDLE command to put a connection into a hibernate state, where there is no ongoing communication but the server can push new messages at any time. Indeed, prior to either Bayeux or WebSockets, the term “HTTP Push” was used to indicate a general mechanism for long-lasting communication channel across HTTP.
However, long-running connections aren't without their problems. A connection that hasn't had any data over a period of time may be considered to have died, and arbitrarily terminated, at some point in the future. To address this, the IMAP IDLE suggests that clients send a re-negotiated IMAP IDLE command every 29 minutes to avoid disconnection. With other proxies in the way of HTTP, it may be that a proxy determines that a connection is idle and drops the connection, even if the client and server agree to maintain an ongoing connection.
The other problem is resource-based; generally, browsers limit the number of concurrent HTTP connections to a single server to avoid overbearing the server (or the network link). It's common for browsers to limit such concurrent connections to between 2 and 4 at any one time.
Both Bayeux and WebSockets attempt to avoid the resource limits by using a fall-back mechanism for long polling (in the case of Bayeux) or switching to a non-HTTP based secondary protocol instead. As a result, users of these libraries don't generally have to worry about limitations placed on them by the browser or infrastructure.
Joe Armstrong, father of Erlang, thinks that WebSockets will kill Comet:
After a small amount of experimentation I was able to make Erlang talk to a web page using pure asynchronous message passing.
I think this means the death of the following technologies:
- keep-alive sockets
All the above are merely hacks, inadequate ways of programming round the central problem that web-browsers could not simply open a socket and do asynchronous I/O like any other regular application.
Greg Wilkins, co-creator of Jetty and the Bayeux protocol, thinks that the WebSocket specification needs improving, both in terms of how it is specified and the behaviour in the case of connection termination. One of the key issues with the spec is of tackling it at the wrong level:
But a more practical problem with this style of specification is that the spec is impenetrable as it is full of text like:
Let /b_v/ be integer corresponding to the low 7 bits of /b/ (the value you would get by _and_ing /b/ with 0x7F).
Multiply /length/ by 128, add /b_v/ to that result, and store the final result in /length/.
If the high-order bit of /b/ is set (i.e. if /b/ _and_ed with 0x80 returns 0x80), then return to the step above labeled _length_.
I challenge the reader to confirm that the client side framing and the server side framing are symmetric and implement the same data framing!
Rather than such verbose means, IETF specifications typically use the precise language of augmented Backus-Naur Form (ABNF RFC5234) to formally describe protocols in a way that is not open to confusion or mis-implementation. To illustrate the clarity possible, I've translated section 4.2 into BNF...
With a chat room, the standard use-case is that once you establish your presence in the room and it remains until you explicitly leave the room. In the context of webchat, that means that you can send receive a chat message until you close the browser or navigate away from the page. Unfortunately the simple chat example does not implement this semantic because the websocket protocol allows for an idle timeout of the connection.
In order to maintain presence, the chat application can send keep-alive messages on the websocket to prevent it being closed due to an idle timeout. However, the application has no idea at all about what the idle timeout are, so it will have to pick some arbitrary frequent period (eg 30s) to send keep-alives and hope that is less than any idle timeout on the path (more or less as long-polling does now).
With onClose handling, keep-alives, message queues, timeouts and retries, we finally will have a chat room that can maintain a users presence while they remain on the web page. But unfortunately the chat room is still not complete, because it needs to handle errors and non transient failures.
Greg proposes a number of additions to the WebSocket protocol to make things better for everyone in the future:
- Ideally a future version of websocket will support timeout discovery, so it can either tell the application the period for keep-alive messages or it could even send the keep alives on behalf of the application.
- Ideally a future version of websocket will support an orderly close message so the application can distinguish between a network failure (and keep the user's presence for a time) and an orderly close as the user leaves the page (and remove the user's present).
- Ideally a future version of websocket will support orderly close, so that delivery can be known for non failed connections and a complication of acknowledgements can be avoided unless the highest quality of service is required.
- Ideally a future version of websocket will allow more access to connection errors, as the handling of no-route-to-host may be entirely different to handling of a 401 unauthorized response from the server.
- Ideally a future version of websocket will be able to send an error status as something distinct from a network failure or idle timeout, so the application will know not to retry errors.
He concludes with a summary and a note of hope for the future:
What this blog illustrates is that there is no silver bullet and that WebSocket will not solve many of the complexities that need to be addressed when developing robust comet web applications. Hopefully some features such as keep alives, timeout negotiation, orderly close and error notification can be built into a future version of websocket, but it is not the role of websocket to provide the more advanced handling of queues, timeouts, reconnections, retries and backoffs. If you wish to have a high quality of service, then either your application or the framework that it uses will need to deal with these features.
CometD version 2 will soon be released with support for websocket as an alternative transport to the currently supported JSON long polling and JSONP callback polling. CometD supports all the features discussed in this blog and makes them available transparently to browsers with or without websocket support. We are hopeful that websocket usage will be able to give us even better throughput and latency for cometd than the already impressive results achieved with long polling.
Jetty 8.0.0 M0 has since been released, which brings WebSocket support to Jetty and also support for the Servlet 3.0 API.
Stephanie Davis (nee Stewart) Dec 21, 2014