Saturday, February 18, 2012

Websockets Revisited - Part 1

So in my last post I naively stated that websockets were easy. What a difference a few days makes. It turns out I can only find two browsers for my MacBook Pro that currently support websockets and the code on both the client and the server must be different for each. As I mentioned to my friend Scott the other day, I hope this doesn't turn out like Ajax did where you have all sorts of cross browser compatibility issues because so far that is exactly what is happening.

A bit of background first. The websocket spec RFC 6455 (http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17) covers the websocket protocol. The first version "draft-ietf-hybi-thewebsocketprotocol-00" which is what my version of Safari (5.1.3) supports, framed its data using a 0x00 byte to start the frame and a 0xff byte to end the frame. It had two security keys in the handshake header and the response to these keys was calculated by concatenating the numbers from the first key, and dividing by the number of spaces. This is then repeated for the second key. The two resulting numbers are concatenated with each other, and with the last 8 bytes after the fields. The final result is an MD5 sum of the concatenated string (this is from the websocket wiki page).

Obviously, the server will need to do some work to provide a proper handshake to the browser. Specifically, it must interpret the header and indicate to the client it has done so by providing a return value derived from the two security keys in the header (ugh!).

Now, while the code in my last post indeed works with Safari, it does not work with Chrome (version 17.0.963.56). That's because Chrome uses the newer version of the websocket protocol; "draft-ietf-hybi-thewebsocketprotocol-06". This version was created because of man-in-the-middle attacks and other security vulnerabilities so now we have a completely different, non-backward compatible version of websockets floating around out there. In this second version of the specification, there is not two but one security key and its value is calculated differently. To make matters worse, there is a bit-oriented header and a 4 byte xor mask passed with each frame and the actual frame data is 'masked' (uber ugh!).

Of course, this is just the server side. My experience on the client side is that Safari passes websocket data back to the Javascript application as a string but Chrome passes it back as a blob. Being new to blobs (and HTML5 files) it took me a while to figure out how to deal with the blob data. The thing that concerns me is not the different ways the data is presented to the application (blob vs string) but that it is different at all. Don't these guys who write browsers communicate? This is why I think Javascript tool kits like Dojo and jQuery are safe at least for the near future.

Anyway, as you can see from the above descriptions of header parsing and handshaking, this is a lot more code than I feel like writing just to get a simple websocket demo working, so I started looking for websocket servers. Now, while I can code in Assembly, C, C++, Java, PERL, PHP, Python, Ruby and Javascript (as well as Pascal and Fortran but we won't go there) I would prefer the server to be written in Python since that is what I am most comfortable with lately and specifically, for most of my personal projects (because I always assume my project will become so successful it will need to scale to gazillions of users) I tend to use Tornado (my favorite Python webserver).

It turns out there is probably a websocket server written in any language you like (including erlang :-) and so I started reading up on them. From what I read, the Tornado websocket support is buggy (but I could be wrong) so I kept away from it; for now. I do not like big ass frameworks and many of the servers I looked at were add-ons to existing systems. I wanted a simple piece of code my simple mind could comprehend so heaven forbid, if it didn't work I could possibly debug it (this turned out to be prophetic to say the least) and as a result I settled on "websocket.py".

When I brought up "websocket.py" it did not work with my version of Safari or Chrome. Hell, I even got Safari working with a small piece of code 2 days ago, so I figured I would roll up my shirt sleeves and hack around "websocket.py" and make it work. I will go over the code mods and the new code in the next blog - "Websockets Revisited - Part 2". It concerns me though because I am sure whoever wrote "websocket.py" tested it on something and it worked so my mods have probably broken something that was working, but, I also know that what was not working with my two browsers is now working so I'll try to relay this information to the original author of "websocket.py".





No comments: