# HG changeset patch # Parent d0302d8ecbc1234d577ae2b3a2f75520e29898bb Issue 10721: Remove support for missing HTTP version in http.server This also removes broken support for HTTP 0.9. Based on patch by Antoine Pitrou. diff -r d0302d8ecbc1 Doc/whatsnew/3.6.rst --- a/Doc/whatsnew/3.6.rst Wed Aug 10 01:05:56 2016 -0500 +++ b/Doc/whatsnew/3.6.rst Wed Aug 10 07:00:47 2016 +0000 @@ -678,6 +678,10 @@ Use :class:`io.TextIOWrapper` for reading compressed text files in :term:`universal newlines` mode. +* The :class:`http.server.BaseHTTPRequestHandler` class no longer + accepts requests that are missing the HTTP version field, and HTTP 0.9 + requests are not supported. + Porting to Python 3.6 ===================== diff -r d0302d8ecbc1 Lib/http/server.py --- a/Lib/http/server.py Wed Aug 10 01:05:56 2016 -0500 +++ b/Lib/http/server.py Wed Aug 10 07:00:47 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 d0302d8ecbc1 Lib/test/test_httpservers.py --- a/Lib/test/test_httpservers.py Wed Aug 10 01:05:56 2016 -0500 +++ b/Lib/test/test_httpservers.py Wed Aug 10 07:00:47 2016 +0000 @@ -158,7 +158,7 @@ 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 @@ -814,12 +814,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 d0302d8ecbc1 Misc/NEWS --- a/Misc/NEWS Wed Aug 10 01:05:56 2016 -0500 +++ b/Misc/NEWS Wed Aug 10 07:00:47 2016 +0000 @@ -43,6 +43,10 @@ Library ------- +- Issue #10721: In the "http.server" module, reject requests that are missing + the HTTP version field. Also, remove attempted support for HTTP 0.9 + requests, which probably never worked. + - Issue #27664: Add to concurrent.futures.thread.ThreadPoolExecutor() the ability to specify a thread name prefix.