You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On ESP32, after receiving 5-10 full websocket frames, this read returns only a partial frame:
payload = await self.reader.read(length)
This is to be expected, as the read(length) call in MicroPython's asyncio.Stream is not guaranteed to return the full length bytes in a single call, especially for large payloads, due to non-blocking I/O or buffer constraints.
I didn't observe this issue on unix/desktop MicroPython, only on the ESP32.
The proper way is to re-do the read() for the remaining length until the entire frame has been received.
The patch below fixes that, as well as adding some error handling.
--- old/aiohttp_ws.py 2025-05-20 14:06:16.111521205 +0200
+++ aiohttp/aiohttp_ws.py 2025-05-20 14:16:28.985286423 +0200
@@ -197,13 +199,31 @@
return opcode, payload
fin, opcode, has_mask, length = self._parse_frame_header(header)
if length == 126: # Magic number, length header is 2 bytes
- (length,) = struct.unpack("!H", await self.reader.read(2))
+ length_data = await self.reader.read(2)
+ if len(length_data) != 2:
+ print("WARNING: aiohttp_ws.py failed to read 2-byte length, closing")
+ return self.CLOSE, b""
+ (length,) = struct.unpack("!H", length_data)
elif length == 127: # Magic number, length header is 8 bytes
- (length,) = struct.unpack("!Q", await self.reader.read(8))
-
+ length_data = await self.reader.read(8)
+ if len(length_data) != 8:
+ print("WARNING: aiohttp_ws.py failed to read 8-byte length, closing")
+ return self.CLOSE, b""
+ (length,) = struct.unpack("!Q", length_data)
if has_mask: # pragma: no cover
mask = await self.reader.read(4)
- payload = await self.reader.read(length)
+ if len(mask) != 4:
+ print("WARNING: aiohttp_ws.py failed to read mask, closing")
+ return self.CLOSE, b""
+ payload = b""
+ remaining_length = length
+ while remaining_length > 0:
+ chunk = await self.reader.read(remaining_length)
+ if not chunk: # Connection closed or error
+ print(f"WARNING: aiohttp_ws.py connection closed while reading payload, got {len(payload)}/{length} bytes, closing")
+ return self.CLOSE, b""
+ payload += chunk
+ remaining_length -= len(chunk)
if has_mask: # pragma: no cover
payload = bytes(x ^ mask[i % 4] for i, x in enumerate(payload))
return opcode, payload
The text was updated successfully, but these errors were encountered:
On ESP32, after receiving 5-10 full websocket frames, this read returns only a partial frame:
This is to be expected, as the read(length) call in MicroPython's asyncio.Stream is not guaranteed to return the full length bytes in a single call, especially for large payloads, due to non-blocking I/O or buffer constraints.
I didn't observe this issue on unix/desktop MicroPython, only on the ESP32.
The proper way is to re-do the read() for the remaining length until the entire frame has been received.
The patch below fixes that, as well as adding some error handling.
The text was updated successfully, but these errors were encountered: