ws4py – WebSocket client and server library for Python

Recently I released ws4py, a package that provides client and server WebSocket support for Python 2.6 and 2.7.

Let’s first have a quick overview of what ws4py offers for now:

  • WebSocket specification draft-10 of the current specification.
  • A threaded client. This gives a simple client that doesn’t require an external dependency.
  • A Tornado client. This client is based on Tornado 2.0 which is quite a popular way of running asynchronous networking code these days. Tornado provides its own server implementation so I didn’t include mine in ws4py.
  • A CherryPy extension so that you can integrate WebSocket from within your CherryPy 3.2.1 server.
  • A gevent server based on the popular gevent library. This is courtesy of Jeff Lindsay.
  • Based on Jeff’s work, a pure WSGI middleware as well (available in the current master branch only until the next release).
  • ws4py runs on Android devices thanks to the SL4A package

Hopefully more client and servers will be added along the way as well as Python 3.x support. The former should be rather simple to add due to the way I designed ws4py.

The main idea is to make a distinction between the bytes provider and the bytes processing. The former is essentially reading and writing bytes from the connected socket. The latter is the function of making something out of the received bytes based on the WebSocket specification. In most implementations I have seen so far, both are rather heavily intertwined making it difficult to use a different bytes provider.

ws4py tries a different path by relying on a great feature of Python: the possibility to send data back to a generator. For instance, the frame parsing yields the quantity of bytes each time it needs more and the caller feeds back the generator those bytes once they are received. In fact, the caller of a frame parser is a stream object which acts the same way. The caller of that stream object is in fact the bytes provider (a client or a server). The stream is in charge of aggregating frames into a WebSocket message. Thanks to that design, both the frame and stream objects are totally unaware of the bytes provider and can be easily adapted in various contexts (gevent, tornado, CherryPy, etc.).

On my TODO list for ws4py:

  • Upgrade to a more recent version of the specification
  • Python 3.x implementation
  • Better documentation, read, write documentation.
  • Better performances on very large WebSocket messages

This entry was posted in android, python, websockets, ws4py and tagged , , . Bookmark the permalink.

17 Responses to ws4py – WebSocket client and server library for Python

  1. Pingback: Sylvain Hellegouarch: ws4py – WebSocket client... | Python | Syngu

  2. Mohamed says:

    On what browser is the echo_cherrypy_server example supposed to work?

    I tried it on 14.0.835.157 beta-m, on Windows 7, 64bit, and it does not work…
    After entering some text and pressing the submit button, nothing happens.
    No errors in the console on chrome, or of cherrypy.

    Thanks.

    • I’m now wondering if it’s not because draft-10 describes the deflate-stream extension which has been dropped from draft-11 and that ws4py doesn’t implement. In such case, it’d be more accurate to say that ws4py implements draft-11 only.

  3. Timothy M. Shead says:

    Many thanks for ws4py … I can confirm that it works great with cherrypy 3.2.0 and Chrome 14.0.835.159 beta on OSX. It also works great with Firefox 7.0 on OSX, with a small modification … Firefox sends

    Connection:keep-alive, Upgrade

    in the initial handshake, and the current logic at ws4py/server/cherrypyserver.py:125 will only accept

    Connection: Upgrade

    I don’t know enough about the relevant standards to say who should change, but it’s an easy tweak to ws4py if you’re interested.

    Finally, a dumb question: how should I initialize a handler derived from WebSocketHandler? I want to pass arguments to its constructor, but since it’s instantiated by WebSocketTool I don’t see how to get to the new instance to hook it into the rest of my application.

    Cheers!
    Tim

    • Hey Tim,

      1. I will look at the connection header issue. But don’t hesitate to open a bug ticket on github.
      2. Indeed it seems you cannot pass an instance itself. I may have to change that so that both can be done. In the mean time, off the top of my head, I’d say you can perform operations on the created instance from your traditional CherryPy page handler.

      Look at line 369 in https://github.com/Lawouach/WebSocket-for-Python/blob/master/ws4py/server/cherrypyserver.py#L369

      You see you have access to the handler instance and therefore could do whatever you want with it. Note that the handler is also available from the WebSocketHandlerPlugin.

      • Timothy M. Shead says:

        Sylvain:

        Thanks for the quick response, I filed the ticket. In the meantime, I figured-out that I can access the handler using cherrypy.request.ws_handler, and it’s working well – I’ve got a simple application that delivers images to the client running on my localhost.

        In the long run, I need to deploy my little app behind an Apache reverse proxy that provides SSL and authentication. Any issues you’re aware of? I have some vague idea that secure sockets are required to go through a proxy, are they supported via cherrypy + ws4py?

        Many thanks,
        Tim

        • Hey Tim,

          Thanks for the ticket.

          As for deployment, I haven’t tried but I’m not certain how reverse proxying and WebSocket will fare together. CherryPy does support SSL but I’ll admit I haven’t yet tried secure WebSocket connection.

          • Timothy M. Shead says:

            Sylvain:

            Well, the good news is that, for a CherryPy server configured to use SSL, secure WebSocket connections seem to work.

            This is all running on the same host, next step is to try and make it work through my Apache proxy.

            Cheers,
            Tim

        • Hey Timothy,

          maybe stunnel can be a help for you:
          http://www.stunnel.org/

          Basically, you can make stunnel accept https connections via e.g.
          accept = 0.0.0.0:443
          and forward them decrypted via e.g.
          connect = localhost:1234
          to your application. By doing so, the SSL encryption is transparent to your Python application which also applies to open websockets if I understand things correctly. Of course this requires a careful security considerations behind your load balancing/stunnel (at least that your Python app must not reachable from the outside).

          stunnel’s use with respect to load balancing and websockets is discussed for example here:

          http://www.mail-archive.com/haproxy@formilux.org/msg02799.html

          http://www.mail-archive.com/haproxy@formilux.org/msg02679.html

          Hope that somehow helps..

          Jan-Philip

          Btw: Thanks to Sylvain for this library. Currently, I am using gevent-websocket, but we all should stick to some standard and so I think I will be using ws4py, too :-)

  4. Mark Place says:

    Hello Sylvain,
    Thanks so much for your great work on this library. I’m pretty new to Python but have learned a lot from studying your code.

    I’ve having trouble getting the chat example to work using CherryPy 3.2.2 and Chrome 18.0.1025.11 beta-m on WindowsXP. My clients connect just fine and are able to send messages. Publish() is called from ChatWebSocketHandler, and eventually calls output.append(). However, the message is never received on the browser side, even though TCPView shows that the connections are established.

    Any idea of what could be going wrong?

    Thanks again,
    Mark

  5. Mark Place says:

    I may have narrowed it down a bit.

    isinstance(data, bytearray) is returning false. isinstance(data.data, bytearray) returns true, however.

    Not sure what this means. BTW I’m running Python 2.6.

  6. David Brooks says:

    Just getting my head around Web Sockets and ws4py. I had a gevent-based test server working using a WSGI function to send data based on URL, but with your recent code changes it appears that everything has to happen inside some sub-class of WebSocket, with no apparent way of getting the environment. Am I missing something? What about having **kwds on the __call__ of WebSocketUpgradeMiddleware and passing them onto websocket_class ??

    Thanks for the work.
    Dave

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">