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.
 
 
 
 

359 rivejä
10 KiB

  1. """
  2. :mod:`websockets.framing` reads and writes WebSocket frames.
  3. It deals with a single frame at a time. Anything that depends on the sequence
  4. of frames is implemented in :mod:`websockets.protocol`.
  5. See `section 5 of RFC 6455`_.
  6. .. _section 5 of RFC 6455: http://tools.ietf.org/html/rfc6455#section-5
  7. """
  8. import io
  9. import random
  10. import struct
  11. from typing import Any, Awaitable, Callable, NamedTuple, Optional, Sequence, Tuple
  12. from .exceptions import PayloadTooBig, ProtocolError
  13. from .typing import Data
  14. try:
  15. from .speedups import apply_mask
  16. except ImportError: # pragma: no cover
  17. from .utils import apply_mask
  18. __all__ = [
  19. "DATA_OPCODES",
  20. "CTRL_OPCODES",
  21. "OP_CONT",
  22. "OP_TEXT",
  23. "OP_BINARY",
  24. "OP_CLOSE",
  25. "OP_PING",
  26. "OP_PONG",
  27. "Frame",
  28. "prepare_data",
  29. "encode_data",
  30. "parse_close",
  31. "serialize_close",
  32. ]
  33. DATA_OPCODES = OP_CONT, OP_TEXT, OP_BINARY = 0x00, 0x01, 0x02
  34. CTRL_OPCODES = OP_CLOSE, OP_PING, OP_PONG = 0x08, 0x09, 0x0A
  35. # Close code that are allowed in a close frame.
  36. # Using a list optimizes `code in EXTERNAL_CLOSE_CODES`.
  37. EXTERNAL_CLOSE_CODES = [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011]
  38. # Remove FrameData when dropping support for Python < 3.6.1 — the first
  39. # version where NamedTuple supports default values, methods, and docstrings.
  40. # Consider converting to a dataclass when dropping support for Python < 3.7.
  41. class FrameData(NamedTuple):
  42. fin: bool
  43. opcode: int
  44. data: bytes
  45. rsv1: bool
  46. rsv2: bool
  47. rsv3: bool
  48. class Frame(FrameData):
  49. """
  50. WebSocket frame.
  51. :param bool fin: FIN bit
  52. :param bool rsv1: RSV1 bit
  53. :param bool rsv2: RSV2 bit
  54. :param bool rsv3: RSV3 bit
  55. :param int opcode: opcode
  56. :param bytes data: payload data
  57. Only these fields are needed. The MASK bit, payload length and masking-key
  58. are handled on the fly by :meth:`read` and :meth:`write`.
  59. """
  60. def __new__(
  61. cls,
  62. fin: bool,
  63. opcode: int,
  64. data: bytes,
  65. rsv1: bool = False,
  66. rsv2: bool = False,
  67. rsv3: bool = False,
  68. ) -> "Frame":
  69. return FrameData.__new__(cls, fin, opcode, data, rsv1, rsv2, rsv3)
  70. @classmethod
  71. async def read(
  72. cls,
  73. reader: Callable[[int], Awaitable[bytes]],
  74. *,
  75. mask: bool,
  76. max_size: Optional[int] = None,
  77. extensions: Optional[Sequence["websockets.extensions.base.Extension"]] = None,
  78. ) -> "Frame":
  79. """
  80. Read a WebSocket frame.
  81. :param reader: coroutine that reads exactly the requested number of
  82. bytes, unless the end of file is reached
  83. :param mask: whether the frame should be masked i.e. whether the read
  84. happens on the server side
  85. :param max_size: maximum payload size in bytes
  86. :param extensions: list of classes with a ``decode()`` method that
  87. transforms the frame and return a new frame; extensions are applied
  88. in reverse order
  89. :raises ~websockets.exceptions.PayloadTooBig: if the frame exceeds
  90. ``max_size``
  91. :raises ~websockets.exceptions.ProtocolError: if the frame
  92. contains incorrect values
  93. """
  94. # Read the header.
  95. data = await reader(2)
  96. head1, head2 = struct.unpack("!BB", data)
  97. # While not Pythonic, this is marginally faster than calling bool().
  98. fin = True if head1 & 0b10000000 else False
  99. rsv1 = True if head1 & 0b01000000 else False
  100. rsv2 = True if head1 & 0b00100000 else False
  101. rsv3 = True if head1 & 0b00010000 else False
  102. opcode = head1 & 0b00001111
  103. if (True if head2 & 0b10000000 else False) != mask:
  104. raise ProtocolError("incorrect masking")
  105. length = head2 & 0b01111111
  106. if length == 126:
  107. data = await reader(2)
  108. length, = struct.unpack("!H", data)
  109. elif length == 127:
  110. data = await reader(8)
  111. length, = struct.unpack("!Q", data)
  112. if max_size is not None and length > max_size:
  113. raise PayloadTooBig(
  114. f"payload length exceeds size limit ({length} > {max_size} bytes)"
  115. )
  116. if mask:
  117. mask_bits = await reader(4)
  118. # Read the data.
  119. data = await reader(length)
  120. if mask:
  121. data = apply_mask(data, mask_bits)
  122. frame = cls(fin, opcode, data, rsv1, rsv2, rsv3)
  123. if extensions is None:
  124. extensions = []
  125. for extension in reversed(extensions):
  126. frame = extension.decode(frame, max_size=max_size)
  127. frame.check()
  128. return frame
  129. def write(
  130. frame,
  131. writer: Callable[[bytes], Any],
  132. *,
  133. mask: bool,
  134. extensions: Optional[Sequence["websockets.extensions.base.Extension"]] = None,
  135. ) -> None:
  136. """
  137. Write a WebSocket frame.
  138. :param frame: frame to write
  139. :param writer: function that writes bytes
  140. :param mask: whether the frame should be masked i.e. whether the write
  141. happens on the client side
  142. :param extensions: list of classes with an ``encode()`` method that
  143. transform the frame and return a new frame; extensions are applied
  144. in order
  145. :raises ~websockets.exceptions.ProtocolError: if the frame
  146. contains incorrect values
  147. """
  148. # The first parameter is called `frame` rather than `self`,
  149. # but it's the instance of class to which this method is bound.
  150. frame.check()
  151. if extensions is None:
  152. extensions = []
  153. for extension in extensions:
  154. frame = extension.encode(frame)
  155. output = io.BytesIO()
  156. # Prepare the header.
  157. head1 = (
  158. (0b10000000 if frame.fin else 0)
  159. | (0b01000000 if frame.rsv1 else 0)
  160. | (0b00100000 if frame.rsv2 else 0)
  161. | (0b00010000 if frame.rsv3 else 0)
  162. | frame.opcode
  163. )
  164. head2 = 0b10000000 if mask else 0
  165. length = len(frame.data)
  166. if length < 126:
  167. output.write(struct.pack("!BB", head1, head2 | length))
  168. elif length < 65536:
  169. output.write(struct.pack("!BBH", head1, head2 | 126, length))
  170. else:
  171. output.write(struct.pack("!BBQ", head1, head2 | 127, length))
  172. if mask:
  173. mask_bits = struct.pack("!I", random.getrandbits(32))
  174. output.write(mask_bits)
  175. # Prepare the data.
  176. if mask:
  177. data = apply_mask(frame.data, mask_bits)
  178. else:
  179. data = frame.data
  180. output.write(data)
  181. # Send the frame.
  182. # The frame is written in a single call to writer in order to prevent
  183. # TCP fragmentation. See #68 for details. This also makes it safe to
  184. # send frames concurrently from multiple coroutines.
  185. writer(output.getvalue())
  186. def check(frame) -> None:
  187. """
  188. Check that reserved bits and opcode have acceptable values.
  189. :raises ~websockets.exceptions.ProtocolError: if a reserved
  190. bit or the opcode is invalid
  191. """
  192. # The first parameter is called `frame` rather than `self`,
  193. # but it's the instance of class to which this method is bound.
  194. if frame.rsv1 or frame.rsv2 or frame.rsv3:
  195. raise ProtocolError("reserved bits must be 0")
  196. if frame.opcode in DATA_OPCODES:
  197. return
  198. elif frame.opcode in CTRL_OPCODES:
  199. if len(frame.data) > 125:
  200. raise ProtocolError("control frame too long")
  201. if not frame.fin:
  202. raise ProtocolError("fragmented control frame")
  203. else:
  204. raise ProtocolError(f"invalid opcode: {frame.opcode}")
  205. def prepare_data(data: Data) -> Tuple[int, bytes]:
  206. """
  207. Convert a string or byte-like object to an opcode and a bytes-like object.
  208. This function is designed for data frames.
  209. If ``data`` is a :class:`str`, return ``OP_TEXT`` and a :class:`bytes`
  210. object encoding ``data`` in UTF-8.
  211. If ``data`` is a bytes-like object, return ``OP_BINARY`` and a bytes-like
  212. object.
  213. :raises TypeError: if ``data`` doesn't have a supported type
  214. """
  215. if isinstance(data, str):
  216. return OP_TEXT, data.encode("utf-8")
  217. elif isinstance(data, (bytes, bytearray)):
  218. return OP_BINARY, data
  219. elif isinstance(data, memoryview):
  220. if data.c_contiguous:
  221. return OP_BINARY, data
  222. else:
  223. return OP_BINARY, data.tobytes()
  224. else:
  225. raise TypeError("data must be bytes-like or str")
  226. def encode_data(data: Data) -> bytes:
  227. """
  228. Convert a string or byte-like object to bytes.
  229. This function is designed for ping and pong frames.
  230. If ``data`` is a :class:`str`, return a :class:`bytes` object encoding
  231. ``data`` in UTF-8.
  232. If ``data`` is a bytes-like object, return a :class:`bytes` object.
  233. :raises TypeError: if ``data`` doesn't have a supported type
  234. """
  235. if isinstance(data, str):
  236. return data.encode("utf-8")
  237. elif isinstance(data, (bytes, bytearray)):
  238. return bytes(data)
  239. elif isinstance(data, memoryview):
  240. return data.tobytes()
  241. else:
  242. raise TypeError("data must be bytes-like or str")
  243. def parse_close(data: bytes) -> Tuple[int, str]:
  244. """
  245. Parse the payload from a close frame.
  246. Return ``(code, reason)``.
  247. :raises ~websockets.exceptions.ProtocolError: if data is ill-formed
  248. :raises UnicodeDecodeError: if the reason isn't valid UTF-8
  249. """
  250. length = len(data)
  251. if length >= 2:
  252. code, = struct.unpack("!H", data[:2])
  253. check_close(code)
  254. reason = data[2:].decode("utf-8")
  255. return code, reason
  256. elif length == 0:
  257. return 1005, ""
  258. else:
  259. assert length == 1
  260. raise ProtocolError("close frame too short")
  261. def serialize_close(code: int, reason: str) -> bytes:
  262. """
  263. Serialize the payload for a close frame.
  264. This is the reverse of :func:`parse_close`.
  265. """
  266. check_close(code)
  267. return struct.pack("!H", code) + reason.encode("utf-8")
  268. def check_close(code: int) -> None:
  269. """
  270. Check that the close code has an acceptable value for a close frame.
  271. :raises ~websockets.exceptions.ProtocolError: if the close code
  272. is invalid
  273. """
  274. if not (code in EXTERNAL_CLOSE_CODES or 3000 <= code < 5000):
  275. raise ProtocolError("invalid status code")
  276. # at the bottom to allow circular import, because Extension depends on Frame
  277. import websockets.extensions.base # isort:skip # noqa