Skip to content

Commit 4dcfac7

Browse files
serhiy-storchakaclaude
authored andcommitted
gh-54930: Send a status line in error responses to malformed request lines (GH-152980)
Previously such error responses were sent in the bare HTTP/0.9 style, without a status line and headers. (cherry picked from commit 2ab620b) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 5095562 commit 4dcfac7

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
@@ -360,20 +360,23 @@ def parse_request(self):
360360
raise ValueError("unreasonable length http version")
361361
version_number = int(version_number[0]), int(version_number[1])
362362
except (ValueError, IndexError):
363+
# Send the error response with a status line and headers.
364+
self.request_version = ''
363365
self.send_error(
364366
HTTPStatus.BAD_REQUEST,
365367
"Bad request version (%r)" % version)
366368
return False
369+
self.request_version = version
367370
if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1":
368371
self.close_connection = False
369372
if version_number >= (2, 0):
370373
self.send_error(
371374
HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
372375
"Invalid HTTP version (%s)" % base_version_number)
373376
return False
374-
self.request_version = version
375377

376378
if not 2 <= len(words) <= 3:
379+
self.request_version = ''
377380
self.send_error(
378381
HTTPStatus.BAD_REQUEST,
379382
"Bad request syntax (%r)" % requestline)
@@ -382,6 +385,7 @@ def parse_request(self):
382385
if len(words) == 2:
383386
self.close_connection = True
384387
if command != 'GET':
388+
self.request_version = ''
385389
self.send_error(
386390
HTTPStatus.BAD_REQUEST,
387391
"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
@@ -382,6 +382,8 @@ def test_simple_get(self):
382382
def test_invalid_request(self):
383383
self.sock.send(b'POST /index.html\r\n')
384384
res = self.sock.recv(1024)
385+
# The error response is not sent in the bare HTTP/0.9 style.
386+
self.assertStartsWith(res, b'HTTP/1.0 400 ')
385387
self.assertIn(b"Bad HTTP/0.9 request type ('POST')", res)
386388

387389
def test_single_request(self):
@@ -1345,6 +1347,19 @@ def test_http_0_9(self):
13451347
self.assertEqual(result[0], b'<html><body>Data</body></html>\r\n')
13461348
self.verify_get_called()
13471349

1350+
@support.subTests('request,code', [
1351+
(b'GET / FUBAR\r\n\r\n', 400), # bad version
1352+
(b'GET / HTTP/2.0\r\n\r\n', 505), # unsupported version
1353+
(b'GET\r\n', 400), # bad syntax
1354+
(b'POST /\r\n', 400), # bad HTTP/0.9 request type
1355+
])
1356+
def test_request_line_error_has_status_line(self, request, code):
1357+
self.handler = SocketlessRequestHandler()
1358+
result = self.send_typical_request(request)
1359+
self.assertStartsWith(result[0], b'HTTP/1.1 %d ' % code)
1360+
self.verify_expected_headers(result[1:result.index(b'\r\n')])
1361+
self.assertFalse(self.handler.get_called)
1362+
13481363
def test_extra_space(self):
13491364
result = self.send_typical_request(
13501365
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)