TorChannels (Simple Channels for Tornado)

This package provides a simple Channel-subscription service for use with Tornado.  This is a very simple API that provides Polling, Long-Polling and Streaming XMLHTTPReqest-style server-to-client channels (server "push") with relatively lightweight protocols and a fairly simple implementation.  It is not a replacement for a complex protocol such as Bayeux, it is intended for small projects where you just need some working Channel system, want to use Tornado as the back-end server, and want to use JQuery on the client.

Usage

This is engineering-prototype-level code.  You should be prepared to read all of the code and be able to debug it if you intend to include the code in a production project!  There is no warranty that the code will even work.

The package provides a Python Request Handler Mix-in for Tornado.  This is a base class that provides "unrestricted" channels.  To use it:

...
from torchannels import torchannel

class ChannelHandler( torchannel.ChannelMixin, basehandler.BaseHandler ):
def can_access( self, sub ):
'''Override to provide authenication for channels'''
if not(super(ChannelHandler,self).can_access(sub)):
return False
if my_logic_says_this_user_cannot_see_this_channel( sub ):
return False
return True

class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r"^/channels$", ChannelHandler),
(r"^/([-a-zA-Z]+)$",YourInitialUploadHandler),
]
settings = dict(
# Note: you must copy/link torchannel.js and json2.js
# into the static file directory!
static_path=os.path.join(os.path.dirname(__file__), "static"),
)
tornado.web.Application.__init__(self, handlers, **settings)

The ChannelMixin class provides basic subscription channels which can be "polled" (or streamed) from Javascript.  The Javascript implementation (using JQuery) provides a trivial API for subscribing/unsubscribing to particular channels on the server:

var subscriptions = $.torChannels( {
url: '/channels'
} );
var subtoken = subscriptions.subscribe( 'some-channel', function( msg ) { } );
subscriptions.unsubscribe( subtoken );

The subscribe call kicks off the "polling" of the channels from the server.  To use the Javascript you'll obviously need to include it in your page:

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
<script src="{{ static_url('json2.js') }}" type="text/javascript"></script>
<script src="{{ static_url('torchannel.js') }}" type="text/javascript"></script>

The json2.js file is from json.org, and provides safer JSON parsing and linearisation than JQuery.  Note the need to copy and/or link both json2.js and torchannel.js into your application's static-files directory.

You send messages to channels by creating regular Tornado request handlers; you use whatever logic you would like to determine whether the user can write to the channel (the customisation of  ChannelHandler shown above controls who can read a channel).  To write a message generate calls like so:

ChannelHandler.get_channel( channel_name ).send( { } )

Where the message sent must be a Python dictionary.  The dictionary will have "channel" and "ts" keys set by the channel on send (thus you cannot send the same dictionary object to multiple channels).

If your channel can do "state compression" so that users who join/rejoin late can see current state, encode this information in a series of messages which will replace all messages before the time the state is set for a client which queries with a timestamp of 0.0 (the initial query).

ChannelHandler.get_channel( channel_name ).set_state( messages )

There is a TODO item which describes having each channel have a different timestamp.  Without this you can expect problems with the state code, as if you join channel A, then later join channel B, the overall timestamp will not be 0.0, and so will re-download all of B's state.

Installation/Acquisition

This is still a very early alpha.  Expect the API to change rapidly and major bugs to remain!  The code is currently only available in bzr via:

bzr branch lp:torchannels

Put the top-level directory on your Python path and link the .js files into your application's static files directory.  The LaunchPad project is available for bug-tracking, merge requests and the like.

License

Most code in this project is Copyright Mike C. Fletcher 2009-2010.  Portions (the initial long-polling transport, which is now fairly hard to find in the codebase) are Copyright 2009 FriendFeed.  All of that is licensed for use under the terms of the Apache 2.0 license.

json2.js is declared to be in the public domain by json.org.