WebSocket within CherryPy is a tricky bit since CherryPy is a threaded server which would choke quickly if each thread of the server were kept attached to a long living connection that WebSocket expects.
In order to work around this constraint, we take some advantage of some internals of CherryPy as well as the introspection Python provides.
Basically, when the WebSocket handshake is complete, we take over the socket and let CherryPy take back the thread that was associated with the upgrade request.
These operations require a bit of work at various levels of the CherryPy framework but this module takes care of them and from your application’s perspective, this is abstracted.
Here are the various utilities provided by this module:
- WebSocketTool: The tool is in charge to perform the
HTTP upgrade and detach the socket from CherryPy. It runs at various hook points of the request’s processing. Enable that tool at any path you wish to handle as a WebSocket handler.
- WebSocketPlugin: The plugin tracks the instanciated web socket handlers.
It also cleans out websocket handler which connection have been closed down. The websocket connection then runs in its own thread that this plugin manages.
Simple usage example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Note that you can set the handler class on per-path basis, meaning you could also dynamically change the class based on other envrionmental settings (is the user authenticated for ex).
upgrade(protocols=None, extensions=None, version=(8, 13), handler_cls=<class 'ws4py.websocket.WebSocket'>, heartbeat_freq=None)¶
Performs the upgrade of the connection to the WebSocket protocol.
The provided protocols may be a list of WebSocket protocols supported by the instance of the tool.
When no list is provided and no protocol is either during the upgrade, then the protocol parameter is not taken into account. On the other hand, if the protocol from the handshake isn’t part of the provided list, the upgrade fails immediatly.
Sets some internal flags of CherryPy so that it doesn’t close the socket down.
Some clients aren’t that smart when it comes to headers lookup.
Runs at the end of the request processing by calling the opened method of the handler.
Tracks the provided handler.
- ws_handler – websocket handler instance
- peer_addr – remote peer address for tracing purpose
Terminate all connections and clear the pool. Executed when the engine stops.
Broadcasts a message to all connected clients known to the server.
- message – a message suitable to pass to the send() method of the connected handler.
- binary – whether or not the message is a binary one
Add WebSocket support to the built-in WSGI server
provided by the
wsgiref. This is clearly not
meant to be a production server so please consider this
only for testing purpose.
Mostly, this module overrides bits and pieces of the built-in classes so that it supports the WebSocket workflow.
from wsgiref.simple_server import make_server from ws4py.websocket import EchoWebSocket from ws4py.server.wsgirefserver import WSGIServer, WebSocketWSGIRequestHandler from ws4py.server.wsgiutils import WebSocketWSGIApplication server = make_server('', 9000, server_class=WSGIServer, handler_class=WebSocketWSGIRequestHandler, app=WebSocketWSGIApplication(handler_cls=EchoWebSocket)) server.initialize_websockets_manager() server.serve_forever()
For some reason this server may fail against autobahntestsuite.
WebSocketWSGIHandler(stdin, stdout, stderr, environ, multithread=True, multiprocess=False)¶
Setup the environ dictionary and add the ‘ws4py.socket’ key. Its associated value is the real socket underlying socket.
Completes the response and performs the following tasks:
- Remove the ‘ws4py.socket’ and ‘ws4py.websocket’ environ keys.
- Attach the returned websocket, if any, to the WSGI server
WebSocketWSGIRequestHandler(request, client_address, server)¶
Unfortunately the base class forces us to override the whole method to actually provide our wsgi handler.
WSGIServer(server_address, RequestHandlerClass, bind_and_activate=True)¶
Constructor. May be extended, do not override.
Call thos to start the underlying websockets manager. Make sure to call it once your server is created.
The base class would close our socket if we didn’t override it.
Call this from your WSGI handler when a websocket has been created.
Properly initiate closing handshakes on all websockets when the WSGI server terminates.
This module provides a WSGI application suitable for a WSGI server such as gevent or wsgiref for instance.
PEP 333 couldn’t foresee a protocol such as WebSockets but luckily the way the initial protocol upgrade was designed means that we can fit the handshake in a WSGI flow.
The handshake validates the request against some internal or user-provided values and fails the request if the validation doesn’t complete.
On success, the provided WebSocket subclass is instanciated and stored into the ‘ws4py.websocket’ environ key so that the WSGI server can handle it.
The WSGI application returns an empty iterable since there is little value to return some content within the response to the handshake.
A server wishing to support WebSocket via ws4py should:
- Provide the real socket object to ws4py through the ‘ws4py.socket’ environ key. We can’t use ‘wsgi.input’ as it may be wrapper to the socket we wouldn’t know how to extract the socket from.
- Look for the ‘ws4py.websocket’ key in the environ
when the application has returned and probably attach
it to a
ws4py.manager.WebSocketManagerinstance so that the websocket runs its life.
- Remove the ‘ws4py.websocket’ and ‘ws4py.socket’ environ keys once the application has returned. No need for these keys to persist.
- Not close the underlying socket otherwise, well, your websocket will also shutdown.
WebSocketWSGIApplication(protocols=None, extensions=None, handler_cls=<class 'ws4py.websocket.WebSocket'>)¶
WSGI application usable to complete the upgrade handshake by validating the requested protocols and extensions as well as the websocket version.
If the upgrade validates, the handler_cls class is instanciated and stored inside the WSGI environ under the ‘ws4py.websocket’ key to make it available to the WSGI handler.
make_websocket(sock, protocols, extensions, environ)¶
Initialize the handler_cls instance with the given negociated sets of protocols and extensions as well as the environ and sock.
Stores then the instance in the environ dict under the ‘ws4py.websocket’ key.