tor-browser

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

design.rst (25372B)


      1 Design
      2 ======
      3 
      4 .. currentmodule:: websockets
      5 
      6 This document describes the design of websockets. It assumes familiarity with
      7 the specification of the WebSocket protocol in :rfc:`6455`.
      8 
      9 It's primarily intended at maintainers. It may also be useful for users who
     10 wish to understand what happens under the hood.
     11 
     12 .. warning::
     13 
     14    Internals described in this document may change at any time.
     15 
     16    Backwards compatibility is only guaranteed for :doc:`public APIs
     17    <../reference/index>`.
     18 
     19 Lifecycle
     20 ---------
     21 
     22 State
     23 .....
     24 
     25 WebSocket connections go through a trivial state machine:
     26 
     27 - ``CONNECTING``: initial state,
     28 - ``OPEN``: when the opening handshake is complete,
     29 - ``CLOSING``: when the closing handshake is started,
     30 - ``CLOSED``: when the TCP connection is closed.
     31 
     32 Transitions happen in the following places:
     33 
     34 - ``CONNECTING -> OPEN``: in
     35  :meth:`~legacy.protocol.WebSocketCommonProtocol.connection_open` which runs when
     36  the :ref:`opening handshake <opening-handshake>` completes and the WebSocket
     37  connection is established — not to be confused with
     38  :meth:`~asyncio.BaseProtocol.connection_made` which runs when the TCP connection
     39  is established;
     40 - ``OPEN -> CLOSING``: in
     41  :meth:`~legacy.protocol.WebSocketCommonProtocol.write_frame` immediately before
     42  sending a close frame; since receiving a close frame triggers sending a
     43  close frame, this does the right thing regardless of which side started the
     44  :ref:`closing handshake <closing-handshake>`; also in
     45  :meth:`~legacy.protocol.WebSocketCommonProtocol.fail_connection` which duplicates
     46  a few lines of code from ``write_close_frame()`` and ``write_frame()``;
     47 - ``* -> CLOSED``: in
     48  :meth:`~legacy.protocol.WebSocketCommonProtocol.connection_lost` which is always
     49  called exactly once when the TCP connection is closed.
     50 
     51 Coroutines
     52 ..........
     53 
     54 The following diagram shows which coroutines are running at each stage of the
     55 connection lifecycle on the client side.
     56 
     57 .. image:: lifecycle.svg
     58   :target: _images/lifecycle.svg
     59 
     60 The lifecycle is identical on the server side, except inversion of control
     61 makes the equivalent of :meth:`~client.connect` implicit.
     62 
     63 Coroutines shown in green are called by the application. Multiple coroutines
     64 may interact with the WebSocket connection concurrently.
     65 
     66 Coroutines shown in gray manage the connection. When the opening handshake
     67 succeeds, :meth:`~legacy.protocol.WebSocketCommonProtocol.connection_open` starts
     68 two tasks:
     69 
     70 - :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` runs
     71  :meth:`~legacy.protocol.WebSocketCommonProtocol.transfer_data` which handles
     72  incoming data and lets :meth:`~legacy.protocol.WebSocketCommonProtocol.recv`
     73  consume it. It may be canceled to terminate the connection. It never exits
     74  with an exception other than :exc:`~asyncio.CancelledError`. See :ref:`data
     75  transfer <data-transfer>` below.
     76 
     77 - :attr:`~legacy.protocol.WebSocketCommonProtocol.keepalive_ping_task` runs
     78  :meth:`~legacy.protocol.WebSocketCommonProtocol.keepalive_ping` which sends Ping
     79  frames at regular intervals and ensures that corresponding Pong frames are
     80  received. It is canceled when the connection terminates. It never exits
     81  with an exception other than :exc:`~asyncio.CancelledError`.
     82 
     83 - :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task` runs
     84  :meth:`~legacy.protocol.WebSocketCommonProtocol.close_connection` which waits for
     85  the data transfer to terminate, then takes care of closing the TCP
     86  connection. It must not be canceled. It never exits with an exception. See
     87  :ref:`connection termination <connection-termination>` below.
     88 
     89 Besides, :meth:`~legacy.protocol.WebSocketCommonProtocol.fail_connection` starts
     90 the same :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task` when
     91 the opening handshake fails, in order to close the TCP connection.
     92 
     93 Splitting the responsibilities between two tasks makes it easier to guarantee
     94 that websockets can terminate connections:
     95 
     96 - within a fixed timeout,
     97 - without leaking pending tasks,
     98 - without leaking open TCP connections,
     99 
    100 regardless of whether the connection terminates normally or abnormally.
    101 
    102 :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` completes when no
    103 more data will be received on the connection. Under normal circumstances, it
    104 exits after exchanging close frames.
    105 
    106 :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task` completes when
    107 the TCP connection is closed.
    108 
    109 
    110 .. _opening-handshake:
    111 
    112 Opening handshake
    113 -----------------
    114 
    115 websockets performs the opening handshake when establishing a WebSocket
    116 connection. On the client side, :meth:`~client.connect` executes it
    117 before returning the protocol to the caller. On the server side, it's executed
    118 before passing the protocol to the ``ws_handler`` coroutine handling the
    119 connection.
    120 
    121 While the opening handshake is asymmetrical — the client sends an HTTP Upgrade
    122 request and the server replies with an HTTP Switching Protocols response —
    123 websockets aims at keeping the implementation of both sides consistent with
    124 one another.
    125 
    126 On the client side, :meth:`~client.WebSocketClientProtocol.handshake`:
    127 
    128 - builds an HTTP request based on the ``uri`` and parameters passed to
    129  :meth:`~client.connect`;
    130 - writes the HTTP request to the network;
    131 - reads an HTTP response from the network;
    132 - checks the HTTP response, validates ``extensions`` and ``subprotocol``, and
    133  configures the protocol accordingly;
    134 - moves to the ``OPEN`` state.
    135 
    136 On the server side, :meth:`~server.WebSocketServerProtocol.handshake`:
    137 
    138 - reads an HTTP request from the network;
    139 - calls :meth:`~server.WebSocketServerProtocol.process_request` which may
    140  abort the WebSocket handshake and return an HTTP response instead; this
    141  hook only makes sense on the server side;
    142 - checks the HTTP request, negotiates ``extensions`` and ``subprotocol``, and
    143  configures the protocol accordingly;
    144 - builds an HTTP response based on the above and parameters passed to
    145  :meth:`~server.serve`;
    146 - writes the HTTP response to the network;
    147 - moves to the ``OPEN`` state;
    148 - returns the ``path`` part of the ``uri``.
    149 
    150 The most significant asymmetry between the two sides of the opening handshake
    151 lies in the negotiation of extensions and, to a lesser extent, of the
    152 subprotocol. The server knows everything about both sides and decides what the
    153 parameters should be for the connection. The client merely applies them.
    154 
    155 If anything goes wrong during the opening handshake, websockets :ref:`fails
    156 the connection <connection-failure>`.
    157 
    158 
    159 .. _data-transfer:
    160 
    161 Data transfer
    162 -------------
    163 
    164 Symmetry
    165 ........
    166 
    167 Once the opening handshake has completed, the WebSocket protocol enters the
    168 data transfer phase. This part is almost symmetrical. There are only two
    169 differences between a server and a client:
    170 
    171 - `client-to-server masking`_: the client masks outgoing frames; the server
    172  unmasks incoming frames;
    173 - `closing the TCP connection`_: the server closes the connection immediately;
    174  the client waits for the server to do it.
    175 
    176 .. _client-to-server masking: https://www.rfc-editor.org/rfc/rfc6455.html#section-5.3
    177 .. _closing the TCP connection: https://www.rfc-editor.org/rfc/rfc6455.html#section-5.5.1
    178 
    179 These differences are so minor that all the logic for `data framing`_, for
    180 `sending and receiving data`_ and for `closing the connection`_ is implemented
    181 in the same class, :class:`~legacy.protocol.WebSocketCommonProtocol`.
    182 
    183 .. _data framing: https://www.rfc-editor.org/rfc/rfc6455.html#section-5
    184 .. _sending and receiving data: https://www.rfc-editor.org/rfc/rfc6455.html#section-6
    185 .. _closing the connection: https://www.rfc-editor.org/rfc/rfc6455.html#section-7
    186 
    187 The :attr:`~legacy.protocol.WebSocketCommonProtocol.is_client` attribute tells which
    188 side a protocol instance is managing. This attribute is defined on the
    189 :attr:`~server.WebSocketServerProtocol` and
    190 :attr:`~client.WebSocketClientProtocol` classes.
    191 
    192 Data flow
    193 .........
    194 
    195 The following diagram shows how data flows between an application built on top
    196 of websockets and a remote endpoint. It applies regardless of which side is
    197 the server or the client.
    198 
    199 .. image:: protocol.svg
    200   :target: _images/protocol.svg
    201 
    202 Public methods are shown in green, private methods in yellow, and buffers in
    203 orange. Methods related to connection termination are omitted; connection
    204 termination is discussed in another section below.
    205 
    206 Receiving data
    207 ..............
    208 
    209 The left side of the diagram shows how websockets receives data.
    210 
    211 Incoming data is written to a :class:`~asyncio.StreamReader` in order to
    212 implement flow control and provide backpressure on the TCP connection.
    213 
    214 :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task`, which is started
    215 when the WebSocket connection is established, processes this data.
    216 
    217 When it receives data frames, it reassembles fragments and puts the resulting
    218 messages in the :attr:`~legacy.protocol.WebSocketCommonProtocol.messages` queue.
    219 
    220 When it encounters a control frame:
    221 
    222 - if it's a close frame, it starts the closing handshake;
    223 - if it's a ping frame, it answers with a pong frame;
    224 - if it's a pong frame, it acknowledges the corresponding ping (unless it's an
    225  unsolicited pong).
    226 
    227 Running this process in a task guarantees that control frames are processed
    228 promptly. Without such a task, websockets would depend on the application to
    229 drive the connection by having exactly one coroutine awaiting
    230 :meth:`~legacy.protocol.WebSocketCommonProtocol.recv` at any time. While this
    231 happens naturally in many use cases, it cannot be relied upon.
    232 
    233 Then :meth:`~legacy.protocol.WebSocketCommonProtocol.recv` fetches the next message
    234 from the :attr:`~legacy.protocol.WebSocketCommonProtocol.messages` queue, with some
    235 complexity added for handling backpressure and termination correctly.
    236 
    237 Sending data
    238 ............
    239 
    240 The right side of the diagram shows how websockets sends data.
    241 
    242 :meth:`~legacy.protocol.WebSocketCommonProtocol.send` writes one or several data
    243 frames containing the message. While sending a fragmented message, concurrent
    244 calls to :meth:`~legacy.protocol.WebSocketCommonProtocol.send` are put on hold until
    245 all fragments are sent. This makes concurrent calls safe.
    246 
    247 :meth:`~legacy.protocol.WebSocketCommonProtocol.ping` writes a ping frame and
    248 yields a :class:`~asyncio.Future` which will be completed when a matching pong
    249 frame is received.
    250 
    251 :meth:`~legacy.protocol.WebSocketCommonProtocol.pong` writes a pong frame.
    252 
    253 :meth:`~legacy.protocol.WebSocketCommonProtocol.close` writes a close frame and
    254 waits for the TCP connection to terminate.
    255 
    256 Outgoing data is written to a :class:`~asyncio.StreamWriter` in order to
    257 implement flow control and provide backpressure from the TCP connection.
    258 
    259 .. _closing-handshake:
    260 
    261 Closing handshake
    262 .................
    263 
    264 When the other side of the connection initiates the closing handshake,
    265 :meth:`~legacy.protocol.WebSocketCommonProtocol.read_message` receives a close
    266 frame while in the ``OPEN`` state. It moves to the ``CLOSING`` state, sends a
    267 close frame, and returns :obj:`None`, causing
    268 :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` to terminate.
    269 
    270 When this side of the connection initiates the closing handshake with
    271 :meth:`~legacy.protocol.WebSocketCommonProtocol.close`, it moves to the ``CLOSING``
    272 state and sends a close frame. When the other side sends a close frame,
    273 :meth:`~legacy.protocol.WebSocketCommonProtocol.read_message` receives it in the
    274 ``CLOSING`` state and returns :obj:`None`, also causing
    275 :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` to terminate.
    276 
    277 If the other side doesn't send a close frame within the connection's close
    278 timeout, websockets :ref:`fails the connection <connection-failure>`.
    279 
    280 The closing handshake can take up to ``2 * close_timeout``: one
    281 ``close_timeout`` to write a close frame and one ``close_timeout`` to receive
    282 a close frame.
    283 
    284 Then websockets terminates the TCP connection.
    285 
    286 
    287 .. _connection-termination:
    288 
    289 Connection termination
    290 ----------------------
    291 
    292 :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task`, which is
    293 started when the WebSocket connection is established, is responsible for
    294 eventually closing the TCP connection.
    295 
    296 First :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task` waits
    297 for :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` to terminate,
    298 which may happen as a result of:
    299 
    300 - a successful closing handshake: as explained above, this exits the infinite
    301  loop in :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task`;
    302 - a timeout while waiting for the closing handshake to complete: this cancels
    303  :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task`;
    304 - a protocol error, including connection errors: depending on the exception,
    305  :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` :ref:`fails the
    306  connection <connection-failure>` with a suitable code and exits.
    307 
    308 :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task` is separate
    309 from :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` to make it
    310 easier to implement the timeout on the closing handshake. Canceling
    311 :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` creates no risk
    312 of canceling :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task`
    313 and failing to close the TCP connection, thus leaking resources.
    314 
    315 Then :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task` cancels
    316 :meth:`~legacy.protocol.WebSocketCommonProtocol.keepalive_ping`. This task has no
    317 protocol compliance responsibilities. Terminating it to avoid leaking it is
    318 the only concern.
    319 
    320 Terminating the TCP connection can take up to ``2 * close_timeout`` on the
    321 server side and ``3 * close_timeout`` on the client side. Clients start by
    322 waiting for the server to close the connection, hence the extra
    323 ``close_timeout``. Then both sides go through the following steps until the
    324 TCP connection is lost: half-closing the connection (only for non-TLS
    325 connections), closing the connection, aborting the connection. At this point
    326 the connection drops regardless of what happens on the network.
    327 
    328 
    329 .. _connection-failure:
    330 
    331 Connection failure
    332 ------------------
    333 
    334 If the opening handshake doesn't complete successfully, websockets fails the
    335 connection by closing the TCP connection.
    336 
    337 Once the opening handshake has completed, websockets fails the connection by
    338 canceling :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task`
    339 and sending a close frame if appropriate.
    340 
    341 :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` exits, unblocking
    342 :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task`, which closes
    343 the TCP connection.
    344 
    345 
    346 .. _server-shutdown:
    347 
    348 Server shutdown
    349 ---------------
    350 
    351 :class:`~websockets.server.WebSocketServer` closes asynchronously like
    352 :class:`asyncio.Server`. The shutdown happen in two steps:
    353 
    354 1. Stop listening and accepting new connections;
    355 2. Close established connections with close code 1001 (going away) or, if
    356   the opening handshake is still in progress, with HTTP status code 503
    357   (Service Unavailable).
    358 
    359 The first call to :class:`~websockets.server.WebSocketServer.close` starts a
    360 task that performs this sequence. Further calls are ignored. This is the
    361 easiest way to make :class:`~websockets.server.WebSocketServer.close` and
    362 :class:`~websockets.server.WebSocketServer.wait_closed` idempotent.
    363 
    364 
    365 .. _cancellation:
    366 
    367 Cancellation
    368 ------------
    369 
    370 User code
    371 .........
    372 
    373 websockets provides a WebSocket application server. It manages connections and
    374 passes them to user-provided connection handlers. This is an *inversion of
    375 control* scenario: library code calls user code.
    376 
    377 If a connection drops, the corresponding handler should terminate. If the
    378 server shuts down, all connection handlers must terminate. Canceling
    379 connection handlers would terminate them.
    380 
    381 However, using cancellation for this purpose would require all connection
    382 handlers to handle it properly. For example, if a connection handler starts
    383 some tasks, it should catch :exc:`~asyncio.CancelledError`, terminate or
    384 cancel these tasks, and then re-raise the exception.
    385 
    386 Cancellation is tricky in :mod:`asyncio` applications, especially when it
    387 interacts with finalization logic. In the example above, what if a handler
    388 gets interrupted with :exc:`~asyncio.CancelledError` while it's finalizing
    389 the tasks it started, after detecting that the connection dropped?
    390 
    391 websockets considers that cancellation may only be triggered by the caller of
    392 a coroutine when it doesn't care about the results of that coroutine anymore.
    393 (Source: `Guido van Rossum <https://groups.google.com/forum/#!msg
    394 /python-tulip/LZQe38CR3bg/7qZ1p_q5yycJ>`_). Since connection handlers run
    395 arbitrary user code, websockets has no way of deciding whether that code is
    396 still doing something worth caring about.
    397 
    398 For these reasons, websockets never cancels connection handlers. Instead it
    399 expects them to detect when the connection is closed, execute finalization
    400 logic if needed, and exit.
    401 
    402 Conversely, cancellation isn't a concern for WebSocket clients because they
    403 don't involve inversion of control.
    404 
    405 Library
    406 .......
    407 
    408 Most :doc:`public APIs <../reference/index>` of websockets are coroutines.
    409 They may be canceled, for example if the user starts a task that calls these
    410 coroutines and cancels the task later. websockets must handle this situation.
    411 
    412 Cancellation during the opening handshake is handled like any other exception:
    413 the TCP connection is closed and the exception is re-raised. This can only
    414 happen on the client side. On the server side, the opening handshake is
    415 managed by websockets and nothing results in a cancellation.
    416 
    417 Once the WebSocket connection is established, internal tasks
    418 :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` and
    419 :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task` mustn't get
    420 accidentally canceled if a coroutine that awaits them is canceled. In other
    421 words, they must be shielded from cancellation.
    422 
    423 :meth:`~legacy.protocol.WebSocketCommonProtocol.recv` waits for the next message in
    424 the queue or for :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task`
    425 to terminate, whichever comes first. It relies on :func:`~asyncio.wait` for
    426 waiting on two futures in parallel. As a consequence, even though it's waiting
    427 on a :class:`~asyncio.Future` signaling the next message and on
    428 :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task`, it doesn't
    429 propagate cancellation to them.
    430 
    431 :meth:`~legacy.protocol.WebSocketCommonProtocol.ensure_open` is called by
    432 :meth:`~legacy.protocol.WebSocketCommonProtocol.send`,
    433 :meth:`~legacy.protocol.WebSocketCommonProtocol.ping`, and
    434 :meth:`~legacy.protocol.WebSocketCommonProtocol.pong`. When the connection state is
    435 ``CLOSING``, it waits for
    436 :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` but shields it to
    437 prevent cancellation.
    438 
    439 :meth:`~legacy.protocol.WebSocketCommonProtocol.close` waits for the data transfer
    440 task to terminate with :func:`~asyncio.timeout`. If it's canceled or if the
    441 timeout elapses, :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task`
    442 is canceled, which is correct at this point.
    443 :meth:`~legacy.protocol.WebSocketCommonProtocol.close` then waits for
    444 :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task` but shields it
    445 to prevent cancellation.
    446 
    447 :meth:`~legacy.protocol.WebSocketCommonProtocol.close` and
    448 :meth:`~legacy.protocol.WebSocketCommonProtocol.fail_connection` are the only
    449 places where :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` may
    450 be canceled.
    451 
    452 :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task` starts by
    453 waiting for :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task`. It
    454 catches :exc:`~asyncio.CancelledError` to prevent a cancellation of
    455 :attr:`~legacy.protocol.WebSocketCommonProtocol.transfer_data_task` from propagating
    456 to :attr:`~legacy.protocol.WebSocketCommonProtocol.close_connection_task`.
    457 
    458 .. _backpressure:
    459 
    460 Backpressure
    461 ------------
    462 
    463 .. note::
    464 
    465    This section discusses backpressure from the perspective of a server but
    466    the concept applies to clients symmetrically.
    467 
    468 With a naive implementation, if a server receives inputs faster than it can
    469 process them, or if it generates outputs faster than it can send them, data
    470 accumulates in buffers, eventually causing the server to run out of memory and
    471 crash.
    472 
    473 The solution to this problem is backpressure. Any part of the server that
    474 receives inputs faster than it can process them and send the outputs
    475 must propagate that information back to the previous part in the chain.
    476 
    477 websockets is designed to make it easy to get backpressure right.
    478 
    479 For incoming data, websockets builds upon :class:`~asyncio.StreamReader` which
    480 propagates backpressure to its own buffer and to the TCP stream. Frames are
    481 parsed from the input stream and added to a bounded queue. If the queue fills
    482 up, parsing halts until the application reads a frame.
    483 
    484 For outgoing data, websockets builds upon :class:`~asyncio.StreamWriter` which
    485 implements flow control. If the output buffers grow too large, it waits until
    486 they're drained. That's why all APIs that write frames are asynchronous.
    487 
    488 Of course, it's still possible for an application to create its own unbounded
    489 buffers and break the backpressure. Be careful with queues.
    490 
    491 
    492 .. _buffers:
    493 
    494 Buffers
    495 -------
    496 
    497 .. note::
    498 
    499    This section discusses buffers from the perspective of a server but it
    500    applies to clients as well.
    501 
    502 An asynchronous systems works best when its buffers are almost always empty.
    503 
    504 For example, if a client sends data too fast for a server, the queue of
    505 incoming messages will be constantly full. The server will always be 32
    506 messages (by default) behind the client. This consumes memory and increases
    507 latency for no good reason. The problem is called bufferbloat.
    508 
    509 If buffers are almost always full and that problem cannot be solved by adding
    510 capacity — typically because the system is bottlenecked by the output and
    511 constantly regulated by backpressure — reducing the size of buffers minimizes
    512 negative consequences.
    513 
    514 By default websockets has rather high limits. You can decrease them according
    515 to your application's characteristics.
    516 
    517 Bufferbloat can happen at every level in the stack where there is a buffer.
    518 For each connection, the receiving side contains these buffers:
    519 
    520 - OS buffers: tuning them is an advanced optimization.
    521 - :class:`~asyncio.StreamReader` bytes buffer: the default limit is 64 KiB.
    522  You can set another limit by passing a ``read_limit`` keyword argument to
    523  :func:`~client.connect()` or :func:`~server.serve`.
    524 - Incoming messages :class:`~collections.deque`: its size depends both on
    525  the size and the number of messages it contains. By default the maximum
    526  UTF-8 encoded size is 1 MiB and the maximum number is 32. In the worst case,
    527  after UTF-8 decoding, a single message could take up to 4 MiB of memory and
    528  the overall memory consumption could reach 128 MiB. You should adjust these
    529  limits by setting the ``max_size`` and ``max_queue`` keyword arguments of
    530  :func:`~client.connect()` or :func:`~server.serve` according to your
    531  application's requirements.
    532 
    533 For each connection, the sending side contains these buffers:
    534 
    535 - :class:`~asyncio.StreamWriter` bytes buffer: the default size is 64 KiB.
    536  You can set another limit by passing a ``write_limit`` keyword argument to
    537  :func:`~client.connect()` or :func:`~server.serve`.
    538 - OS buffers: tuning them is an advanced optimization.
    539 
    540 Concurrency
    541 -----------
    542 
    543 Awaiting any combination of :meth:`~legacy.protocol.WebSocketCommonProtocol.recv`,
    544 :meth:`~legacy.protocol.WebSocketCommonProtocol.send`,
    545 :meth:`~legacy.protocol.WebSocketCommonProtocol.close`
    546 :meth:`~legacy.protocol.WebSocketCommonProtocol.ping`, or
    547 :meth:`~legacy.protocol.WebSocketCommonProtocol.pong` concurrently is safe, including
    548 multiple calls to the same method, with one exception and one limitation.
    549 
    550 * **Only one coroutine can receive messages at a time.** This constraint
    551  avoids non-deterministic behavior (and simplifies the implementation). If a
    552  coroutine is awaiting :meth:`~legacy.protocol.WebSocketCommonProtocol.recv`,
    553  awaiting it again in another coroutine raises :exc:`RuntimeError`.
    554 
    555 * **Sending a fragmented message forces serialization.** Indeed, the WebSocket
    556  protocol doesn't support multiplexing messages. If a coroutine is awaiting
    557  :meth:`~legacy.protocol.WebSocketCommonProtocol.send` to send a fragmented message,
    558  awaiting it again in another coroutine waits until the first call completes.
    559  This will be transparent in many cases. It may be a concern if the
    560  fragmented message is generated slowly by an asynchronous iterator.
    561 
    562 Receiving frames is independent from sending frames. This isolates
    563 :meth:`~legacy.protocol.WebSocketCommonProtocol.recv`, which receives frames, from
    564 the other methods, which send frames.
    565 
    566 While the connection is open, each frame is sent with a single write. Combined
    567 with the concurrency model of :mod:`asyncio`, this enforces serialization. The
    568 only other requirement is to prevent interleaving other data frames in the
    569 middle of a fragmented message.
    570 
    571 After the connection is closed, sending a frame raises
    572 :exc:`~websockets.exceptions.ConnectionClosed`, which is safe.