= Faye

* http://github.com/jcoglan/faye

Faye is a set of tools for dirt-simple publish-subscribe messaging
between web clients. It ships with easy-to-use message routing servers
for Node.js and Rack applications, and clients that can be used on
the server and in the browser.


== Introduction

Faye is an implementation of the Bayeux prototcol (http://svn.cometd.com/trunk/bayeux/bayeux.html),
a publish-subscribe messaging protocol designed primarily to allow
client-side JavaScript programs to send messages to each other with
low latency over HTTP. It also allows for server-side clients that let
your backend applications push data to the client side.

Bayeux works by letting clients publish and subscribe to named data
channels. For example, one client may publish a message:

clientA.publish('/foo', 'world');

And another client can subscribe to that channel to receive messages
that are published to it:

// alerts "world"
clientB.subscribe('/foo', function(message) {
alert(message.hello);
});

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 deployment under Passenger. It has since been ported to
Node.js which is better designed for handling highly concurrent traffic.
If you use the Ruby version, I recommend deploying it behind Thin, an
event-driver Ruby web server.

The two backends are architecturally identical, using evented messaging
throughout and maintaining subscription data in memory. Neither is geared
up yet for running multi-process servers, but their event-driven setup
should let them handle more concurrent connections than a typical
threaded server.


== Installation

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

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 message server
accepts requests on. Say you set this to <tt>/faye</tt>, the client script
will be available from <tt>/faye.js</tt> and should connect to <tt>/faye</tt>.

You should set up the client as follows:

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

<script type="text/javascript">
fayeClient = new Faye.Client('/faye');
</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:

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

fayeClient.publish('/some/other/channel', '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 <tt>/meta/</tt> are reserved for use by the messaging protocol
and may not be subscribed to.

The client can also be used cross-domain if connecting to a backend that
supports callback polling (see below under 'Transports'). Just pass in the
full path to the endpoint including the domain. Faye figures out whether the
server is on the same domain and uses an appropriate transport.

fayeClient = new Faye.Client('http://example.com/faye');


=== Transports

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

<tt>long-polling</tt> is where the client makes an <tt>XMLHttpRequest</tt>
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 server endpoint must be on the same
domain as the client page.

<tt>callback-polling</tt> 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 client supports this transport, as does the
Node.js backend. The Rack backend supports it if running under Thin.

Faye also supports an <tt>in-process</tt> transport, which is used when a
server-side client has direct in-memory access to the server without going
over HTTP.


== 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 Faye service is accessible. e.g. if
set to <tt>/faye</tt>, the Faye endpoint will be at <tt>http://yoursite.com/faye</tt>
and the client script at <tt>http://yoursite.com/faye.js</tt>.
* +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 messaging
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 <tt>faye.js</tt> and <tt>faye-client-min.js</tt> in the same
directory.

var http = require('http')
faye = require('./faye');

var server = new faye.NodeAdapter('/faye', timeout: 45);

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

response.sendHeader(200, {'Content-Type': 'text/plain');
response.write('Hello, non-Faye request!');
response.close();

}).listen(9292);


=== Node.js client

Faye's JavaScript client can be used server-side under Node. There are
two ways you can set a client up: either connect to as remote server
over HTTP or connect directly to the <tt>NodeAdapter</tt>. Either way,
the API for the client is exactly as it is in the browser.

// Remote client
client = new Faye.Client('http://example.com/faye');

// Local client
server = new Faye.NodeAdapter(options);
client = server.getClient();

Note <tt>getClient()</tt> returns the same client object every time you
call it, so any subscriptions you make with it are retained.


=== 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 <tt>config.ru</tt> for running it with Sinatra:

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

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

run Sinatra::Application

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


=== Rack client

Again mirroring the Node tools, Faye has a client-side Ruby client that
can be used under EventMachine. Setup is identical:

# Remote client
client = Faye::Client.new('http://example.com/faye')

# Local client
server = Faye::RackAdapter.new(options)
client = server.get_client

Both types of client must be run inside EventMachine, and can publish and
subscribe just like the JavaScript client:

EM.run {
client.subscribe('/from/*') do |message|
# do something with message hash
end

client.publish('/from/jcoglan', 'hello' => 'world')
}

If you're using the <tt>RackAdapter</tt> as middleware and running your app
using Thin, applications further down the chain can get a client for it from
the Rack environment, for example in a Sinatra application:

get '/post' do
env['faye.client'].publish('/mentioning/*', {
'user' => 'sinatra',
'message' => params[:message]
})
params[:message]
end


== Development

If you want to hack on Faye, you'll need Node.js, Ruby and the following
gems installed:

sudo gem install eventmachine em-http-request rack thin json jake

DO NOT edit any JavaScript files outside the +javascript+ and +test+
directories. They are generated using Jake, which you should run after
editing the JavaScript source.

jake -f

Ordinarily I would keep generated files out of the repo but I'm keeping
some in here as it's currently the only place to download them.

If you want to submit patches, they're far more likely to make it in
if you update both the Ruby and JavaScript versions with equivalent
changes where appropriate.


== To-do

* Investigate WebSockets as a possible message transport
* Provide support for user-defined <tt>/service/*</tt> channels
* Allow server to scale to multiple nodes


== License

(The MIT License)

Copyright (c) 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.