https://github.com/python/cpython/commit/9f52b443c002cccfb54bda0706a9276b6b4a109d
commit: 9f52b443c002cccfb54bda0706a9276b6b4a109d
branch: main
author: blago1 <[email protected]>
committer: encukou <[email protected]>
date: 2026-10-14T14:50:35+02:00
summary:

gh-150751: Ignore optional whitespace around Content-Length in http.client 
(GH-159156)

gh-150752 started validating the Content-Length value against the
RFC 9112 grammar (1*DIGIT), but the compat32 header parser keeps
trailing SP/HTAB in the field value, so "Content-Length: 5 " is now
treated as a missing header. HTTPResponse then assumes that the
connection will close and read() blocks until the server closes it,
which never happens on a keep-alive connection.

RFC 9112, section 5.1 says that optional whitespace around a field
value is excluded by parsers. Strip SP/HTAB before the grammar check;
values such as "+5", "5_0" and "5 0" are still rejected.

files:
A Misc/NEWS.d/next/Library/2026-10-10-22-14-22.gh-issue-150751.JInuGL.rst
M Lib/http/client.py
M Lib/test/test_httplib.py

diff --git a/Lib/http/client.py b/Lib/http/client.py
index f13eb6d91b77f6..21484b835a08ff 100644
--- a/Lib/http/client.py
+++ b/Lib/http/client.py
@@ -397,6 +397,10 @@ def begin(self, *, _max_headers=None):
         # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked"
         self.length = None
         length = self.headers.get("content-length")
+        if length is not None:
+            # RFC 9112, section 5.1: optional whitespace (SP / HTAB) around
+            # the field value is not part of the value.
+            length = length.strip(' \t')
         if length and not self.chunked and _is_legal_content_length(length):
             self.length = int(length)
         else:
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
index 5fd4e8fca30fcf..1e5ec0d4e43c27 100644
--- a/Lib/test/test_httplib.py
+++ b/Lib/test/test_httplib.py
@@ -1351,7 +1351,7 @@ def test_negative_content_length(self):
     def test_malformed_content_length(self):
         # RFC 9112: Content-Length = 1*DIGIT.  Values that int() accepts but
         # the grammar forbids must not be used to frame the body.
-        for value in ('+5', '5_0'):
+        for value in ('+5', '5_0', '5 0'):
             with self.subTest(value=value):
                 sock = FakeSocket(
                     'HTTP/1.1 200 OK\r\nContent-Length: %s\r\n\r\nHello\r\n' % 
value)
@@ -1361,6 +1361,22 @@ def test_malformed_content_length(self):
                 self.assertEqual(resp.read(), b'Hello\r\n')
                 resp.close()
 
+    def test_content_length_with_whitespace(self):
+        # RFC 9112, section 5.1: optional whitespace (SP / HTAB) around
+        # the field value is not part of the value, so it must not
+        # prevent the Content-Length from being used to frame the body.
+        for value in ('7 ', '7\t', '7 \t ', ' 7 '):
+            with self.subTest(value=value):
+                sock = FakeSocket(
+                    'HTTP/1.1 200 OK\r\nContent-Length: %s\r\n\r\n'
+                    'Hello\r\nextra' % value)
+                resp = client.HTTPResponse(sock, method="GET")
+                resp.begin()
+                self.assertEqual(resp.length, 7)
+                self.assertEqual(resp.read(), b'Hello\r\n')
+                self.assertTrue(resp.isclosed())
+                resp.close()
+
     def test_malformed_chunk_size(self):
         # RFC 9112: chunk-size = 1*HEXDIG.  Reject sizes that int(_, 16) 
accepts
         # but the grammar forbids (a sign, an "0x" prefix, underscores or
diff --git 
a/Misc/NEWS.d/next/Library/2026-10-10-22-14-22.gh-issue-150751.JInuGL.rst 
b/Misc/NEWS.d/next/Library/2026-10-10-22-14-22.gh-issue-150751.JInuGL.rst
new file mode 100644
index 00000000000000..40c8d606aa6773
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-10-22-14-22.gh-issue-150751.JInuGL.rst
@@ -0,0 +1,5 @@
+:mod:`http.client` now ignores optional whitespace around the
+``Content-Length`` header value when validating it, as required by
+:rfc:`9112`. Previously a value with trailing whitespace (for example
+``Content-Length: 5`` followed by a space) was treated as missing and the
+response body was read until the connection was closed.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to