# HG changeset patch # Parent 60b6e820abe5d196508e2c0ddcfdae72805b4afa Issue 10721: Remove attempted support for HTTP 0.9 in http.server Based on patch by Antoine Pitrou. diff -r 60b6e820abe5 Doc/whatsnew/3.7.rst --- a/Doc/whatsnew/3.7.rst Fri Oct 28 19:01:46 2016 -0400 +++ b/Doc/whatsnew/3.7.rst Sat Oct 29 00:55:13 2016 +0000 @@ -108,6 +108,9 @@ Removed ======= +* The :class:`http.server.BaseHTTPRequestHandler` class no longer + accepts requests that are missing the HTTP version field, and no longer + sends HTTP 0.9 responses. Porting to Python 3.7 diff -r 60b6e820abe5 Lib/http/server.py --- a/Lib/http/server.py Fri Oct 28 19:01:46 2016 -0400 +++ b/Lib/http/server.py Sat Oct 29 00:55:13 2016 +0000 @@ -177,14 +177,6 @@ Similarly, for output, lines ought to be separated by CRLF pairs but most clients grok LF characters just fine. - If the first line of the request has the form - - - - (i.e. is left out) then this is assumed to be an HTTP - 0.9 request; this form has no optional headers and data part and - the reply consists of just the data. - The reply form of the HTTP 1.x protocol again has three parts: 1. One line giving the response code @@ -257,8 +249,7 @@ # The default request version. This only affects responses up until # the point where the request line is parsed, so it mainly decides what # the client gets back when sending a malformed request line. - # Most web servers default to HTTP 0.9, i.e. don't send a status line. - default_request_version = "HTTP/0.9" + default_request_version = "HTTP/1.0" def parse_request(self): """Parse a request (internal). @@ -306,14 +297,6 @@ HTTPStatus.HTTP_VERSION_NOT_SUPPORTED, "Invalid HTTP version (%s)" % base_version_number) return False - elif len(words) == 2: - command, path = words - self.close_connection = True - if command != 'GET': - self.send_error( - HTTPStatus.BAD_REQUEST, - "Bad HTTP/0.9 request type (%r)" % command) - return False elif not words: return False else: @@ -487,25 +470,23 @@ def send_response_only(self, code, message=None): """Send the response header only.""" - if self.request_version != 'HTTP/0.9': - if message is None: - if code in self.responses: - message = self.responses[code][0] - else: - message = '' - if not hasattr(self, '_headers_buffer'): - self._headers_buffer = [] - self._headers_buffer.append(("%s %d %s\r\n" % - (self.protocol_version, code, message)).encode( - 'latin-1', 'strict')) + if message is None: + if code in self.responses: + message = self.responses[code][0] + else: + message = '' + if not hasattr(self, '_headers_buffer'): + self._headers_buffer = [] + self._headers_buffer.append(("%s %d %s\r\n" % + (self.protocol_version, code, message)).encode( + 'latin-1', 'strict')) def send_header(self, keyword, value): """Send a MIME header to the headers buffer.""" - if self.request_version != 'HTTP/0.9': - if not hasattr(self, '_headers_buffer'): - self._headers_buffer = [] - self._headers_buffer.append( - ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict')) + if not hasattr(self, '_headers_buffer'): + self._headers_buffer = [] + self._headers_buffer.append( + ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict')) if keyword.lower() == 'connection': if value.lower() == 'close': @@ -515,9 +496,8 @@ def end_headers(self): """Send the blank line ending the MIME headers.""" - if self.request_version != 'HTTP/0.9': - self._headers_buffer.append(b"\r\n") - self.flush_headers() + self._headers_buffer.append(b"\r\n") + self.flush_headers() def flush_headers(self): if hasattr(self, '_headers_buffer'): diff -r 60b6e820abe5 Lib/test/test_httpservers.py --- a/Lib/test/test_httpservers.py Fri Oct 28 19:01:46 2016 -0400 +++ b/Lib/test/test_httpservers.py Sat Oct 29 00:55:13 2016 +0000 @@ -154,11 +154,12 @@ self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) def test_version_none_get(self): + # Sends "GET / \r\n", with second space but empty version field self.con._http_vsn_str = '' self.con.putrequest('GET', '/') self.con.endheaders() res = self.con.getresponse() - self.assertEqual(res.status, HTTPStatus.NOT_IMPLEMENTED) + self.assertEqual(res.status, HTTPStatus.BAD_REQUEST) def test_version_none(self): # Test that a valid method is rejected when not HTTP/1.x @@ -816,12 +817,21 @@ self.assertEqual(self.handler.request_version, 'HTTP/1.0') self.assertSequenceEqual(self.handler.headers.items(), ()) - def test_http_0_9(self): + def test_http_0_9_in_http1(self): + # This test actually uses the HTTP 1 version and request format result = self.send_typical_request(b'GET / HTTP/0.9\r\n\r\n') - self.assertEqual(len(result), 1) - self.assertEqual(result[0], b'Data\r\n') + self.verify_http_server_response(result[0]) + self.verify_expected_headers(result[1:-1]) + self.assertEqual(result[-1], b'Data\r\n') self.verify_get_called() + def test_simple_request(self): + # Test a HTTP 0.9 Simple-Request + result = self.send_typical_request(b'GET /\r\n') + self.assertTrue(result[0].startswith(b'HTTP/1.1 400 ')) + self.verify_expected_headers(result[1:result.index(b'\r\n')]) + self.assertFalse(self.handler.get_called) + def test_with_continue_1_0(self): result = self.send_typical_request(b'GET / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n') self.verify_http_server_response(result[0]) diff -r 60b6e820abe5 Misc/NEWS --- a/Misc/NEWS Fri Oct 28 19:01:46 2016 -0400 +++ b/Misc/NEWS Sat Oct 29 00:55:13 2016 +0000 @@ -99,6 +99,10 @@ Library ------- +- Issue #10721: In the "http.server" module, reject requests that are missing + the HTTP version field. Also, remove support for sending HTTP 0.9 + responses, since HTTP 0.9 request parsing probably never worked. + - Issue #27275: Fixed implementation of pop() and popitem() methods in subclasses of accelerated OrderedDict.