In my earlier post, I referred to some minor changes that I had to make to the wsgi server to seamlessly support a websocket client. I think these changes are generic and simple enough that they, or something similar in spirit, should make it into the WSGI standard someday. Being able to support websockets from within WSGI applications is just too powerful of a use case to ignore.
Get the Socket Out of the Environment
The first change I made was to add a get_socket() method to the file-like object that appears in 'wsgi.input'. This method returns the underlying socket object directly, which is then used in the WebSocket object to handle message transfer.
One reason that this might not be the most ideal of implementations is buffering. It happens that eventlet.wsgi is implemented such that when calling the wsgi application, the very next byte to be read off the socket is the first byte of the first websocket messsage. It seems plausible that other WSGI servers might not have this property.
Cancel Post-Application Processing
The second change is that when eventlet.wsgi sees that the application returned a special flag value ALREADY_HANDLED, it aborts all post-application processing and skips straight to closing the socket. Normally it would write the headers and return value to the socket, but since the application has already handled that stuff, we don’t want the WSGI server throwing junk on the line (or complaining that the app didn’t call start_response).
The downside of this approach is that most middleware would have to be changed (slightly) to support ALREADY_HANDLED. Raising a special exception seems to have the same problem. But then again, changing the behavior of middleware is kinda the point.
So, those are my two changes. I’m certain that there is a way to do this that is even more seamless with the intent of the WSGI standard, but these changes required three lines of code to implement, and work today. :)

Posted by Ben on April 8, 2010 at 4:34 am
Hi,
I’ve been busy adding websocket support to a repoze.bfg app over the last few days and have hit a little wrinkle:
When you return ALREADY_HANDLED parts of your WSGI pipeline are expecting an iterable. For example if you want to use the excellent Werkzeug debuggung middleware, there’s this piece of code in your upstream pipeline:
try:
app_iter = self.app(environ, start_response)
for item in app_iter:
yield item
if hasattr(app_iter, ‘close’):
app_iter.close()
…
So here when the websocket is closed on the client side you’ll get an exception on the server side. There are a couple of other WSGI libraries that do this and it is a part of the WSGI api that you have a callable that returns an iterable.
I’ve got a fork of eventlet on bitbucket would you like me to send a patch?
Thanks for the great work,
Ben
Posted by Ryan Williams on April 8, 2010 at 7:56 pm
Yes, I’d love a patch! Thanks for thinking of this, fixing this will definitely make the websocket implementation even more wsgi-compatible!
Posted by Ben on April 9, 2010 at 6:25 am
I’ve made some initial changes and submitted a pull request on bitbucket
Ben
Posted by Ryan Williams on April 11, 2010 at 11:12 pm
For whatever reason I didn’t get the pull request; however I saw the changes in your repo and pulled them. Let me know when you’ve got more tests!
Posted by Ben on May 6, 2010 at 4:26 am
Hi Ryan,
I’ve made some more changes:
Added eventlet.websocket which is the WebSocket object in examples/websocket with some of my changes.
Added my modified tests from the repoze project I was working on for 100% test coverage for the websocket stuff.
I think my last set of changes re ALREADY_HANDLED may have been incomplete, so added a little diff that I missed.
I have a couple of failing tests under OSX:
======================================================================
FAIL: test_cancel_accumulated (tests.hub_test.TestTimerCleanup)
———————————————————————-
Traceback (most recent call last):
File “/Users/benford/pycharm/eventlet/tests/hub_test.py”, line 19, in test_cancel_accumulated
self.assert_(hub.timers_cancelled < len(hub.timers))
AssertionError
======================================================================
FAIL: test_cancel_proportion (tests.hub_test.TestTimerCleanup)
———————————————————————-
Traceback (most recent call last):
File "/Users/benford/pycharm/eventlet/tests/hub_test.py", line 39, in test_cancel_proportion
self.assert_(hub.timers_cancelled < len(hub.timers))
AssertionError
(these were failing before my changes) have you seen these before?
Cheers,
Ben
P.S. I think I sent you a real pull request this time :-)
Posted by Ryan Williams on May 7, 2010 at 10:34 pm
Yeah, those were failing in the continuous build already. Check that link out if you haven’t seen it already. I fixed those failures the other day, so you should be good to go.
Thanks for the changes, glad to have a serious complement of tests!