mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
[3.14] gh-70765: avoid waiting for HTTP headers when parsing HTTP/0.9 requests (GH-139514) (#139600)
(cherry picked from commit13dc2fde8c) (cherry picked from commit1fe89d324e) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
297157d968
commit
32233d68d8
3 changed files with 51 additions and 0 deletions
|
|
@ -324,6 +324,7 @@ def parse_request(self):
|
||||||
error response has already been sent back.
|
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.command = None # set in case of error on the first line
|
||||||
self.request_version = version = self.default_request_version
|
self.request_version = version = self.default_request_version
|
||||||
self.close_connection = True
|
self.close_connection = True
|
||||||
|
|
@ -381,6 +382,7 @@ def parse_request(self):
|
||||||
HTTPStatus.BAD_REQUEST,
|
HTTPStatus.BAD_REQUEST,
|
||||||
"Bad HTTP/0.9 request type (%r)" % command)
|
"Bad HTTP/0.9 request type (%r)" % command)
|
||||||
return False
|
return False
|
||||||
|
is_http_0_9 = True
|
||||||
self.command, self.path = command, path
|
self.command, self.path = command, path
|
||||||
|
|
||||||
# gh-87389: The purpose of replacing '//' with '/' is to protect
|
# gh-87389: The purpose of replacing '//' with '/' is to protect
|
||||||
|
|
@ -390,6 +392,11 @@ def parse_request(self):
|
||||||
if self.path.startswith('//'):
|
if self.path.startswith('//'):
|
||||||
self.path = '/' + self.path.lstrip('/') # Reduce to a single /
|
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.
|
# Examine the headers and look for a Connection directive.
|
||||||
try:
|
try:
|
||||||
self.headers = http.client.parse_headers(self.rfile,
|
self.headers = http.client.parse_headers(self.rfile,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
SimpleHTTPRequestHandler, CGIHTTPRequestHandler
|
SimpleHTTPRequestHandler, CGIHTTPRequestHandler
|
||||||
from http import server, HTTPStatus
|
from http import server, HTTPStatus
|
||||||
|
|
||||||
|
import contextlib
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -359,6 +360,44 @@ def test_head_via_send_error(self):
|
||||||
self.assertEqual(b'', data)
|
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")
|
||||||
|
|
||||||
|
# Ignore errors if the connection is already closed,
|
||||||
|
# as this is the expected behavior of HTTP/0.9.
|
||||||
|
with contextlib.suppress(OSError):
|
||||||
|
self.sock.send(b'GET /bar.html\r\n')
|
||||||
|
res = self.sock.recv(1024)
|
||||||
|
# The server should not process our request.
|
||||||
|
self.assertEqual(res, b'')
|
||||||
|
|
||||||
|
|
||||||
def certdata_file(*path):
|
def certdata_file(*path):
|
||||||
return os.path.join(os.path.dirname(__file__), "certdata", *path)
|
return os.path.join(os.path.dirname(__file__), "certdata", *path)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue