Skip to content

Commit cf2b944

Browse files
Fix server hang on chunked transfer encoding size mismatch (aio-libs#12119)
1 parent 7343fd3 commit cf2b944

File tree

3 files changed

+40
-0
lines changed

3 files changed

+40
-0
lines changed

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
@@ -889,6 +889,12 @@ def feed_data(
889889
if chunk[: len(SEP)] == SEP:
890890
chunk = chunk[len(SEP) :]
891891
self._chunk = ChunkState.PARSE_CHUNKED_SIZE
892+
elif len(chunk) >= len(SEP) or chunk != SEP[: len(chunk)]:
893+
exc = TransferEncodingError(
894+
"Chunk size mismatch: expected CRLF after chunk data"
895+
)
896+
set_exception(self.payload, exc)
897+
raise exc
892898
else:
893899
self._chunk_tail = chunk
894900
return False, b""

tests/test_http_parser.py

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

1750+
async def test_parse_chunked_payload_size_data_mismatch(
1751+
self, protocol: BaseProtocol
1752+
) -> None:
1753+
"""Chunk-size does not match actual data: should raise, not hang.
1754+
1755+
Regression test for #10596.
1756+
"""
1757+
out = aiohttp.StreamReader(protocol, 2**16, loop=asyncio.get_running_loop())
1758+
p = HttpPayloadParser(out, chunked=True, headers_parser=HeadersParser())
1759+
# Declared chunk-size is 4 but actual data is "Hello" (5 bytes).
1760+
# After consuming 4 bytes, remaining starts with "o" not "\r\n".
1761+
with pytest.raises(http_exceptions.TransferEncodingError):
1762+
p.feed_data(b"4\r\nHello\r\n0\r\n\r\n")
1763+
assert isinstance(out.exception(), http_exceptions.TransferEncodingError)
1764+
1765+
async def test_parse_chunked_payload_size_data_mismatch_too_short(
1766+
self, protocol: BaseProtocol
1767+
) -> None:
1768+
"""Chunk-size larger than data: declared 6 but only 5 bytes before CRLF.
1769+
1770+
Regression test for #10596.
1771+
"""
1772+
out = aiohttp.StreamReader(protocol, 2**16, loop=asyncio.get_running_loop())
1773+
p = HttpPayloadParser(out, chunked=True, headers_parser=HeadersParser())
1774+
# Declared chunk-size is 6 but actual data before CRLF is "Hello" (5 bytes).
1775+
# Parser reads 6 bytes: "Hello\r", then expects \r\n but sees "\n0\r\n..."
1776+
with pytest.raises(http_exceptions.TransferEncodingError):
1777+
p.feed_data(b"6\r\nHello\r\n0\r\n\r\n")
1778+
assert isinstance(out.exception(), http_exceptions.TransferEncodingError)
1779+
17501780
async def test_parse_chunked_payload_split_end(
17511781
self, protocol: BaseProtocol
17521782
) -> None:

0 commit comments

Comments
 (0)