Skip to content

Commit 4bcb942

Browse files
[PR #12119/0e2d3ec4 backport][3.14] Fix server hang on chunked transfer encoding size mismatch (#12122)
**This is a backport of PR #12119 as merged into master (0e2d3ec).** Co-authored-by: Fridayai700 <[email protected]>
1 parent 6bebdca commit 4bcb942

3 files changed

Lines changed: 40 additions & 0 deletions

File tree

CHANGES/10596.bugfix.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fixed server hanging indefinitely when chunked transfer encoding chunk-size
2+
does not match actual data length. The server now raises
3+
``TransferEncodingError`` instead of waiting forever for data that will
4+
never arrive -- by :user:`Fridayai700`.

aiohttp/http_parser.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,12 @@ def feed_data(
917917
if chunk[: len(SEP)] == SEP:
918918
chunk = chunk[len(SEP) :]
919919
self._chunk = ChunkState.PARSE_CHUNKED_SIZE
920+
elif len(chunk) >= len(SEP) or chunk != SEP[: len(chunk)]:
921+
exc = TransferEncodingError(
922+
"Chunk size mismatch: expected CRLF after chunk data"
923+
)
924+
set_exception(self.payload, exc)
925+
raise exc
920926
else:
921927
self._chunk_tail = chunk
922928
return False, b""

tests/test_http_parser.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,6 +1698,36 @@ async def test_parse_chunked_payload_size_error(
16981698
p.feed_data(b"blah\r\n")
16991699
assert isinstance(out.exception(), http_exceptions.TransferEncodingError)
17001700

1701+
async def test_parse_chunked_payload_size_data_mismatch(
1702+
self, protocol: BaseProtocol
1703+
) -> None:
1704+
"""Chunk-size does not match actual data: should raise, not hang.
1705+
1706+
Regression test for #10596.
1707+
"""
1708+
out = aiohttp.StreamReader(protocol, 2**16, loop=asyncio.get_running_loop())
1709+
p = HttpPayloadParser(out, chunked=True, headers_parser=HeadersParser())
1710+
# Declared chunk-size is 4 but actual data is "Hello" (5 bytes).
1711+
# After consuming 4 bytes, remaining starts with "o" not "\r\n".
1712+
with pytest.raises(http_exceptions.TransferEncodingError):
1713+
p.feed_data(b"4\r\nHello\r\n0\r\n\r\n")
1714+
assert isinstance(out.exception(), http_exceptions.TransferEncodingError)
1715+
1716+
async def test_parse_chunked_payload_size_data_mismatch_too_short(
1717+
self, protocol: BaseProtocol
1718+
) -> None:
1719+
"""Chunk-size larger than data: declared 6 but only 5 bytes before CRLF.
1720+
1721+
Regression test for #10596.
1722+
"""
1723+
out = aiohttp.StreamReader(protocol, 2**16, loop=asyncio.get_running_loop())
1724+
p = HttpPayloadParser(out, chunked=True, headers_parser=HeadersParser())
1725+
# Declared chunk-size is 6 but actual data before CRLF is "Hello" (5 bytes).
1726+
# Parser reads 6 bytes: "Hello\r", then expects \r\n but sees "\n0\r\n..."
1727+
with pytest.raises(http_exceptions.TransferEncodingError):
1728+
p.feed_data(b"6\r\nHello\r\n0\r\n\r\n")
1729+
assert isinstance(out.exception(), http_exceptions.TransferEncodingError)
1730+
17011731
async def test_parse_chunked_payload_split_end(
17021732
self, protocol: BaseProtocol
17031733
) -> None:

0 commit comments

Comments
 (0)