Skip to content

Commit 78d45e7

Browse files
Fix Python parser when chunk separators align (#8720) (#8722)
(cherry picked from commit 6d3d1fc)
1 parent 5ef8cba commit 78d45e7

3 files changed

Lines changed: 26 additions & 2 deletions

File tree

CHANGES/8720.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed an edge case in the Python parser when chunk separators happen to align with network chunks -- by :user:`Dreamsorcerer`.

aiohttp/http_parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -870,13 +870,13 @@ def feed_data(
870870
self._chunk_size = 0
871871
self.payload.feed_data(chunk[:required], required)
872872
chunk = chunk[required:]
873-
if self._lax and chunk.startswith(b"\r"):
874-
chunk = chunk[1:]
875873
self._chunk = ChunkState.PARSE_CHUNKED_CHUNK_EOF
876874
self.payload.end_http_chunk_receiving()
877875

878876
# toss the CRLF at the end of the chunk
879877
if self._chunk == ChunkState.PARSE_CHUNKED_CHUNK_EOF:
878+
if self._lax and chunk.startswith(b"\r"):
879+
chunk = chunk[1:]
880880
if chunk[: len(SEP)] == SEP:
881881
chunk = chunk[len(SEP) :]
882882
self._chunk = ChunkState.PARSE_CHUNKED_SIZE

tests/test_http_parser.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,29 @@ def test_parse_chunked_payload_empty_body_than_another_chunked(
14101410
assert b"second" == b"".join(d for d in payload._buffer)
14111411

14121412

1413+
async def test_parse_chunked_payload_split_chunks(response: Any) -> None:
1414+
network_chunks = (
1415+
b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n",
1416+
b"5\r\nfi",
1417+
b"rst",
1418+
# This simulates a bug in lax mode caused when the \r\n separator, before the
1419+
# next HTTP chunk, appears at the start of the next network chunk.
1420+
b"\r\n",
1421+
b"6",
1422+
b"\r",
1423+
b"\n",
1424+
b"second\r",
1425+
b"\n0\r\n\r\n",
1426+
)
1427+
reader = response.feed_data(network_chunks[0])[0][0][1]
1428+
for c in network_chunks[1:]:
1429+
response.feed_data(c)
1430+
1431+
assert response.feed_eof() is None
1432+
assert reader.is_eof()
1433+
assert await reader.read() == b"firstsecond"
1434+
1435+
14131436
def test_partial_url(parser: Any) -> None:
14141437
messages, upgrade, tail = parser.feed_data(b"GET /te")
14151438
assert len(messages) == 0

0 commit comments

Comments
 (0)