Source code for pulsar.apps.ws
'''The :mod:`pulsar.apps.ws` contains WSGI_ middleware for
handling the WebSocket_ protocol.
Web sockets allow for bidirectional communication between the browser
and server. Pulsar implementation uses the WSGI middleware
:class:`.WebSocket` for the handshake_ and a class derived from
:class:`.WS` handler for the communication part.
This is a Web Socket handler which echoes all received messages
back to the client::
from pulsar.apps import wsgi, ws
class EchoWS(ws.WS):
def on_message(self, websocket, message):
websocket.write(message)
To create a valid :class:`.WebSocket` middleware initialise as follow::
wm = ws.WebSocket('/bla', EchoWS())
app = wsgi.WsgiHandler(middleware=(..., wm))
wsgi.WSGIServer(callable=app).start()
.. _WSGI: http://www.python.org/dev/peps/pep-3333/
.. _WebSocket: http://tools.ietf.org/html/rfc6455
.. _handshake: http://tools.ietf.org/html/rfc6455#section-1.3
API
==============
.. _websocket-middleware:
.. _websocket-handler:
WebSocket handler
~~~~~~~~~~~~~~~~~~~~
.. autoclass:: WS
:members:
:member-order: bysource
.. module:: pulsar.apps.ws.websocket
WebSocket
~~~~~~~~~~~~~~~~
.. autoclass:: WebSocket
:members:
:member-order: bysource
WebSocket protocol
~~~~~~~~~~~~~~~~~~~~
.. autoclass:: WebSocketProtocol
:members:
:member-order: bysource
'''
import logging
from pulsar.apps import data
from .websocket import WebSocket, WebSocketProtocol
__all__ = ['WebSocket', 'WebSocketProtocol', 'WS']
LOGGER = logging.getLogger('pulsar.ws')
[docs]class WS:
'''A web socket handler for both servers and clients.
It implements the asynchronous message passing for a
:class:`.WebSocketProtocol`.
On the server, the communication is started by the
:class:`.WebSocket` middleware after a successful handshake.
Override :meth:`on_message` to handle incoming string messages and
:meth:`on_bytes` to handle incoming ``bytes`` messages.
You can also override :meth:`on_open` and :meth:`on_close` to perform
specific tasks when the websocket is opened or closed.
These methods accept as first parameter the
:class:`.WebSocketProtocol` created during the handshake.
'''
frame_parser = None
[docs] def on_open(self, websocket):
'''Invoked when a new ``websocket`` is opened.
A web socket is opened straight after the upgrade headers are
sent (servers) or received (clients).
'''
pass
[docs] def on_message(self, websocket, message):
'''Handles incoming messages on the WebSocket.
This method should be overwritten
'''
pass
[docs] def on_bytes(self, websocket, body):
'''Handles incoming bytes.'''
pass
[docs] def on_ping(self, websocket, message):
'''Handle incoming ping ``message``.
By default it writes back the message as a ``pong`` frame.
'''
websocket.pong(message)
[docs] def on_pong(self, websocket, body):
'''Handle incoming pong ``message``.'''
pass
[docs] def on_close(self, websocket):
"""Invoked when the ``websocket`` is closed.
"""
pass
class PubSubClient(data.PubSubClient):
__slots__ = ('connection', 'channel')
def __init__(self, websocket, channel):
self.websocket = websocket
self.channel = channel
def __call__(self, channel, message):
handler = self.websocket.handler
handler.write(self.websocket, message)
class PubSubWS(WS):
'''A :class:`.WS` handler with a publish-subscribe handler
'''
client = PubSubClient
def __init__(self, pubsub, channel):
self.pubsub = pubsub
self.channel = channel
def on_open(self, websocket):
'''When a new websocket connection is established it creates a
new :class:`ChatClient` and adds it to the set of clients of the
:attr:`pubsub` handler.'''
LOGGER.info('New websocket opened. Add client to %s on "%s" channel',
self.pubsub, self.channel)
self.pubsub.add_client(self.client(websocket, self.channel))
def write(self, websocket, message):
websocket.write(message)