Skip to content

Commit 96b38d5

Browse files
author
Serge S. Koval
committed
Documentation and some minor code cleanup.
1 parent 592838c commit 96b38d5

File tree

5 files changed

+133
-12
lines changed

5 files changed

+133
-12
lines changed

README.rst

+129-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
TornadIO
33
========
44

5-
TBD
6-
75
Contributors
86
------------
97

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

1715
- Brendan W. McAdams [email protected]
1816
- `Matt Swanson <http://github.com/swanson>`_
17+
18+
This is implementation of the `Socket.IO <http://socket.io>` realtime
19+
transport library on top of the `Tornado <http://www.tornadoweb.org>` framework.
20+
21+
Short Background
22+
----------------
23+
24+
There's already library that implements Socket.IO integration with Tornado
25+
framework - `SocketTornad.IO <http://github.com/SocketTornad.IO/>`, but
26+
it was not finished. Also, I did not like how it is designed, so instead
27+
of writing patches for the original library, decided to implement one
28+
from scratch.
29+
30+
TornadIO is different from SocketTornad.IO library in following aspects:
31+
32+
- Simplier internal design, easier to maintain/extend
33+
- No external dependencies (except of the Tornado itself and simplejson on python < 2.6)
34+
- Properly handles on_open/on_close events for polling transports
35+
- Proper Socket.IO protocol parser
36+
- Proper unicode support
37+
- Actively maintained
38+
39+
Introduction
40+
------------
41+
42+
In order to start working with the TornadIO library, you need to know some basic concepts
43+
on how Tornado works. If you don't, please read Tornado tutorial, which can be found
44+
`here <http://www.tornadoweb.org/documentation#tornado-walkthrough>`.
45+
46+
If you're familiar with Tornado, do following to add support for Socket.IO to your application:
47+
48+
1. Derive from tornadio.SocketConnection class and override on_message method (on_open/on_close are optional)::
49+
50+
class MyConnection(tornadio.SocketConnection):
51+
def on_message(self, message):
52+
pass
53+
54+
2. Create handler object that will handle all `socket.io` transport related functionality::
55+
56+
MyRouter = tornadio.get_router(MyConnection)
57+
58+
3. Add your handler routes to the Tornado application::
59+
60+
application = tornado.web.Application(
61+
[ChatRouter.route()],
62+
socket_io_port = 8000)
63+
64+
4. Start your application
65+
5. You have your `socket.io` server running at port 8000. Simple, right?
66+
67+
Goodies
68+
-------
69+
70+
``SocketConnection`` class implements three overridable methods:
71+
72+
1. ``on_open`` called when new client connection was established.
73+
2. ``on_message`` called when message was received from the client. If client sent JSON object,
74+
it will be automatically decoded into appropriate Python data structures.
75+
3. ``on_close`` called when client connection was closed (due to network error or timeout)
76+
77+
78+
Each ``SocketConnection`` has ``send()`` method which is used to send data to this client. Input parameter
79+
can be one of the:
80+
81+
1. String/unicode string - sent as is (though with utf-8 encoding)
82+
2. Arbitrary python object - encoded as JSON string automatically
83+
3. List of python objects/strings - encoded as series of the socket.io messages using one of the rules above.
84+
85+
Configuration
86+
-------------
87+
88+
You can configure your handler by passing settings to the ``get_router`` function as a ``dict`` object.
89+
90+
- **enabled_protocols**: This is a ``list`` of the socket.io protocols the server will respond requests for.
91+
Possibilities are:
92+
- *websocket*: HTML5 WebSocket transport
93+
- *flashsocket*: Flash emulated websocket transport. Requires Flash policy server running on port 843.
94+
- *xhr-multipart*: Works with two connections - long GET connection with multipart transfer encoding to receive
95+
updates from the server and separate POST requests to send data from the client.
96+
- *xhr-polling*: Long polling AJAX request to read data from the server and POST requests to send data to the server.
97+
If message is available, it will be sent through open GET connection (which is then closed) or queued on the
98+
server otherwise.
99+
- *jsonp-polling*: Similar to the *xhr-polling*, but pushes data through the JSONp.
100+
- *htmlfile*: IE only. Creates HTMLFile control which reads data from the server through one persistent connection.
101+
POST requests are used to send data back to the server.
102+
103+
104+
- **session_check_interval**: Specifies how often TornadIO will check session container for expired session objects.
105+
In seconds.
106+
- **session_expiry**: Specifies session expiration interval, in seconds. For polling transports it is actually
107+
maximum time allowed between GET requests to consider virtual connection closed.
108+
- **heartbeat_interval**: Heartbeat interval for persistent transports. Specifies how often heartbeat events should
109+
be sent from the server to the clients.
110+
- **xhr_polling_timeout**: Timeout for long running XHR connection for *xhr-polling* transport, in seconds. If no
111+
data was available during this time, connection will be closed on server side to avoid client-side timeouts.
112+
113+
Starting Up
114+
-----------
115+
116+
Best Way: SocketServer
117+
^^^^^^^^^^^^^^^^^^^^^^
118+
119+
We provide customized version (shamesly borrowed from the SocketTornad.IO library) of the HttpServer, which
120+
simplifies start of the your TornadIO server.
121+
122+
To start it, do following (assuming you created application object before)::
123+
124+
if __name__ == "__main__":
125+
socketio_server = SocketServer(application)
126+
127+
Examples
128+
--------
129+
130+
Chatroom Example
131+
^^^^^^^^^^^^^^^^
132+
133+
There is a chatroom example application from the SocketTornad.IO library, contributed by
134+
`swanson <http://github.com/swanson>`_. It is in the ``examples/chatroom`` directory.
135+
136+
Ping Example
137+
^^^^^^^^^^^^
138+
139+
Simple ping/pong example to measure network performance. It is in the ``examples/ping`` directory.
140+
141+
Transports Example
142+
^^^^^^^^^^^^^^^^^^
143+
144+
Simple ping/pong example with chat-like interface with selectable transports. It is in the
145+
``examples/transports`` directory.

examples/chatroom/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<script>
88
window.onload = function() {
99

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

1313
s.addEvent('connect', function() {

tests/proto_test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def test_decode():
3434

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

3939
# Test JSON decode
4040
eq_(proto.decode(proto.encode({'a':'b'})),

tornadio/persistent.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,16 @@ def open(self, *args, **kwargs):
3939
self.connection.on_open(*args, **kwargs)
4040

4141
def on_message(self, message):
42-
logging.debug('Message: %s', message)
4342
self.async_callback(self.connection.raw_message)(message)
4443

4544
def on_close(self):
46-
logging.debug('Closed')
47-
4845
self.connection.on_close()
4946
self.connection.is_closed = True
5047

5148
self.connection.stop_heartbeat()
5249

5350
def send(self, message):
54-
logging.debug('Send: %s (%s)', message, self)
55-
56-
self.async_callback(self.write_message)(proto.encode(message))
57-
51+
self.write_message(proto.encode(message))
5852
self.connection.delay_heartbeat()
5953

6054
class TornadioFlashSocketHandler(TornadioWebSocketHandler):

tornadio/proto.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def decode(data):
6565
# Skip message type
6666
idx += 3
6767

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

7070
if msg_data.startswith(JSON):
7171
msg_data = json.loads(msg_data[3:])

0 commit comments

Comments
 (0)