Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Dirt-simple publish/subscribe messaging over HTTP for Node.js and Rack

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
build
examples
javascript
lib
test
.autotest
.gitignore
History.txt
Jakefile
Manifest.txt
README.rdoc
Rakefile
jake.yml

README.rdoc

Faye

Faye provides dirt-simple Comet messaging based on the Bayeux protocol. It ships with a self-contained client for use in the browser and two message-routing backends; one for Node.js and one for Rack.

Introduction

Comet is an umbrella term for a collection of techniques that let web servers push data to clients, allowing for low-latency data transfer. Bayeux is a protocol designed to support publish/subscribe-style messaging between web clients and servers. Clients can subscribe to named channels and publish messages, each message being distributed to all clients subscribed to its target channel. See svn.cometd.com/trunk/bayeux/bayeux.html

Faye's messaging backend was originally developed in Ruby for a toy project of mine, but was not written to scale across multiple processes as required for scaling a Ruby server. It has since been ported to Node.js which should handle the required connection load more gracefully.

The two backends are architecturally identical, using evented messaging throughout and maintaining subscription data in memory. Node.js allows for far more concurrent connections per process than a typical Ruby server, and will get you more mileage from a single process.

Installation

The JavaScript client and Node.js server are in the build directory. Just copy them onto your machine and require('path/to/faye').

The Rack server is distributed as a Ruby gem:

sudo gem install faye

Using the client

Both backends allow you to specify a 'mount point' that the Comet server accepts requests on. Say you set this to /comet, the client script will be available from /comet.js and should connect to /comet.

You should set up the client as follows:

<script type="text/javascript" src="/comet.js"></script>

<script type="text/javascript">
  CometClient = new Faye.Client('/comet');
  CometClient.connect();
</script>

Take care only to have one instance of the client per page; since each one opens a long-running request you will hit the two-requests-per-host limit and block all other Ajax calls if you use more than one client.

This client object can be used to publish and subscribe to named channels:

CometClient.subscribe('/path/to/channel', function(message) {
  // process received message object
});

CometClient.publish('/some/other/channel', {foo: 'bar'});

You can publish arbitrary JavaScript objects to a channel, and the object will be transmitted as the message parameter to any subscribers to that channel. Channel names must be formatted as absolute path names as shown. Channels beginning with /meta/ are reserved for use by the messaging protocol and may not be subscribed to.

Transports

The Bayeux spec defines several transport mechanisms for clients to establish low-latency connections with the server, the two required types being long-polling and callback-polling.

long-polling is where the client makes an XMLHttpRequest to the server, and the server waits until it has new messages for that client before it returns a response. Faye's client and server backends all support this transport. Since it uses XHR, the Comet endpoint must be on the same domain as the client page.

callback-polling involves the client using JSON-P to make the request. The server wraps its JSON responses in a JavaScript function call that the client then executes. This transport does not require the client and server to be on the same domain. Faye's server backends support it, but the client is currently a little buggy in some browsers.

Using the backend

Both the Node.js and Rack backends have identical architectures and are designed to be easily plugged into other web services. For Rack the adapter is explicitly designed as middleware, while for Node.js the adapter is a simple object you can manually offload requests to.

The backends provide a service for routing messages between clients; no server-side programming is needed to control them, just start them up and they'll sit there merrily chewing through requests. They are both currently single-process since they hold all channel subscriptions in memory. This means, for example, that the Rack backend will not work under Passenger since that spawns multiple Ruby processes to serve your site.

Faye uses async messaging internally so nothing blocks while waiting for new messages. If running under a Ruby web server (except Thin) the server will block while waiting for a response from Faye. Thin supports async responses and is a better choice for long-running concurrent connections.

Both backends support the following initialization options:

  • mount - the path at which the Comet service is accessible. e.g. if set to /faye, the Comet endpoint will be at yoursite.com/faye and the client script at yoursite.com/faye.js.

  • timeout - the maximum time (seconds) to hold a long-running request open before returning a response. This must be smaller than the timeout on your frontend webserver to make sure Faye sends a response before the server kills the connection.

Usage examples and a demo app are in the examples directory.

Node.js backend

Here's a very simple Node web server that offloads requests to the Comet service to Faye and deals with all other requests itself. The Faye object returns true or false to indicate whether it handled the request. You'll need faye.js and faye-client-min.js in the same directory.

var posix = require('posix'),
    path  = require('path'),
    http  = require('http')
    faye  = require('./faye');

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

http.createServer(function(request, response) {
  if (comet.call(request, response)) return;

  response.sendHeader(200, {'Content-Type': 'text/plain'});
  response.sendBody('Hello, non-Comet request!');
  response.finish();

}).listen(9292);

Rack backend

Faye can be installed as middleware in front of any Rack application. The Rack backend uses EventMachine for asynchronous message distribution and timeouts. It can run under any web server, though Thin is best placed for handling long-running concurrent connections and supports async server responses. Under other servers the request thread will block while waiting for a response from Faye.

Here's a config.ru for running it with Sinatra:

require 'rubygems'
require 'faye'
require 'sinatra'
require 'path/to/sinatra/app'

use Faye::RackAdapter, :mount   => '/comet',
                       :timeout => 25

run Sinatra::Application

This functions much the same as the Node.js example; Faye catches Comet requests and deals with them, letting all other requests fall down the stack of Rack middlewares.

To-do

  • Fix callback-polling support in the client

  • Provide support for user-defined /service/* channels

  • Allow server to scale to multiple nodes

  • Provide a server-side client

Installation

sudo gem install hoe faye

License

(The MIT License)

Copyright © 2009-2010 James Coglan

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Something went wrong with that request. Please try again.