Skip to content

Commit 60acc63

Browse files
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 <noreply@anthropic.com>
1 parent ac1acb6 commit 60acc63

3 files changed

Lines changed: 25 additions & 1 deletion

File tree

Lib/http/server.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,20 +334,23 @@ def parse_request(self):
334334
raise ValueError("unreasonable length http version")
335335
version_number = int(version_number[0]), int(version_number[1])
336336
except (ValueError, IndexError):
337+
# Send the error response with a status line and headers.
338+
self.request_version = ''
337339
self.send_error(
338340
HTTPStatus.BAD_REQUEST,
339341
"Bad request version (%r)" % version)
340342
return False
343+
self.request_version = version
341344
if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
342345
self.close_connection = False
343346
if version_number >= (2, 0):
344347
self.send_error(
345348
HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
346349
"Invalid HTTP version (%s)" % base_version_number)
347350
return False
348-
self.request_version = version
349351

350352
if not 2 <= len(words) <= 3:
353+
self.request_version = ''
351354
self.send_error(
352355
HTTPStatus.BAD_REQUEST,
353356
"Bad request syntax (%r)" % requestline)
@@ -356,6 +359,7 @@ def parse_request(self):
356359
if len(words) == 2:
357360
self.close_connection = True
358361
if command != 'GET':
362+
self.request_version = ''
359363
self.send_error(
360364
HTTPStatus.BAD_REQUEST,
361365
"Bad HTTP/0.9 request type (%r)" % command)

Lib/test/test_httpservers.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,8 @@ def test_simple_get(self):
386386
def test_invalid_request(self):
387387
self.sock.send(b'POST /index.html\r\n')
388388
res = self.sock.recv(1024)
389+
# The error response is not sent in the bare HTTP/0.9 style.
390+
self.assertStartsWith(res, b'HTTP/1.0 400 ')
389391
self.assertIn(b"Bad HTTP/0.9 request type ('POST')", res)
390392

391393
def test_single_request(self):
@@ -1102,6 +1104,19 @@ def test_http_0_9(self):
11021104
self.assertEqual(result[0], b'<html><body>Data</body></html>\r\n')
11031105
self.verify_get_called()
11041106

1107+
@support.subTests('request,code', [
1108+
(b'GET / FUBAR\r\n\r\n', 400), # bad version
1109+
(b'GET / HTTP/2.0\r\n\r\n', 505), # unsupported version
1110+
(b'GET\r\n', 400), # bad syntax
1111+
(b'POST /\r\n', 400), # bad HTTP/0.9 request type
1112+
])
1113+
def test_request_line_error_has_status_line(self, request, code):
1114+
self.handler = SocketlessRequestHandler()
1115+
result = self.send_typical_request(request)
1116+
self.assertStartsWith(result[0], b'HTTP/1.1 %d ' % code)
1117+
self.verify_expected_headers(result[1:result.index(b'\r\n')])
1118+
self.assertFalse(self.handler.get_called)
1119+
11051120
def test_extra_space(self):
11061121
result = self.send_typical_request(
11071122
b'GET /spaced out HTTP/1.1\r\n'
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Error responses of :class:`http.server.BaseHTTPRequestHandler` to malformed
2+
request lines now include a status line and headers instead of being sent in
3+
the bare HTTP/0.9 style.
4+
Only a valid HTTP/0.9 request (a two-word ``GET`` request line) now receives
5+
an HTTP/0.9 style response.

0 commit comments

Comments
 (0)