Scalable, WSGI-compatible Websockets

I wanted to mess around with websockets, and create something cool and Eventlet-y for using them.  This was my first experience using websockets, and I found these sites helpful in learning about it.

One of the design patterns that I observed was that people were running their websocket server as a separate process on a separate port, because there are just enough differences between the websocket protocol and HTTP that it’s difficult to get them into a normal web server.  That’s crap; if websockets are going to replace xmlhttprequest, it has to be just as easy to write the server part of web sockets as it is to write a web page.

So here’s a websocket implementation that works inside (a slight modification to) WSGI.  It requires the latest development version of Eventlet (see sidebar to the right for instructions on getting that). It’s very very rough at this point, but it’s powerful enough that it seemed worth sharing this initial version.

Now, here’s the code for a simple echo server:

def handle(ws):
    while True:
        m = ws.wait()
        if m is None:
            break
        ws.send(m)

It shows the basics in a nutshell, which are incredibly simple:

  • call wait() to receive a message from the browser
  • call send() to send a message to the browser
  • wait() returns None when the browser closes the socket
  • return from the function to close the socket from the server side

You’re free to spawn off new greenthreads to do complex stuff with reading from and writing to the ws object concurrently; it will just work.

Here’s how you set up the server:

from eventlet.green import socket
from eventlet import wsgi
listener = socket.socket()
listener.bind(('localhost', 7000))
listener.listen(500)
wsgi.server(listener, WebSocketWSGI(handle, 'http://localhost:7000'))

That’s it! Most of that is socket creation boilerplate; the WebSocketWSGI class handles the work of converting an incoming WSGI request to a websocket call. The “http://localhost:7000/” is the websocket ORIGIN field, which needs to be hardcoded to some degree in order to prevent XSS attacks.

So, that’s how the code looks, but you want to run something right away. In your copy of the eventlet tree, run this command:

PYTHONPATH=. python examples/websocket.py

Now, fire up Google Chrome and navigate to http://localhost:7000.  It should show you a page that looks like this:

Google Chrome showing a graph of random values between 0 and 1It’s a graph that fills in dynamically based on values that it gets from the websocket.  The cool thing about this is that the HTML page and the websocket are both served from the same port, and the same WSGI application.  It simply dispatches to the websocket handler like any other url-path-based dispatcher.  It’s flashy, self-contained, and scalable, and all it took was ~30 lines of code.

Take a look at the code!  Have fun with it!  I’ll make a post shortly about the slight modifications I had to make to eventlet.wsgi to get this working seamlessly.

5 responses to this post.

  1. Posted by manatlan on April 13, 2010 at 11:11 am

    with latest eventlet dev version (from now)…
    >>> NameError: name ‘WebSocketWSGI’ is not defined

    it seems it doesn’t work

  2. Posted by Ryan Williams on May 7, 2010 at 10:30 pm

    That’s odd — WebSocketWSGI was always defined in the file itself, so it’s weird that it wouldn’t be able to find a definition that’s right above it. Nevertheless, Ben Ford and I have pulled the websocket support into the core of Eventlet now, and it certainly works. Please give it a shot again, and let me know if you have trouble with it.

  3. If it requires ‘(a slight modification to) WSGI’ then it isn’t compatible with WSGI. Thus it is deceiving to say it it WSGI compatible.

    Are you confusing a particular web servers implementation of a WSGI server with the WSGI specification itself. From all I have seen, it is not technically possible to run WebSocket protocol over a WSGI specification conforming implementation. That said, you might be able to hack some web servers that also support WSGI to support WebSocket at the same time, but that has nothing to do with the WSGI specification.

  4. Posted by Ryan Williams on January 12, 2011 at 3:26 am

    Thanks for popping in, Graham.

    This implementation works with a lot of WSGI middleware, that is what is meant by compatible. It definitely requires Eventlet.wsgi (or Spawning) as the web server, the point of the post is to spell that out. The technique is a possible direction for modifying WSGI to support websockets in the future.

  5. Posted by Ryan Williams on January 12, 2011 at 3:27 am

    And when I say “a lot of middleware” I’m hedging my words because I haven’t tested all of them. There is a lot of non-spec-compliant middleware out there, and I wouldn’t want to speak for it.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.