From 650925caf88a73268b0396cab6b2f5c41b4e745b Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Tue, 30 Jun 2026 13:47:02 +0000 Subject: [PATCH 1/2] DeflateBuffer: handle empty chunks in feed_data without raising The chunked-transfer decoder resumes a paused res --- .github/workflows/ci-cd.yml | 4 ++-- CHANGES/12994.bugfix.rst | 1 + CONTRIBUTORS.txt | 1 + aiohttp/http_parser.py | 2 +- tests/test_http_parser.py | 29 +++++++++++++++++++++++++---- 5 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 CHANGES/12994.bugfix.rst diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index c018dba6177..f9f99c7d09f 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -75,7 +75,7 @@ jobs: with: python-version: 3.11 - name: Cache PyPI - uses: actions/cache@v6.1.0 + uses: actions/cache@v5.0.5 with: key: pip-lint-${{ hashFiles('requirements/*.txt') }} path: ~/.cache/pip @@ -158,7 +158,7 @@ jobs: with: submodules: true - name: Cache llhttp generated files - uses: actions/cache@v6.1.0 + uses: actions/cache@v5.0.5 id: cache with: key: llhttp-${{ hashFiles('vendor/llhttp/package*.json', 'vendor/llhttp/src/**/*') }} diff --git a/CHANGES/12994.bugfix.rst b/CHANGES/12994.bugfix.rst new file mode 100644 index 00000000000..a1f69791ac5 --- /dev/null +++ b/CHANGES/12994.bugfix.rst @@ -0,0 +1 @@ +Fixed :py:class:`~aiohttp.http_parser.DeflateBuffer` to treat an empty ``feed_data`` call as a \ No newline at end of file diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 65c20d023e7..14873f27ede 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -442,3 +442,4 @@ Zlatan Sičanica Łukasz Setla Марк Коренберг Семён Марьясин +Hrach Shahumyan diff --git a/aiohttp/http_parser.py b/aiohttp/http_parser.py index 6abbe04feae..9a8d3538e88 100644 --- a/aiohttp/http_parser.py +++ b/aiohttp/http_parser.py @@ -1137,7 +1137,7 @@ def feed_data(self, chunk: bytes) -> bool: # RFC1950 # bits 0..3 = CM = 0b1000 = 8 = "deflate" # bits 4..7 = CINFO = 1..7 = windows size. - if ( + if chunk and ( not self._started_decoding and self.encoding == "deflate" and chunk[0] & 0xF != 8 diff --git a/tests/test_http_parser.py b/tests/test_http_parser.py index e61af88cc16..7cd66c3e19b 100644 --- a/tests/test_http_parser.py +++ b/tests/test_http_parser.py @@ -857,15 +857,15 @@ def test_request_chunked(parser: HttpRequestParser) -> None: def test_te_header_non_ascii(parser: HttpRequestParser) -> None: - # K = Kelvin sign, not valid ascii. - text = "GET /test HTTP/1.1\r\nHost: a\r\nTransfer-Encoding: chunKed\r\n\r\n" + # K = Kelvin sign, not valid ascii. + text = "GET /test HTTP/1.1\r\nHost: a\r\nTransfer-Encoding: chunKed\r\n\r\n" with pytest.raises(http_exceptions.BadHttpMessage): parser.feed_data(text.encode()) def test_upgrade_header_non_ascii(parser: HttpRequestParser) -> None: - # K = Kelvin sign, not valid ascii. - text = "GET /test HTTP/1.1\r\nHost: a\r\nUpgrade: websocKet\r\n\r\n" + # K = Kelvin sign, not valid ascii. + text = "GET /test HTTP/1.1\r\nHost: a\r\nUpgrade: websocKet\r\n\r\n" messages, upgrade, tail = parser.feed_data(text.encode()) assert not upgrade @@ -2982,6 +2982,27 @@ async def test_feed_data(self, protocol: BaseProtocol) -> None: dbuf.feed_data(b"xxxx") assert [b"line"] == list(buf._buffer) + async def test_feed_data_empty(self, protocol: BaseProtocol) -> None: + """feed_data(b"") must not raise even on a fresh deflate stream. + + The chunked-transfer decoder calls feed_data(b"") to give a paused + decoder another chance to make progress. The CM-byte sniff in + feed_data would previously read chunk[0] on an empty chunk and raise + IndexError. Regression test for #12994. + """ + buf = aiohttp.StreamReader( + protocol, DEFAULT_CHUNK_SIZE, loop=asyncio.get_running_loop() + ) + dbuf = DeflateBuffer(buf, "deflate") + + # Should be a no-op, returning False (no more data). + assert dbuf.feed_data(b"") is False + # No bytes pushed to the downstream stream. + assert list(buf._buffer) == [] + # Decoder was not switched to suppress_deflate_header by the empty + # chunk. + assert dbuf._started_decoding is False + async def test_feed_data_err(self, protocol: BaseProtocol) -> None: buf = aiohttp.StreamReader(protocol, 2**16, loop=asyncio.get_running_loop()) dbuf = DeflateBuffer(buf, "deflate") From 42c32d3e48376b167b3b25dc4e26cac7e171374f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 30 Jun 2026 13:48:30 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CHANGES/12994.bugfix.rst | 2 +- CONTRIBUTORS.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGES/12994.bugfix.rst b/CHANGES/12994.bugfix.rst index a1f69791ac5..6221cf43a3a 100644 --- a/CHANGES/12994.bugfix.rst +++ b/CHANGES/12994.bugfix.rst @@ -1 +1 @@ -Fixed :py:class:`~aiohttp.http_parser.DeflateBuffer` to treat an empty ``feed_data`` call as a \ No newline at end of file +Fixed :py:class:`~aiohttp.http_parser.DeflateBuffer` to treat an empty ``feed_data`` call as a diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 14873f27ede..719f3eac01e 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -165,6 +165,7 @@ Hans Adema Harmon Y. Harry Liu Hiroshi Ogawa +Hrach Shahumyan Hrishikesh Paranjape Hu Bo Hugh Young @@ -442,4 +443,3 @@ Zlatan Sičanica Łukasz Setla Марк Коренберг Семён Марьясин -Hrach Shahumyan