ws4py is eager for a new maintainer

Years ago, I got really interested in the WebSocket protocol that eventually landed as RFC 6455. It was the first time I would spend so much time trying to participate elaborating a protocol. Obviously, my role was totally minor but the whole experience was both frustrating and exhilarating at the same time.

I thought the protocol was small enough that it would be a good scope for a Python project: ws4py. Aside from implementing the core protocol, what I was interested in wa two folds. First, relying on Python generators all the way down as a control flow mechanism. Second, I wanted to decouple the socket layer from the interface so I could more easily write tests through fake WebSocket objects. Indeed, as any network protocol, the devil is in the details and WebSocket is no different. There are a few corner cases that would have been hard to test with a socket but were trivial with an interface.

Did I succeed? Partly. In insight, my design was not perfect and I made a couple of mistakes:

  • I relied too much on an OOP design for high-level interface. Quickly I realised I could, and should, have used a more functional approach. After all, WebSockets are merely event handlers and a functional approach would have made more sense. With that said, Python is not a functional language, it has a few features but the ecosystem is not entirely driven that way so, at the time, that might have made the library adoption more difficult.
  • I sort of wrote my internal event loop abstraction on top of select, epoll… One goal I had set to myself is not to rely on any external dependency for the library. At least, I wanted to make sure the library could be used as-is for quick and dirty tests. That’s a bad idea for things as complex as proper event looping. Eventually, I provided support for gevent and asyncio (called tulip back then) though. Still, there is a chunk of the code that could be made simpler and more robust if we changed that.
  • Though I did decouple the socket from the interface and provided a nicer way of testing the protocol, the socket interface still leaks here and there making the code not as tight as it could be.

I’ve started that project years ago and I haven’t really paid attention to it for the last two years. Some folks, using the library, have been getting restless and no doubt frustrated by my lack of commitment to it. I want to be honest, I’ve lost interest in that project, I’ve moved to other puzzles that have picked my brain and I don’t have the energy for ws4py any longer.

Is the project still relevant anyway? Well, GitHub tells me it is starred by almost 800 individuals. That’s not massive but it’s decent for sure, the highest rank for any my projects. Also, it’s the only websocket library that runs with CherryPy.

Is WebSocket relevant still? That’s not for me to say. Some people claim it’s dying a slow death due to HTTP/2. Honestly, I have no idea. WebSocket, much like the Atom Publishing Protocol (another protocol I cared for), didn’t do as well as their authors may have expected initially.

Anyhow, I think I should be passing the relay to someone else who is motivated to take the project on. I’m not sure how this will happen but I would like to think we can be as transparent as we can about this. Please use the comments below or the mailing-list to discuss your interest.

If by the end of February, no one has showed any interest, I will deprecate the project officially so people can gradually move on. The project will stay on GitHub but it’ll be clear that no further changes or releases will be done.

It’s sad to let go a project you cared for but it’s only fair to the community to be transparent when you’ve lost the energy for it. Long live ws4py!

Having fun with WebSocket and Canvas

Recently, I was advised that WebFaction had added support for WebSocket in their custom applications by enabling the according nginx module in their frontend. Obviously, I had to try it out with my own websocket library: ws4py.

Setting up your WebFaction application

Well, as usual with WebFaction, setting up the application is dead simple. Only a few clicks from their control panel.

Create a Custom application and select websocket. This will provide you with a port that your backend will be bound to. And voilà.

Now, your application is created but you won’t yet be able to connect a websocket client. Indeed, you must associate a domain or subdomain with that application.

It is likely your application will be used from a javascript connector in living in a browser, which means, you will be bound by the browser same-origin security model. I would therefore advise you to carefully consider your sub-domain and URL strategies. Probably something along:

  • http://yourhost/ : for the webapp
  • http://yourhost/ws : as the base url for all websocket endpoints

This is just a suggestion of course but this will make it easier for your deployment to follow a simple strategy like this one.

In the WebFaction control panel, create a website which associates your web application with the domain (your webapp can be anything you need to). Associate then your custom websocket application with the same domain but a different path segment. Again, by sharing the same domain, you’ll avoid troubles regarding working around the same-origin security model. I would probably advise as well that you enable SSL but it’s up to you to make that decision.

Once done, you will have now a configured endpoint for your websocket application.

The Custome websocket application will forward all requests to you so that you can run your web and websocket apps from that single port. This is what the demo below allows itself doing. I would recommend that you run two different application processes, one for your webapp and another one for your websocket endpoint. Be antifragile.

Drawing stuff collaboratively

I setup a small demo (sorry self-signed certificate) to demonstrate how you can use HTML5 canvas and websocket features to perform collaborative tasks across various connected clients.

That demo runs a small webapp that also enables a websocket endpoint. When you are on the drawing board, everything you draw is sent to other connected clients so that their view reflects what’s happening on yours. Obviously this goes in any way frm any client to any other clients.

The demo is implemented using ws4py hosted within a CherryPy application. Drawing events are serialized into a json structure and sent over to the server which dispatches them to all participants of that board, and only that board (note, this demo doesn’t validate the content before dispatching back so please conservative with whom you share your board with).

Open the link found in the demo page and share it on as many browsers as you can (including your mobile device). Starting drawing from one device will make all other devices been drawn onto simultaneously and synchronously. Note that the board stays available for up to 5mn only and will disconnect all participants then.

The source code for the demo is located here.

Some feedback…

Let me share a bit of feedback about the whole process.

  • WebSockets are finally a reality. Unless you’re running an old browser or old mobile platform, RFC6455 is available to you.  This means, you can really leverage the power of push from the server. Mind you, you might want to look at Server-Side Event  as well.
  • There isn’t yet a clear understanding on how to properly configure your server resources. In my demo, the whole webapp also hosts the websocket app but this is probably not a good idea if you have a large amount of connected clients or intensive work done server side. Even though the initial connection is initiated from a HTTP request, I would suggest the websocket server is disconnected from the HTTP server process.
  • Security wise, I would suggest you follow the usual principles of validating that any data coming through before you process or dispatch them back.
  • WebFaction supports for websocket is dead easy to setup and fast (at least, since my demo is hosted in Europe and I live in France, I almost cannot see any delay). I would consider their performances good enough to support some really funky real-time applications.
  • jCanvas is really useful to unify your canvas operations.  For this demo, it’s been a blessing.
  • Device motion events are low level and you need to do a lot of leg work to actually make sense of them. This demo is probably not making a really good use of them.
  • There seems to be no universal way to detect that you are running on a mobile device. Go figure.

Next, I wouldn’t mind adding websocket to that fun demo from the mozilla developer network.

The joy of distributing Python packages for Python 2 and 3

I released ws4py 0.3.4 this weekend and although I had integrated support for Python 2 and 3 for a long time now, I ran into a challenge I had quite missed. Indeed, until now my Python 3 support had mainly been concerned about string handling and various compatibility modules. This has proven to work very well and avoided having to rely on external packages such as six.

However, in the past few weeks and I added asyncio support to ws4py and therefore introduced the newly yield from statement. Of course, this isn’t tolerated by Python 2 which complains with a well deserved SyntaxError.

The issues however is that I wished to distribute the same source code with a single source distribution archive. Initially, I had written a function that was preventing modules using that statement to actually be packaged. However, this was rather daft since that, if it weren’t packaged, it wouldn’t be distributed either. Next, I decided to some of setuptools magic. Well, it didn’t help since that’s not what it’s there for anyhow. At this stage, I should say: Don’t simply copy/paste. It will do no good.

Finally, I opted for a fairly simple solution. I knew that when a package is installed from a distribution packages, it is obviously built first. I therefore had to act after Python modules had been gathered but before they would be built. After briefly browsing through distutils source code, I found where I would perform surgery: the find_package_modules of the distutils.command.build_py.build_py class. The nice aspect of this solution is that the source distribution contains indeed all the modules, whether they aim Python 2 or 3 but it’s only when installed that the appropriate modules will be selected and built.

Am I doing it wrong? Is there a cleaner, nicer more pythonic way? If so, please let me know. If not, I hope this may help others that want a simple solution to handle their Python 2 and 3 modules in a single baseline.

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