# HG changeset patch # Parent 831f73efe3a6c510557f402a20f9b1f773944f60 Issue #10721: Remove attempted support for HTTP 0.9 in http.server Based on patch by Antoine Pitrou. diff -r 831f73efe3a6 Doc/whatsnew/3.7.rst --- a/Doc/whatsnew/3.7.rst Wed Nov 23 12:23:16 2016 -0800 +++ b/Doc/whatsnew/3.7.rst Thu Nov 24 03:00:55 2016 +0000 @@ -114,6 +114,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 831f73efe3a6 Lib/http/server.py --- a/Lib/http/server.py Wed Nov 23 12:23:16 2016 -0800 +++ b/Lib/http/server.py Thu Nov 24 03:00:55 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). @@ -311,20 +302,12 @@ return False self.request_version = version - if not 2 <= len(words) <= 3: + if len(words) != 3: self.send_error( HTTPStatus.BAD_REQUEST, "Bad request syntax (%r)" % requestline) return False - command, path = words[:2] - if len(words) == 2: - self.close_connection = True - if command != 'GET': - self.send_error( - HTTPStatus.BAD_REQUEST, - "Bad HTTP/0.9 request type (%r)" % command) - return False - self.command, self.path = command, path + self.command, self.path = words[:2] # Examine the headers and look for a Connection directive. try: @@ -490,25 +473,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': @@ -518,9 +499,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 831f73efe3a6 Lib/test/test_httpservers.py --- a/Lib/test/test_httpservers.py Wed Nov 23 12:23:16 2016 -0800 +++ b/Lib/test/test_httpservers.py Thu Nov 24 03:00:55 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_extra_space(self): result = self.send_typical_request( b'GET /spaced out HTTP/1.1\r\n' diff -r 831f73efe3a6 Misc/NEWS --- a/Misc/NEWS Wed Nov 23 12:23:16 2016 -0800 +++ b/Misc/NEWS Thu Nov 24 03:00:55 2016 +0000 @@ -145,6 +145,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 #28752: Restored the __reduce__() methods of datetime objects. - Issue #28727: Regular expression patterns, _sre.SRE_Pattern objects created