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.