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 | import cherrypy
from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool
from ws4py.websocket import EchoWebSocket
cherrypy.config.update({'server.socket_port': 9000})
WebSocketPlugin(cherrypy.engine).subscribe()
cherrypy.tools.websocket = WebSocketTool()
class Root(object):
@cherrypy.expose
def index(self):
return 'some HTML with a websocket javascript connection'
@cherrypy.expose
def ws(self):
pass
cherrypy.quickstart(Root(), '/', config={'/ws': {'tools.websocket.on': True,
'tools.websocket.handler_cls': EchoWebSocket}})
|
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).
Bases: cherrypy._cptools.Tool
Runs at the end of the request processing by calling the opened method of the handler.
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.
Bases: cherrypy.process.plugins.SimplePlugin
Broadcasts a message to all connected clients known to the server.
Parameters: |
|
---|
Tracks the provided handler.
Parameters: |
|
---|
Bases: gevent.pywsgi.WSGIHandler
Upgradable version of gevent.pywsgi.WSGIHandler class
This is a drop-in replacement for gevent.pywsgi.WSGIHandler that supports protocol upgrades via WSGI environment. This means you can create upgraders as WSGI apps or WSGI middleware.
If an HTTP request comes in that includes the Upgrade header, it will add to the environment two items:
The upgrade must be signalled by starting a response using the 101 status code. This will inform the server to flush the headers and response status immediately, not to expect the normal WSGI app return value, and not to look for more HTTP requests on this connection.
To use this handler with gevent.pywsgi.WSGIServer, you can pass it to the constructor:
1 2 | server = WSGIServer(('127.0.0.1', 80), app,
handler_class=UpgradableWSGIHandler)
|
Alternatively, you can specify it as a class variable for a WSGIServer subclass:
1 2 | class UpgradableWSGIServer(gevent.pywsgi.WSGIServer):
handler_class = UpgradableWSGIHandler
|