From dd1de5ee3065f1eb60c954db9995b3e216e09e67 Mon Sep 17 00:00:00 2001 From: Brandon Holbrook Date: Mon, 4 Dec 2023 17:30:05 -0600 Subject: [PATCH] websocket: fix bug where interleaved control frame breaks compression --- tornado/websocket.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tornado/websocket.py b/tornado/websocket.py index fbfd700887..89b7146392 100644 --- a/tornado/websocket.py +++ b/tornado/websocket.py @@ -1117,7 +1117,9 @@ async def _receive_frame(self) -> None: reserved_bits = header & self.RSV_MASK opcode = header & self.OPCODE_MASK opcode_is_control = opcode & 0x8 - if self._decompressor is not None and opcode != 0: + # Control frames are never compressed, but can also be interleaved with a series + # of fragmented data frames. Don't let them affect _frame_compressed + if self._decompressor is not None and opcode != 0 and not opcode_is_control: # Compression flag is present in the first frame's header, # but we can't decompress until we have all the frames of # the message. @@ -1197,7 +1199,10 @@ def _handle_message(self, opcode: int, data: bytes) -> "Optional[Future[None]]": if self.client_terminated: return None - if self._frame_compressed: + opcode_is_control = opcode & 0x8 + # _frame_compressed does not apply to control frames. + # Control frames are never compressed. + if self._frame_compressed and not opcode_is_control: assert self._decompressor is not None try: data = self._decompressor.decompress(data)