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
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.
WSGI entities to support WebSocket from within gevent.
Its usage is rather simple:
Bases: gevent.pywsgi.WSGIHandler
A WSGI handler that will perform the RFC 6455 upgrade and handshake before calling the WSGI application.
If the incoming request doesn’t have a ‘Upgrade’ header, the handler will simply fallback to the gevent builtin’s handler and process it as per usual.
Bases: gevent.pool.Pool
Simple pool of bound websockets. Internally it uses a gevent group to track the websockets. The server should call the clear method to initiate the closing handshake when the server is shutdown.
Bases: gevent.pywsgi.WSGIServer
WSGI server that simply tracks websockets and send them a proper closing handshake when the server terminates.
Other than that, the server is the same as its gevent.pywsgi.WSGIServer base.
alias of WebSocketWSGIHandler
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()
Note
For some reason this server may fail against autobahntestsuite.
Bases: wsgiref.handlers.SimpleHandler
Bases: wsgiref.simple_server.WSGIRequestHandler
Bases: wsgiref.simple_server.WSGIServer
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.
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:
Warning
The WSGI application sets the ‘Upgrade’ header response as specified by RFC 6455. This is not tolerated by PEP 333 since it’s a hop-by-hop header. We expect most servers won’t mind.
Bases: object
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.