tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

patterns.rst (3670B)


      1 Patterns
      2 ========
      3 
      4 .. currentmodule:: websockets
      5 
      6 Here are typical patterns for processing messages in a WebSocket server or
      7 client. You will certainly implement some of them in your application.
      8 
      9 This page gives examples of connection handlers for a server. However, they're
     10 also applicable to a client, simply by assuming that ``websocket`` is a
     11 connection created with :func:`~client.connect`.
     12 
     13 WebSocket connections are long-lived. You will usually write a loop to process
     14 several messages during the lifetime of a connection.
     15 
     16 Consumer
     17 --------
     18 
     19 To receive messages from the WebSocket connection::
     20 
     21    async def consumer_handler(websocket):
     22        async for message in websocket:
     23            await consumer(message)
     24 
     25 In this example, ``consumer()`` is a coroutine implementing your business
     26 logic for processing a message received on the WebSocket connection. Each
     27 message may be :class:`str` or :class:`bytes`.
     28 
     29 Iteration terminates when the client disconnects.
     30 
     31 Producer
     32 --------
     33 
     34 To send messages to the WebSocket connection::
     35 
     36    async def producer_handler(websocket):
     37        while True:
     38            message = await producer()
     39            await websocket.send(message)
     40 
     41 In this example, ``producer()`` is a coroutine implementing your business
     42 logic for generating the next message to send on the WebSocket connection.
     43 Each message must be :class:`str` or :class:`bytes`.
     44 
     45 Iteration terminates when the client disconnects
     46 because :meth:`~server.WebSocketServerProtocol.send` raises a
     47 :exc:`~exceptions.ConnectionClosed` exception,
     48 which breaks out of the ``while  True`` loop.
     49 
     50 Consumer and producer
     51 ---------------------
     52 
     53 You can receive and send messages on the same WebSocket connection by
     54 combining the consumer and producer patterns. This requires running two tasks
     55 in parallel::
     56 
     57    async def handler(websocket):
     58        await asyncio.gather(
     59            consumer_handler(websocket),
     60            producer_handler(websocket),
     61        )
     62 
     63 If a task terminates, :func:`~asyncio.gather` doesn't cancel the other task.
     64 This can result in a situation where the producer keeps running after the
     65 consumer finished, which may leak resources.
     66 
     67 Here's a way to exit and close the WebSocket connection as soon as a task
     68 terminates, after canceling the other task::
     69 
     70    async def handler(websocket):
     71        consumer_task = asyncio.create_task(consumer_handler(websocket))
     72        producer_task = asyncio.create_task(producer_handler(websocket))
     73        done, pending = await asyncio.wait(
     74            [consumer_task, producer_task],
     75            return_when=asyncio.FIRST_COMPLETED,
     76        )
     77        for task in pending:
     78            task.cancel()
     79 
     80 Registration
     81 ------------
     82 
     83 To keep track of currently connected clients, you can register them when they
     84 connect and unregister them when they disconnect::
     85 
     86    connected = set()
     87 
     88    async def handler(websocket):
     89        # Register.
     90        connected.add(websocket)
     91        try:
     92            # Broadcast a message to all connected clients.
     93            websockets.broadcast(connected, "Hello!")
     94            await asyncio.sleep(10)
     95        finally:
     96            # Unregister.
     97            connected.remove(websocket)
     98 
     99 This example maintains the set of connected clients in memory. This works as
    100 long as you run a single process. It doesn't scale to multiple processes.
    101 
    102 Publish–subscribe
    103 -----------------
    104 
    105 If you plan to run multiple processes and you want to communicate updates
    106 between processes, then you must deploy a messaging system. You may find
    107 publish-subscribe functionality useful.
    108 
    109 A complete implementation of this idea with Redis is described in
    110 the :doc:`Django integration guide <../howto/django>`.