Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

protocol.py 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. from __future__ import annotations
  2. import enum
  3. import logging
  4. import uuid
  5. from typing import Generator, List, Optional, Type, Union
  6. from .exceptions import (
  7. ConnectionClosed,
  8. ConnectionClosedError,
  9. ConnectionClosedOK,
  10. InvalidState,
  11. PayloadTooBig,
  12. ProtocolError,
  13. )
  14. from .extensions import Extension
  15. from .frames import (
  16. OK_CLOSE_CODES,
  17. OP_BINARY,
  18. OP_CLOSE,
  19. OP_CONT,
  20. OP_PING,
  21. OP_PONG,
  22. OP_TEXT,
  23. Close,
  24. Frame,
  25. )
  26. from .http11 import Request, Response
  27. from .streams import StreamReader
  28. from .typing import LoggerLike, Origin, Subprotocol
  29. __all__ = [
  30. "Protocol",
  31. "Side",
  32. "State",
  33. "SEND_EOF",
  34. ]
  35. Event = Union[Request, Response, Frame]
  36. """Events that :meth:`~Protocol.events_received` may return."""
  37. class Side(enum.IntEnum):
  38. """A WebSocket connection is either a server or a client."""
  39. SERVER, CLIENT = range(2)
  40. SERVER = Side.SERVER
  41. CLIENT = Side.CLIENT
  42. class State(enum.IntEnum):
  43. """A WebSocket connection is in one of these four states."""
  44. CONNECTING, OPEN, CLOSING, CLOSED = range(4)
  45. CONNECTING = State.CONNECTING
  46. OPEN = State.OPEN
  47. CLOSING = State.CLOSING
  48. CLOSED = State.CLOSED
  49. SEND_EOF = b""
  50. """Sentinel signaling that the TCP connection must be half-closed."""
  51. class Protocol:
  52. """
  53. Sans-I/O implementation of a WebSocket connection.
  54. Args:
  55. side: :attr:`~Side.CLIENT` or :attr:`~Side.SERVER`.
  56. state: initial state of the WebSocket connection.
  57. max_size: maximum size of incoming messages in bytes;
  58. :obj:`None` disables the limit.
  59. logger: logger for this connection; depending on ``side``,
  60. defaults to ``logging.getLogger("websockets.client")``
  61. or ``logging.getLogger("websockets.server")``;
  62. see the :doc:`logging guide <../../topics/logging>` for details.
  63. """
  64. def __init__(
  65. self,
  66. side: Side,
  67. *,
  68. state: State = OPEN,
  69. max_size: Optional[int] = 2**20,
  70. logger: Optional[LoggerLike] = None,
  71. ) -> None:
  72. # Unique identifier. For logs.
  73. self.id: uuid.UUID = uuid.uuid4()
  74. """Unique identifier of the connection. Useful in logs."""
  75. # Logger or LoggerAdapter for this connection.
  76. if logger is None:
  77. logger = logging.getLogger(f"websockets.{side.name.lower()}")
  78. self.logger: LoggerLike = logger
  79. """Logger for this connection."""
  80. # Track if DEBUG is enabled. Shortcut logging calls if it isn't.
  81. self.debug = logger.isEnabledFor(logging.DEBUG)
  82. # Connection side. CLIENT or SERVER.
  83. self.side = side
  84. # Connection state. Initially OPEN because subclasses handle CONNECTING.
  85. self.state = state
  86. # Maximum size of incoming messages in bytes.
  87. self.max_size = max_size
  88. # Current size of incoming message in bytes. Only set while reading a
  89. # fragmented message i.e. a data frames with the FIN bit not set.
  90. self.cur_size: Optional[int] = None
  91. # True while sending a fragmented message i.e. a data frames with the
  92. # FIN bit not set.
  93. self.expect_continuation_frame = False
  94. # WebSocket protocol parameters.
  95. self.origin: Optional[Origin] = None
  96. self.extensions: List[Extension] = []
  97. self.subprotocol: Optional[Subprotocol] = None
  98. # Close code and reason, set when a close frame is sent or received.
  99. self.close_rcvd: Optional[Close] = None
  100. self.close_sent: Optional[Close] = None
  101. self.close_rcvd_then_sent: Optional[bool] = None
  102. # Track if an exception happened during the handshake.
  103. self.handshake_exc: Optional[Exception] = None
  104. """
  105. Exception to raise if the opening handshake failed.
  106. :obj:`None` if the opening handshake succeeded.
  107. """
  108. # Track if send_eof() was called.
  109. self.eof_sent = False
  110. # Parser state.
  111. self.reader = StreamReader()
  112. self.events: List[Event] = []
  113. self.writes: List[bytes] = []
  114. self.parser = self.parse()
  115. next(self.parser) # start coroutine
  116. self.parser_exc: Optional[Exception] = None
  117. @property
  118. def state(self) -> State:
  119. """
  120. WebSocket connection state.
  121. Defined in 4.1, 4.2, 7.1.3, and 7.1.4 of :rfc:`6455`.
  122. """
  123. return self._state
  124. @state.setter
  125. def state(self, state: State) -> None:
  126. if self.debug:
  127. self.logger.debug("= connection is %s", state.name)
  128. self._state = state
  129. @property
  130. def close_code(self) -> Optional[int]:
  131. """
  132. `WebSocket close code`_.
  133. .. _WebSocket close code:
  134. https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.5
  135. :obj:`None` if the connection isn't closed yet.
  136. """
  137. if self.state is not CLOSED:
  138. return None
  139. elif self.close_rcvd is None:
  140. return 1006
  141. else:
  142. return self.close_rcvd.code
  143. @property
  144. def close_reason(self) -> Optional[str]:
  145. """
  146. `WebSocket close reason`_.
  147. .. _WebSocket close reason:
  148. https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.6
  149. :obj:`None` if the connection isn't closed yet.
  150. """
  151. if self.state is not CLOSED:
  152. return None
  153. elif self.close_rcvd is None:
  154. return ""
  155. else:
  156. return self.close_rcvd.reason
  157. @property
  158. def close_exc(self) -> ConnectionClosed:
  159. """
  160. Exception to raise when trying to interact with a closed connection.
  161. Don't raise this exception while the connection :attr:`state`
  162. is :attr:`~websockets.protocol.State.CLOSING`; wait until
  163. it's :attr:`~websockets.protocol.State.CLOSED`.
  164. Indeed, the exception includes the close code and reason, which are
  165. known only once the connection is closed.
  166. Raises:
  167. AssertionError: if the connection isn't closed yet.
  168. """
  169. assert self.state is CLOSED, "connection isn't closed yet"
  170. exc_type: Type[ConnectionClosed]
  171. if (
  172. self.close_rcvd is not None
  173. and self.close_sent is not None
  174. and self.close_rcvd.code in OK_CLOSE_CODES
  175. and self.close_sent.code in OK_CLOSE_CODES
  176. ):
  177. exc_type = ConnectionClosedOK
  178. else:
  179. exc_type = ConnectionClosedError
  180. exc: ConnectionClosed = exc_type(
  181. self.close_rcvd,
  182. self.close_sent,
  183. self.close_rcvd_then_sent,
  184. )
  185. # Chain to the exception raised in the parser, if any.
  186. exc.__cause__ = self.parser_exc
  187. return exc
  188. # Public methods for receiving data.
  189. def receive_data(self, data: bytes) -> None:
  190. """
  191. Receive data from the network.
  192. After calling this method:
  193. - You must call :meth:`data_to_send` and send this data to the network.
  194. - You should call :meth:`events_received` and process resulting events.
  195. Raises:
  196. EOFError: if :meth:`receive_eof` was called earlier.
  197. """
  198. self.reader.feed_data(data)
  199. next(self.parser)
  200. def receive_eof(self) -> None:
  201. """
  202. Receive the end of the data stream from the network.
  203. After calling this method:
  204. - You must call :meth:`data_to_send` and send this data to the network;
  205. it will return ``[b""]``, signaling the end of the stream, or ``[]``.
  206. - You aren't expected to call :meth:`events_received`; it won't return
  207. any new events.
  208. Raises:
  209. EOFError: if :meth:`receive_eof` was called earlier.
  210. """
  211. self.reader.feed_eof()
  212. next(self.parser)
  213. # Public methods for sending events.
  214. def send_continuation(self, data: bytes, fin: bool) -> None:
  215. """
  216. Send a `Continuation frame`_.
  217. .. _Continuation frame:
  218. https://datatracker.ietf.org/doc/html/rfc6455#section-5.6
  219. Parameters:
  220. data: payload containing the same kind of data
  221. as the initial frame.
  222. fin: FIN bit; set it to :obj:`True` if this is the last frame
  223. of a fragmented message and to :obj:`False` otherwise.
  224. Raises:
  225. ProtocolError: if a fragmented message isn't in progress.
  226. """
  227. if not self.expect_continuation_frame:
  228. raise ProtocolError("unexpected continuation frame")
  229. self.expect_continuation_frame = not fin
  230. self.send_frame(Frame(OP_CONT, data, fin))
  231. def send_text(self, data: bytes, fin: bool = True) -> None:
  232. """
  233. Send a `Text frame`_.
  234. .. _Text frame:
  235. https://datatracker.ietf.org/doc/html/rfc6455#section-5.6
  236. Parameters:
  237. data: payload containing text encoded with UTF-8.
  238. fin: FIN bit; set it to :obj:`False` if this is the first frame of
  239. a fragmented message.
  240. Raises:
  241. ProtocolError: if a fragmented message is in progress.
  242. """
  243. if self.expect_continuation_frame:
  244. raise ProtocolError("expected a continuation frame")
  245. self.expect_continuation_frame = not fin
  246. self.send_frame(Frame(OP_TEXT, data, fin))
  247. def send_binary(self, data: bytes, fin: bool = True) -> None:
  248. """
  249. Send a `Binary frame`_.
  250. .. _Binary frame:
  251. https://datatracker.ietf.org/doc/html/rfc6455#section-5.6
  252. Parameters:
  253. data: payload containing arbitrary binary data.
  254. fin: FIN bit; set it to :obj:`False` if this is the first frame of
  255. a fragmented message.
  256. Raises:
  257. ProtocolError: if a fragmented message is in progress.
  258. """
  259. if self.expect_continuation_frame:
  260. raise ProtocolError("expected a continuation frame")
  261. self.expect_continuation_frame = not fin
  262. self.send_frame(Frame(OP_BINARY, data, fin))
  263. def send_close(self, code: Optional[int] = None, reason: str = "") -> None:
  264. """
  265. Send a `Close frame`_.
  266. .. _Close frame:
  267. https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.1
  268. Parameters:
  269. code: close code.
  270. reason: close reason.
  271. Raises:
  272. ProtocolError: if a fragmented message is being sent, if the code
  273. isn't valid, or if a reason is provided without a code
  274. """
  275. if self.expect_continuation_frame:
  276. raise ProtocolError("expected a continuation frame")
  277. if code is None:
  278. if reason != "":
  279. raise ProtocolError("cannot send a reason without a code")
  280. close = Close(1005, "")
  281. data = b""
  282. else:
  283. close = Close(code, reason)
  284. data = close.serialize()
  285. # send_frame() guarantees that self.state is OPEN at this point.
  286. # 7.1.3. The WebSocket Closing Handshake is Started
  287. self.send_frame(Frame(OP_CLOSE, data))
  288. self.close_sent = close
  289. self.state = CLOSING
  290. def send_ping(self, data: bytes) -> None:
  291. """
  292. Send a `Ping frame`_.
  293. .. _Ping frame:
  294. https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2
  295. Parameters:
  296. data: payload containing arbitrary binary data.
  297. """
  298. self.send_frame(Frame(OP_PING, data))
  299. def send_pong(self, data: bytes) -> None:
  300. """
  301. Send a `Pong frame`_.
  302. .. _Pong frame:
  303. https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3
  304. Parameters:
  305. data: payload containing arbitrary binary data.
  306. """
  307. self.send_frame(Frame(OP_PONG, data))
  308. def fail(self, code: int, reason: str = "") -> None:
  309. """
  310. `Fail the WebSocket connection`_.
  311. .. _Fail the WebSocket connection:
  312. https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.7
  313. Parameters:
  314. code: close code
  315. reason: close reason
  316. Raises:
  317. ProtocolError: if the code isn't valid.
  318. """
  319. # 7.1.7. Fail the WebSocket Connection
  320. # Send a close frame when the state is OPEN (a close frame was already
  321. # sent if it's CLOSING), except when failing the connection because
  322. # of an error reading from or writing to the network.
  323. if self.state is OPEN:
  324. if code != 1006:
  325. close = Close(code, reason)
  326. data = close.serialize()
  327. self.send_frame(Frame(OP_CLOSE, data))
  328. self.close_sent = close
  329. self.state = CLOSING
  330. # When failing the connection, a server closes the TCP connection
  331. # without waiting for the client to complete the handshake, while a
  332. # client waits for the server to close the TCP connection, possibly
  333. # after sending a close frame that the client will ignore.
  334. if self.side is SERVER and not self.eof_sent:
  335. self.send_eof()
  336. # 7.1.7. Fail the WebSocket Connection "An endpoint MUST NOT continue
  337. # to attempt to process data(including a responding Close frame) from
  338. # the remote endpoint after being instructed to _Fail the WebSocket
  339. # Connection_."
  340. self.parser = self.discard()
  341. next(self.parser) # start coroutine
  342. # Public method for getting incoming events after receiving data.
  343. def events_received(self) -> List[Event]:
  344. """
  345. Fetch events generated from data received from the network.
  346. Call this method immediately after any of the ``receive_*()`` methods.
  347. Process resulting events, likely by passing them to the application.
  348. Returns:
  349. List[Event]: Events read from the connection.
  350. """
  351. events, self.events = self.events, []
  352. return events
  353. # Public method for getting outgoing data after receiving data or sending events.
  354. def data_to_send(self) -> List[bytes]:
  355. """
  356. Obtain data to send to the network.
  357. Call this method immediately after any of the ``receive_*()``,
  358. ``send_*()``, or :meth:`fail` methods.
  359. Write resulting data to the connection.
  360. The empty bytestring :data:`~websockets.protocol.SEND_EOF` signals
  361. the end of the data stream. When you receive it, half-close the TCP
  362. connection.
  363. Returns:
  364. List[bytes]: Data to write to the connection.
  365. """
  366. writes, self.writes = self.writes, []
  367. return writes
  368. def close_expected(self) -> bool:
  369. """
  370. Tell if the TCP connection is expected to close soon.
  371. Call this method immediately after any of the ``receive_*()``,
  372. ``send_close()``, or :meth:`fail` methods.
  373. If it returns :obj:`True`, schedule closing the TCP connection after a
  374. short timeout if the other side hasn't already closed it.
  375. Returns:
  376. bool: Whether the TCP connection is expected to close soon.
  377. """
  378. # We expect a TCP close if and only if we sent a close frame:
  379. # * Normal closure: once we send a close frame, we expect a TCP close:
  380. # server waits for client to complete the TCP closing handshake;
  381. # client waits for server to initiate the TCP closing handshake.
  382. # * Abnormal closure: we always send a close frame and the same logic
  383. # applies, except on EOFError where we don't send a close frame
  384. # because we already received the TCP close, so we don't expect it.
  385. # We already got a TCP Close if and only if the state is CLOSED.
  386. return self.state is CLOSING or self.handshake_exc is not None
  387. # Private methods for receiving data.
  388. def parse(self) -> Generator[None, None, None]:
  389. """
  390. Parse incoming data into frames.
  391. :meth:`receive_data` and :meth:`receive_eof` run this generator
  392. coroutine until it needs more data or reaches EOF.
  393. :meth:`parse` never raises an exception. Instead, it sets the
  394. :attr:`parser_exc` and yields control.
  395. """
  396. try:
  397. while True:
  398. if (yield from self.reader.at_eof()):
  399. if self.debug:
  400. self.logger.debug("< EOF")
  401. # If the WebSocket connection is closed cleanly, with a
  402. # closing handhshake, recv_frame() substitutes parse()
  403. # with discard(). This branch is reached only when the
  404. # connection isn't closed cleanly.
  405. raise EOFError("unexpected end of stream")
  406. if self.max_size is None:
  407. max_size = None
  408. elif self.cur_size is None:
  409. max_size = self.max_size
  410. else:
  411. max_size = self.max_size - self.cur_size
  412. # During a normal closure, execution ends here on the next
  413. # iteration of the loop after receiving a close frame. At
  414. # this point, recv_frame() replaced parse() by discard().
  415. frame = yield from Frame.parse(
  416. self.reader.read_exact,
  417. mask=self.side is SERVER,
  418. max_size=max_size,
  419. extensions=self.extensions,
  420. )
  421. if self.debug:
  422. self.logger.debug("< %s", frame)
  423. self.recv_frame(frame)
  424. except ProtocolError as exc:
  425. self.fail(1002, str(exc))
  426. self.parser_exc = exc
  427. except EOFError as exc:
  428. self.fail(1006, str(exc))
  429. self.parser_exc = exc
  430. except UnicodeDecodeError as exc:
  431. self.fail(1007, f"{exc.reason} at position {exc.start}")
  432. self.parser_exc = exc
  433. except PayloadTooBig as exc:
  434. self.fail(1009, str(exc))
  435. self.parser_exc = exc
  436. except Exception as exc:
  437. self.logger.error("parser failed", exc_info=True)
  438. # Don't include exception details, which may be security-sensitive.
  439. self.fail(1011)
  440. self.parser_exc = exc
  441. # During an abnormal closure, execution ends here after catching an
  442. # exception. At this point, fail() replaced parse() by discard().
  443. yield
  444. raise AssertionError("parse() shouldn't step after error")
  445. def discard(self) -> Generator[None, None, None]:
  446. """
  447. Discard incoming data.
  448. This coroutine replaces :meth:`parse`:
  449. - after receiving a close frame, during a normal closure (1.4);
  450. - after sending a close frame, during an abnormal closure (7.1.7).
  451. """
  452. # The server close the TCP connection in the same circumstances where
  453. # discard() replaces parse(). The client closes the connection later,
  454. # after the server closes the connection or a timeout elapses.
  455. # (The latter case cannot be handled in this Sans-I/O layer.)
  456. assert (self.side is SERVER) == (self.eof_sent)
  457. while not (yield from self.reader.at_eof()):
  458. self.reader.discard()
  459. if self.debug:
  460. self.logger.debug("< EOF")
  461. # A server closes the TCP connection immediately, while a client
  462. # waits for the server to close the TCP connection.
  463. if self.side is CLIENT:
  464. self.send_eof()
  465. self.state = CLOSED
  466. # If discard() completes normally, execution ends here.
  467. yield
  468. # Once the reader reaches EOF, its feed_data/eof() methods raise an
  469. # error, so our receive_data/eof() methods don't step the generator.
  470. raise AssertionError("discard() shouldn't step after EOF")
  471. def recv_frame(self, frame: Frame) -> None:
  472. """
  473. Process an incoming frame.
  474. """
  475. if frame.opcode is OP_TEXT or frame.opcode is OP_BINARY:
  476. if self.cur_size is not None:
  477. raise ProtocolError("expected a continuation frame")
  478. if frame.fin:
  479. self.cur_size = None
  480. else:
  481. self.cur_size = len(frame.data)
  482. elif frame.opcode is OP_CONT:
  483. if self.cur_size is None:
  484. raise ProtocolError("unexpected continuation frame")
  485. if frame.fin:
  486. self.cur_size = None
  487. else:
  488. self.cur_size += len(frame.data)
  489. elif frame.opcode is OP_PING:
  490. # 5.5.2. Ping: "Upon receipt of a Ping frame, an endpoint MUST
  491. # send a Pong frame in response"
  492. pong_frame = Frame(OP_PONG, frame.data)
  493. self.send_frame(pong_frame)
  494. elif frame.opcode is OP_PONG:
  495. # 5.5.3 Pong: "A response to an unsolicited Pong frame is not
  496. # expected."
  497. pass
  498. elif frame.opcode is OP_CLOSE:
  499. # 7.1.5. The WebSocket Connection Close Code
  500. # 7.1.6. The WebSocket Connection Close Reason
  501. self.close_rcvd = Close.parse(frame.data)
  502. if self.state is CLOSING:
  503. assert self.close_sent is not None
  504. self.close_rcvd_then_sent = False
  505. if self.cur_size is not None:
  506. raise ProtocolError("incomplete fragmented message")
  507. # 5.5.1 Close: "If an endpoint receives a Close frame and did
  508. # not previously send a Close frame, the endpoint MUST send a
  509. # Close frame in response. (When sending a Close frame in
  510. # response, the endpoint typically echos the status code it
  511. # received.)"
  512. if self.state is OPEN:
  513. # Echo the original data instead of re-serializing it with
  514. # Close.serialize() because that fails when the close frame
  515. # is empty and Close.parse() synthesizes a 1005 close code.
  516. # The rest is identical to send_close().
  517. self.send_frame(Frame(OP_CLOSE, frame.data))
  518. self.close_sent = self.close_rcvd
  519. self.close_rcvd_then_sent = True
  520. self.state = CLOSING
  521. # 7.1.2. Start the WebSocket Closing Handshake: "Once an
  522. # endpoint has both sent and received a Close control frame,
  523. # that endpoint SHOULD _Close the WebSocket Connection_"
  524. # A server closes the TCP connection immediately, while a client
  525. # waits for the server to close the TCP connection.
  526. if self.side is SERVER:
  527. self.send_eof()
  528. # 1.4. Closing Handshake: "after receiving a control frame
  529. # indicating the connection should be closed, a peer discards
  530. # any further data received."
  531. self.parser = self.discard()
  532. next(self.parser) # start coroutine
  533. else:
  534. # This can't happen because Frame.parse() validates opcodes.
  535. raise AssertionError(f"unexpected opcode: {frame.opcode:02x}")
  536. self.events.append(frame)
  537. # Private methods for sending events.
  538. def send_frame(self, frame: Frame) -> None:
  539. if self.state is not OPEN:
  540. raise InvalidState(
  541. f"cannot write to a WebSocket in the {self.state.name} state"
  542. )
  543. if self.debug:
  544. self.logger.debug("> %s", frame)
  545. self.writes.append(
  546. frame.serialize(mask=self.side is CLIENT, extensions=self.extensions)
  547. )
  548. def send_eof(self) -> None:
  549. assert not self.eof_sent
  550. self.eof_sent = True
  551. if self.debug:
  552. self.logger.debug("> EOF")
  553. self.writes.append(SEND_EOF)