Skip to content

Commit

Permalink
Documentation and some minor code cleanup.
Browse files Browse the repository at this point in the history
  • Loading branch information
Serge S. Koval committed Jan 27, 2011
1 parent 592838c commit 96b38d5
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 12 deletions.
131 changes: 129 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
TornadIO
========

TBD

Contributors
------------

Expand All @@ -16,3 +14,132 @@ Authors of SocketTornad.IO project:

- Brendan W. McAdams bwmcadams@evilmonkeylabs.com
- `Matt Swanson <http://github.com/swanson>`_

This is implementation of the `Socket.IO <http://socket.io>` realtime
transport library on top of the `Tornado <http://www.tornadoweb.org>` framework.

Short Background
----------------

There's already library that implements Socket.IO integration with Tornado
framework - `SocketTornad.IO <http://github.com/SocketTornad.IO/>`, but
it was not finished. Also, I did not like how it is designed, so instead
of writing patches for the original library, decided to implement one
from scratch.

TornadIO is different from SocketTornad.IO library in following aspects:

- Simplier internal design, easier to maintain/extend
- No external dependencies (except of the Tornado itself and simplejson on python < 2.6)
- Properly handles on_open/on_close events for polling transports
- Proper Socket.IO protocol parser
- Proper unicode support
- Actively maintained

Introduction
------------

In order to start working with the TornadIO library, you need to know some basic concepts
on how Tornado works. If you don't, please read Tornado tutorial, which can be found
`here <http://www.tornadoweb.org/documentation#tornado-walkthrough>`.

If you're familiar with Tornado, do following to add support for Socket.IO to your application:

1. Derive from tornadio.SocketConnection class and override on_message method (on_open/on_close are optional)::

class MyConnection(tornadio.SocketConnection):
def on_message(self, message):
pass

2. Create handler object that will handle all `socket.io` transport related functionality::

MyRouter = tornadio.get_router(MyConnection)

3. Add your handler routes to the Tornado application::

application = tornado.web.Application(
[ChatRouter.route()],
socket_io_port = 8000)

4. Start your application
5. You have your `socket.io` server running at port 8000. Simple, right?

Goodies
-------

``SocketConnection`` class implements three overridable methods:

1. ``on_open`` called when new client connection was established.
2. ``on_message`` called when message was received from the client. If client sent JSON object,
it will be automatically decoded into appropriate Python data structures.
3. ``on_close`` called when client connection was closed (due to network error or timeout)


Each ``SocketConnection`` has ``send()`` method which is used to send data to this client. Input parameter
can be one of the:

1. String/unicode string - sent as is (though with utf-8 encoding)
2. Arbitrary python object - encoded as JSON string automatically
3. List of python objects/strings - encoded as series of the socket.io messages using one of the rules above.

Configuration
-------------

You can configure your handler by passing settings to the ``get_router`` function as a ``dict`` object.

- **enabled_protocols**: This is a ``list`` of the socket.io protocols the server will respond requests for.
Possibilities are:
- *websocket*: HTML5 WebSocket transport
- *flashsocket*: Flash emulated websocket transport. Requires Flash policy server running on port 843.
- *xhr-multipart*: Works with two connections - long GET connection with multipart transfer encoding to receive
updates from the server and separate POST requests to send data from the client.
- *xhr-polling*: Long polling AJAX request to read data from the server and POST requests to send data to the server.
If message is available, it will be sent through open GET connection (which is then closed) or queued on the
server otherwise.
- *jsonp-polling*: Similar to the *xhr-polling*, but pushes data through the JSONp.
- *htmlfile*: IE only. Creates HTMLFile control which reads data from the server through one persistent connection.
POST requests are used to send data back to the server.


- **session_check_interval**: Specifies how often TornadIO will check session container for expired session objects.
In seconds.
- **session_expiry**: Specifies session expiration interval, in seconds. For polling transports it is actually
maximum time allowed between GET requests to consider virtual connection closed.
- **heartbeat_interval**: Heartbeat interval for persistent transports. Specifies how often heartbeat events should
be sent from the server to the clients.
- **xhr_polling_timeout**: Timeout for long running XHR connection for *xhr-polling* transport, in seconds. If no
data was available during this time, connection will be closed on server side to avoid client-side timeouts.

Starting Up
-----------

Best Way: SocketServer
^^^^^^^^^^^^^^^^^^^^^^

We provide customized version (shamesly borrowed from the SocketTornad.IO library) of the HttpServer, which
simplifies start of the your TornadIO server.

To start it, do following (assuming you created application object before)::

if __name__ == "__main__":
socketio_server = SocketServer(application)

Examples
--------

Chatroom Example
^^^^^^^^^^^^^^^^

There is a chatroom example application from the SocketTornad.IO library, contributed by
`swanson <http://github.com/swanson>`_. It is in the ``examples/chatroom`` directory.

Ping Example
^^^^^^^^^^^^

Simple ping/pong example to measure network performance. It is in the ``examples/ping`` directory.

Transports Example
^^^^^^^^^^^^^^^^^^

Simple ping/pong example with chat-like interface with selectable transports. It is in the
``examples/transports`` directory.
2 changes: 1 addition & 1 deletion examples/chatroom/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<script>
window.onload = function() {

var s = new io.Socket(window.location.hostname, {port: 8001, transports:['xhr-polling']});
var s = new io.Socket(window.location.hostname, {port: 8001, rememberTransport: false});
s.connect();

s.addEvent('connect', function() {
Expand Down
2 changes: 1 addition & 1 deletion tests/proto_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def test_decode():

# Test unicode decode
eq_(proto.decode(proto.encode(u'\u0430\u0431\u0432')),
[('~m~', u'\u0430\u0431\u0432')])
[('~m~', u'\u0430\u0431\u0432'.encode('utf-8'))])

# Test JSON decode
eq_(proto.decode(proto.encode({'a':'b'})),
Expand Down
8 changes: 1 addition & 7 deletions tornadio/persistent.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,16 @@ def open(self, *args, **kwargs):
self.connection.on_open(*args, **kwargs)

def on_message(self, message):
logging.debug('Message: %s', message)
self.async_callback(self.connection.raw_message)(message)

def on_close(self):
logging.debug('Closed')

self.connection.on_close()
self.connection.is_closed = True

self.connection.stop_heartbeat()

def send(self, message):
logging.debug('Send: %s (%s)', message, self)

self.async_callback(self.write_message)(proto.encode(message))

self.write_message(proto.encode(message))
self.connection.delay_heartbeat()

class TornadioFlashSocketHandler(TornadioWebSocketHandler):
Expand Down
2 changes: 1 addition & 1 deletion tornadio/proto.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def decode(data):
# Skip message type
idx += 3

msg_data = data[idx:idx + msg_len].decode('utf-8')
msg_data = data[idx:idx + msg_len]

if msg_data.startswith(JSON):
msg_data = json.loads(msg_data[3:])
Expand Down

0 comments on commit 96b38d5

Please sign in to comment.