From 60acc63318890b27dab628edd2737f57232027ab Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 3 Jul 2026 22:54:18 +0300 Subject: [PATCH] gh-54930: Send a status line in error responses to malformed request lines Previously such error responses were sent in the bare HTTP/0.9 style, without a status line and headers. Co-Authored-By: Claude Fable 5 --- Lib/http/server.py | 6 +++++- Lib/test/test_httpservers.py | 15 +++++++++++++++ .../2026-07-03-21-10-00.gh-issue-54930.hq09Er.rst | 5 +++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-03-21-10-00.gh-issue-54930.hq09Er.rst diff --git a/Lib/http/server.py b/Lib/http/server.py index ebc85052aecb90..095b5744bd12fc 100644 --- a/Lib/http/server.py +++ b/Lib/http/server.py @@ -334,10 +334,13 @@ def parse_request(self): raise ValueError("unreasonable length http version") version_number = int(version_number[0]), int(version_number[1]) except (ValueError, IndexError): + # Send the error response with a status line and headers. + self.request_version = '' self.send_error( HTTPStatus.BAD_REQUEST, "Bad request version (%r)" % version) return False + self.request_version = version if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1": self.close_connection = False if version_number >= (2, 0): @@ -345,9 +348,9 @@ def parse_request(self): HTTPStatus.HTTP_VERSION_NOT_SUPPORTED, "Invalid HTTP version (%s)" % base_version_number) return False - self.request_version = version if not 2 <= len(words) <= 3: + self.request_version = '' self.send_error( HTTPStatus.BAD_REQUEST, "Bad request syntax (%r)" % requestline) @@ -356,6 +359,7 @@ def parse_request(self): if len(words) == 2: self.close_connection = True if command != 'GET': + self.request_version = '' self.send_error( HTTPStatus.BAD_REQUEST, "Bad HTTP/0.9 request type (%r)" % command) diff --git a/Lib/test/test_httpservers.py b/Lib/test/test_httpservers.py index d4ae032610a91e..63f65a9c5cf47a 100644 --- a/Lib/test/test_httpservers.py +++ b/Lib/test/test_httpservers.py @@ -386,6 +386,8 @@ def test_simple_get(self): def test_invalid_request(self): self.sock.send(b'POST /index.html\r\n') res = self.sock.recv(1024) + # The error response is not sent in the bare HTTP/0.9 style. + self.assertStartsWith(res, b'HTTP/1.0 400 ') self.assertIn(b"Bad HTTP/0.9 request type ('POST')", res) def test_single_request(self): @@ -1102,6 +1104,19 @@ def test_http_0_9(self): self.assertEqual(result[0], b'Data\r\n') self.verify_get_called() + @support.subTests('request,code', [ + (b'GET / FUBAR\r\n\r\n', 400), # bad version + (b'GET / HTTP/2.0\r\n\r\n', 505), # unsupported version + (b'GET\r\n', 400), # bad syntax + (b'POST /\r\n', 400), # bad HTTP/0.9 request type + ]) + def test_request_line_error_has_status_line(self, request, code): + self.handler = SocketlessRequestHandler() + result = self.send_typical_request(request) + self.assertStartsWith(result[0], b'HTTP/1.1 %d ' % code) + self.verify_expected_headers(result[1:result.index(b'\r\n')]) + self.assertFalse(self.handler.get_called) + def test_extra_space(self): result = self.send_typical_request( b'GET /spaced out HTTP/1.1\r\n' diff --git a/Misc/NEWS.d/next/Library/2026-07-03-21-10-00.gh-issue-54930.hq09Er.rst b/Misc/NEWS.d/next/Library/2026-07-03-21-10-00.gh-issue-54930.hq09Er.rst new file mode 100644 index 00000000000000..1cfc0212c609a6 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-03-21-10-00.gh-issue-54930.hq09Er.rst @@ -0,0 +1,5 @@ +Error responses of :class:`http.server.BaseHTTPRequestHandler` to malformed +request lines now include a status line and headers instead of being sent in +the bare HTTP/0.9 style. +Only a valid HTTP/0.9 request (a two-word ``GET`` request line) now receives +an HTTP/0.9 style response.