server Package

cherrypyserver Module

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).

class ws4py.server.cherrypyserver.WebSocketTool[source]

Bases: cherrypy._cptools.Tool

upgrade(protocols=None, extensions=None, version=(8, 13), handler_cls=<class 'ws4py.websocket.WebSocket'>, heartbeat_freq=None)[source]

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.

complete()[source]

Sets some internal flags of CherryPy so that it doesn’t close the socket down.

cleanup_headers()[source]

Some clients aren’t that smart when it comes to headers lookup.

start_handler()[source]

Runs at the end of the request processing by calling the opened method of the handler.

class ws4py.server.cherrypyserver.WebSocketPlugin(bus)[source]

Bases: cherrypy.process.plugins.SimplePlugin

start()[source]
stop()[source]
handle(ws_handler, peer_addr)[source]

Tracks the provided handler.

Parameters:
  • ws_handler – websocket handler instance
  • peer_addr – remote peer address for tracing purpose
cleanup()[source]

Terminate all connections and clear the pool. Executed when the engine stops.

broadcast(message, binary=False)[source]

Broadcasts a message to all connected clients known to the server.

Parameters:
  • message – a message suitable to pass to the send() method of the connected handler.
  • binary – whether or not the message is a binary one

geventserver Module

wsgirefserver Module

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.

class ws4py.server.wsgirefserver.WebSocketWSGIHandler(stdin, stdout, stderr, environ, multithread=True, multiprocess=False)[source]

Bases: wsgiref.handlers.SimpleHandler

setup_environ()[source]

Setup the environ dictionary and add the ‘ws4py.socket’ key. Its associated value is the real socket underlying socket.

finish_response()[source]

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 using its link_websocket_to_server method.
class ws4py.server.wsgirefserver.WebSocketWSGIRequestHandler(request, client_address, server)[source]

Bases: wsgiref.simple_server.WSGIRequestHandler

class WebSocketWSGIHandler(stdin, stdout, stderr, environ, multithread=True, multiprocess=False)

Bases: wsgiref.handlers.SimpleHandler

finish_response()

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 using its link_websocket_to_server method.
setup_environ()

Setup the environ dictionary and add the ‘ws4py.socket’ key. Its associated value is the real socket underlying socket.

handle()[source]

Unfortunately the base class forces us to override the whole method to actually provide our wsgi handler.

class ws4py.server.wsgirefserver.WSGIServer(server_address, RequestHandlerClass, bind_and_activate=True)[source]

Bases: wsgiref.simple_server.WSGIServer

Constructor. May be extended, do not override.

initialize_websockets_manager()[source]

Call thos to start the underlying websockets manager. Make sure to call it once your server is created.

shutdown_request(request)[source]

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.

server_close()[source]

Properly initiate closing handshakes on all websockets when the WSGI server terminates.

wsgitutils Module

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.WebSocketManager instance 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.

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.

class ws4py.server.wsgiutils.WebSocketWSGIApplication(protocols=None, extensions=None, handler_cls=<class 'ws4py.websocket.WebSocket'>)[source]

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.

make_websocket(sock, protocols, extensions, environ)[source]

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.