Skip to content

Autobahn

A package implementing web sockets and WAMP for python.

This package might be a little more up-to-date than the later examples. Autobahn not only implements the websocket protocol but also WAMP: the websocket application messaging protocol. WAMP uses json, and kees likes json. Also.. of all sites claiming to have a working tutorial, this is the only one that delivered, not a minor advantage. But let's not kid ourselves, i chose this because i love kraftwerk.

First get it :

install
pip install autobahn
pip install twisted

twisted

Twisted is an event-driven networking engine written in Python, it is used heavily in at least the demo's.

For web development twisted provides :

  • Twisted web, which is a simple (extremely) stable http server.
  • Divmod Nevow, is a template toolkit.
  • Divmod Mantissa, is an application server.

As i see it, twisted web will be the part that we use.

simple(st) example

Here is an example of an autobahn echo server :

example
#!/usr/bin/python
# reactor implements an 'event' loop (internally a 'select')
from twisted.internet import reactor
from autobahn.websocket import WebSocketServerFactory, \
                           WebSocketServerProtocol, \
                           listenWS

# create a custom protocol subclassed from WebSocketServerProtocol
class EchoServerProtocol(WebSocketServerProtocol):

    # with a handler for incoming messages
    def onMessage(self, msg, binary):
        self.sendMessage(msg, binary)

# detect standalone or imported version by checking __main__
if __name__ == '__main__':
    factory = WebSocketServerFactory("ws://localhost:9000")
    # and set our custom protocol 
    factory.protocol = EchoServerProtocol
    # than listen on it
    listenWS(factory)
    # start the reactor and select 
    reactor.run()

Let's break that down step by step, if this script is ran standalone (it is) create a WebSocketServerFactory at the specified address. The reactor does the actual server loop, and normally you should call .install() on some specific reactor (for instance pollreactor) to change the default reactor (which just loops without doing anything). I can only assume WebSocketServerFactory() or listenWS() calls it's .install() internally, so assume the loop now handles the WebSocketServerFactory, with our custom protocol.

You could use a browser script like this :

browser script
<!DOCTYPE html>
<html>
   <head>
      <script type="text/javascript">
         var sock = null;
         var wsuri = "ws://localhost:9000";

         window.onload = function() {

            sock = new WebSocket(wsuri);

            sock.onopen = function() {
               console.log("connected to " + wsuri);
            }

            sock.onclose = function(e) {
               console.log("connection closed (" + e.code + ")");
            }

            sock.onmessage = function(e) {
               console.log("message received: " + e.data);
            }
         };

         function send() {
            var msg = document.getElementById('message').value;
            sock.send(msg);
         };
      </script>
   </head>
   <body>
      <h1>WebSocket Echo Test</h1>
      <form>
         <p>
            Message:
            <input id="message" type="text" value="Hello, world!">
         </p>
      </form>
      <button onclick='send();'>Send Message</button>
   </body>
</html>

Put it somewhere reachable under apache and open it up in your browser. You should be able to send the message and see it coming in on the server.

We make a small input box, and connect it to the 'send()' function. onload() set's everything up before we can push and 'send()' so it opens the websocket to the running server, and we can send() our message onto this connected socket. This is all very C like, and i expect that from python:). As expected also, if you stop the server and start it again, the send message button does nothing until you refresh the page, thus reconnecting the socket.

Now extending this to use WAMP, see the next chapter

WAMP

server

The server gets augmented in a number of ways :

  1. We make it a WampServerFactory instead of a WebSocketServerFactory.
  2. We no longer use apache, but create a web server/site onto the current directory, on port 7070.

The new script :

WampServerFactory
#!/usr/bin/python
import sys, math

from twisted.python import log
from twisted.internet import reactor, defer
from twisted.web.server import Site
from twisted.web.static import File

from autobahn.websocket import listenWS
from autobahn.wamp import exportRpc, WampServerFactory, WampServerProtocol


class Calc:
    # this decorator makes this method ready for rpc 
    @exportRpc
    def add(self, x, y):
        return x + y

class SimpleServerProtocol(WampServerProtocol):
    def onSessionOpen(self):
        self.calc = Calc()
        self.registerForRpc(self.calc, "http://example.com/simple/calc#")

if __name__ == '__main__':

    if len(sys.argv) > 1 and sys.argv[1] == 'debug':
        log.startLogging(sys.stdout)
        debug = True
    else:
        debug = False

    factory = WampServerFactory("ws://localhost:9000", debugWamp = debug)
    factory.protocol = SimpleServerProtocol
    factory.setProtocolOptions(allowHixie76 = True)
    listenWS(factory)

    webdir = File(".")
    web = Site(webdir)
    reactor.listenTCP(7070, web)

    reactor.run()

index.html will change as well :

script
<!DOCTYPE html>
<html>
   <head>

      <!-- include AutobahnJS .. that's all you need -->
      <script src="http://autobahn.s3.amazonaws.com/js/autobahn.min.js"></script>

      <script>
         // WAMP session object
         var sess = null;

         window.onload = function() {

            var wsuri;
            if (window.location.protocol === "file:") {
               wsuri = "ws://localhost:9000";
            } else {
               wsuri = "ws://" + window.location.hostname + ":9000";
            }

            // connect to WAMP server
            ab.connect(wsuri,

               // WAMP session was established
               function (session) {

                  sess = session;

                  console.log("Connected to " + wsuri);
                  test();
               },

               // WAMP session is gone
               function (code, reason) {

                  sess = null;

                  if (code == ab.CONNECTION_UNSUPPORTED) {
                     window.location = "http://autobahn.ws/unsupportedbrowser";
                  } else {
                     alert(reason);
                  }
               }
            );
         };

         function test()
         {
            // establish a prefix, so we can abbreviate procedure URIs ..

            sess.prefix("calc", "http://example.com/simple/calc#");

            // call a function and log result on success
            sess.call("calc:add", 23, 7).then(ab.log);
        } 

      </script>
   </head>
   <body>
      <h1>RPCs with AutobahnJS - Example 2</h1>
      <noscript>
         <span style="color: #f00; font-weight: bold;">
            You need to turn on JavaScript.
         </span>
      </noscript>
      <p>
         Open development console (press F12) to watch.
      </p>
   </body>
 </html>

Since there is no output, the script will ask you to hit F12 to open the console, and there you will see that the server returned 30: 23 and 7 added.

complete install on amazon

As a reminder, this is how you login ;)

amazon install
cd ~/.ssh
ssh -i ec2solver.pem ubuntu@ec2-107-20-53-156.compute-1.amazonaws.com

Then some installation preliminaries :

amazon install
1
2
3
sudo su
apt-get install subversion
svn co https://klopt.googlecode.com/svn/web/cycles

Now we need autobahn :

autobahn
apt-get install python-pip
pip install autobahn