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]