gh-70765: avoid waiting for HTTP headers when parsing HTTP/0.9 requests (#139514)

This commit is contained in:
Bénédikt Tran 2025-10-05 14:03:25 +02:00 committed by GitHub
parent 29616f3d2c
commit 13dc2fde8c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 49 additions and 0 deletions

View file

@ -302,6 +302,7 @@ def parse_request(self):
error response has already been sent back.
"""
is_http_0_9 = False
self.command = None # set in case of error on the first line
self.request_version = version = self.default_request_version
self.close_connection = True
@ -359,6 +360,7 @@ def parse_request(self):
HTTPStatus.BAD_REQUEST,
"Bad HTTP/0.9 request type (%r)" % command)
return False
is_http_0_9 = True
self.command, self.path = command, path
# gh-87389: The purpose of replacing '//' with '/' is to protect
@ -368,6 +370,11 @@ def parse_request(self):
if self.path.startswith('//'):
self.path = '/' + self.path.lstrip('/') # Reduce to a single /
# For HTTP/0.9, headers are not expected at all.
if is_http_0_9:
self.headers = {}
return True
# Examine the headers and look for a Connection directive.
try:
self.headers = http.client.parse_headers(self.rfile,

View file

@ -362,6 +362,43 @@ def test_head_via_send_error(self):
self.assertEqual(b'', data)
class HTTP09ServerTestCase(BaseTestCase):
class request_handler(NoLogRequestHandler, BaseHTTPRequestHandler):
"""Request handler for HTTP/0.9 server."""
def do_GET(self):
self.wfile.write(f'OK: here is {self.path}\r\n'.encode())
def setUp(self):
super().setUp()
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock = self.enterContext(self.sock)
self.sock.connect((self.HOST, self.PORT))
def test_simple_get(self):
self.sock.send(b'GET /index.html\r\n')
res = self.sock.recv(1024)
self.assertEqual(res, b"OK: here is /index.html\r\n")
def test_invalid_request(self):
self.sock.send(b'POST /index.html\r\n')
res = self.sock.recv(1024)
self.assertIn(b"Bad HTTP/0.9 request type ('POST')", res)
def test_single_request(self):
self.sock.send(b'GET /foo.html\r\n')
res = self.sock.recv(1024)
self.assertEqual(res, b"OK: here is /foo.html\r\n")
self.sock.send(b'GET /bar.html\r\n')
res = self.sock.recv(1024)
# The server will not parse more input as it closed the connection.
# Note that the socket connection itself is still opened since the
# client is responsible for also closing it on their side.
self.assertEqual(res, b'')
def certdata_file(*path):
return os.path.join(os.path.dirname(__file__), "certdata", *path)

View file

@ -0,0 +1,5 @@
:mod:`http.server`: fix default handling of HTTP/0.9 requests in
:class:`~http.server.BaseHTTPRequestHandler`. Previously,
:meth:`!BaseHTTPRequestHandler.parse_request`` incorrectly
waited for headers in the request although those are not
supported in HTTP/0.9. Patch by Bénédikt Tran.