diff --git a/Lib/http/client.py b/Lib/http/client.py index 7ef99e7201c005..af10201f8c7e0e 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -408,6 +408,9 @@ def begin(self, *, _max_headers=None): 100 <= status < 200 or # 1xx codes self._method == "HEAD"): self.length = 0 + # These responses never have a body, so a Transfer-Encoding + # header must not make read() wait for chunks (RFC 9112 6.3). + self.chunked = False # if the connection remains open, and we aren't using chunked, and # a content-length was not provided, then assume that the connection diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 5b1d6e0aa52079..c53b5224574ec4 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1548,6 +1548,20 @@ def test_chunked_sync(self): self.assertEqual(sock.file.read(), extradata.encode("ascii")) #we read to the end resp.close() + def test_bodiless_status_with_chunked_sync(self): + """1xx, 204 and 304 responses have no body even with Transfer-Encoding: chunked""" + extradata = b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok" + for status in (b"204 No Content", b"304 Not Modified"): + with self.subTest(status=status): + sock = FakeSocket(b"HTTP/1.1 " + status + + b"\r\nTransfer-Encoding: chunked\r\n\r\n" + extradata) + resp = client.HTTPResponse(sock, method="GET") + resp.begin() + self.assertEqual(resp.read(), b"") + # the next response must still be unread + self.assertEqual(sock.file.read(), extradata) + resp.close() + def test_content_length_sync(self): """Check that we don't read past the end of the Content-Length stream""" extradata = b"extradata" diff --git a/Misc/NEWS.d/next/Library/2026-09-22-13-43-27.gh-issue-157951.5xqNEQ.rst b/Misc/NEWS.d/next/Library/2026-09-22-13-43-27.gh-issue-157951.5xqNEQ.rst new file mode 100644 index 00000000000000..84ad06cc65fb1d --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-22-13-43-27.gh-issue-157951.5xqNEQ.rst @@ -0,0 +1,3 @@ +Fix :mod:`http.client` blocking in :meth:`~http.client.HTTPResponse.read` when +a 1xx, 204 or 304 response has a ``Transfer-Encoding: chunked`` header. Such +responses never have a body.