diff --git a/ws4py/server/geventserver.py b/ws4py/server/geventserver.py index 04fcf46..be31d5d 100644 --- a/ws4py/server/geventserver.py +++ b/ws4py/server/geventserver.py @@ -18,11 +18,11 @@ class UpgradableWSGIHandler(gevent.pywsgi.WSGIHandler): If an HTTP request comes in that includes the Upgrade header, it will add to the environment two items: - `upgrade.protocol` + ``upgrade.protocol` ` The protocol to upgrade to. Checking for this lets you know the request wants to be upgraded and the WSGI server supports this interface. - `upgrade.socket` + ``upgrade.socket`` The raw Python socket object for the connection. From this you can do any upgrade negotiation and hand it off to the proper protocol handler. @@ -34,14 +34,20 @@ class UpgradableWSGIHandler(gevent.pywsgi.WSGIHandler): To use this handler with gevent.pywsgi.WSGIServer, you can pass it to the constructor: - server = WSGIServer(('127.0.0.1', 80), app, - handler_class=UpgradableWSGIHandler) + .. code-block:: python + :linenos: + + server = WSGIServer(('127.0.0.1', 80), app, + handler_class=UpgradableWSGIHandler) Alternatively, you can specify it as a class variable for a WSGIServer subclass: - class UpgradableWSGIServer(gevent.pywsgi.WSGIServer): - handler_class = UpgradableWSGIHandler + .. code-block:: python + :linenos: + + class UpgradableWSGIServer(gevent.pywsgi.WSGIServer): + handler_class = UpgradableWSGIHandler """ def run_application(self): diff --git a/ws4py/server/wsgi/middleware.py b/ws4py/server/wsgi/middleware.py index b92c652..0130bd4 100644 --- a/ws4py/server/wsgi/middleware.py +++ b/ws4py/server/wsgi/middleware.py @@ -11,10 +11,37 @@ from ws4py.websocket import WebSocket class WebSocketUpgradeMiddleware(object): - """WSGI middleware for handling WebSocket upgrades""" - def __init__(self, app, fallback_app=None, protocols=None, extensions=None, websocket_class=WebSocket): + """ + WSGI middleware that performs the WebSocket upgrade handshake. + + .. code-block:: python + :linenos: + + def ws_handler(websocket): + ... + + app = WebSocketUpgradeMiddleware(ws_handler) + + + If the handshake succeeds, it calls ``app`` with an instance of + ``websocket_class`` with a copy of the environ dictionary. + + If an error occurs and ``fallback_app`` is provided, it must be a + WSGI application which will be called. Otherwise it returns a + simple error through the inner ``start_response``. + + One interesting aspect is that wsgiref fails with this middleware + due to the ``Upgrade`` hop-by-hop header which is not allowed. + + Make sure that your server does not close the underlying socket for you + since it would close the whole WebSocket connection as well. + + You may provide your own representation of the socket by setting + the environ key: ``'upgrade.socket'``. Otherwise, ``'wsgi.input'._sock`` + will be used. + """ self.app = app self.fallback_app = fallback_app self.protocols = protocols @@ -24,7 +51,7 @@ def __init__(self, app, fallback_app=None, protocols=None, extensions=None, def __call__(self, environ, start_response): # Initial handshake validation try: - if 'websocket' not in environ.get('upgrade.protocol', '').lower(): + if 'websocket' not in environ.get('upgrade.protocol', environ.get('HTTP_UPGRADE', '')).lower(): raise HandshakeError("Upgrade protocol is not websocket") if environ.get('REQUEST_METHOD') != 'GET': @@ -86,7 +113,8 @@ def __call__(self, environ, start_response): start_response("101 Web Socket Hybi Handshake", headers) - return self.app(self.websocket_class(environ.get('upgrade.socket'), + return self.app(self.websocket_class(environ.get('upgrade.socket', + environ.get('wsgi.input')._sock), ws_protocols, ws_extensions, environ.copy()))