From: Ben Darnell Date: Sun, 11 Jan 2015 18:50:58 +0000 (-0500) Subject: Correct handling of HTTP version numbers. X-Git-Tag: v4.1.0b1~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=889e1f378eed48d203ac603f0c144ecea7beee80;p=thirdparty%2Ftornado.git Correct handling of HTTP version numbers. Per RFC 7230 section 2.6, we always output HTTP/1.1 even if the request was HTTP/1.0 (and we accept any 1.x version). --- diff --git a/tornado/http1connection.py b/tornado/http1connection.py index 1ff9b8335..32bf83f2d 100644 --- a/tornado/http1connection.py +++ b/tornado/http1connection.py @@ -326,8 +326,10 @@ class HTTP1Connection(httputil.HTTPConnection): def write_headers(self, start_line, headers, chunk=None, callback=None): """Implements `.HTTPConnection.write_headers`.""" + lines = [] if self.is_client: self._request_start_line = start_line + lines.append(utf8('%s %s HTTP/1.1' % (start_line[0], start_line[1]))) # Client requests with a non-empty body must have either a # Content-Length or a Transfer-Encoding. self._chunking_output = ( @@ -336,6 +338,7 @@ class HTTP1Connection(httputil.HTTPConnection): 'Transfer-Encoding' not in headers) else: self._response_start_line = start_line + lines.append(utf8('HTTP/1.1 %s %s' % (start_line[1], start_line[2]))) self._chunking_output = ( # TODO: should this use # self._request_start_line.version or @@ -365,7 +368,6 @@ class HTTP1Connection(httputil.HTTPConnection): self._expected_content_remaining = int(headers['Content-Length']) else: self._expected_content_remaining = None - lines = [utf8("%s %s %s" % start_line)] lines.extend([utf8(n) + b": " + utf8(v) for n, v in headers.get_all()]) for line in lines: if b'\n' in line: diff --git a/tornado/httputil.py b/tornado/httputil.py index dfee4f650..0f798ca95 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -548,6 +548,8 @@ class HTTPConnection(object): headers. :arg callback: a callback to be run when the write is complete. + The ``version`` field of ``start_line`` is ignored. + Returns a `.Future` if no callback is given. """ raise NotImplementedError() @@ -787,7 +789,7 @@ def parse_request_start_line(line): method, path, version = line.split(" ") except ValueError: raise HTTPInputError("Malformed HTTP request line") - if not version.startswith("HTTP/"): + if not re.match(r"^HTTP/1\.[0-9]$", version): raise HTTPInputError( "Malformed HTTP version in HTTP Request-Line: %r" % version) return RequestStartLine(method, path, version) @@ -806,7 +808,7 @@ def parse_response_start_line(line): ResponseStartLine(version='HTTP/1.1', code=200, reason='OK') """ line = native_str(line) - match = re.match("(HTTP/1.[01]) ([0-9]+) ([^\r]*)", line) + match = re.match("(HTTP/1.[0-9]) ([0-9]+) ([^\r]*)", line) if not match: raise HTTPInputError("Error parsing response start line") return ResponseStartLine(match.group(1), int(match.group(2)), diff --git a/tornado/simple_httpclient.py b/tornado/simple_httpclient.py index 7c915e90a..31d076e2d 100644 --- a/tornado/simple_httpclient.py +++ b/tornado/simple_httpclient.py @@ -345,7 +345,7 @@ class _HTTPConnection(httputil.HTTPMessageDelegate): decompress=self.request.decompress_response), self._sockaddr) start_line = httputil.RequestStartLine(self.request.method, - req_path, 'HTTP/1.1') + req_path, '') self.connection.write_headers(start_line, self.request.headers) if self.request.expect_100_continue: self._read_response() diff --git a/tornado/test/httpserver_test.py b/tornado/test/httpserver_test.py index 49a099659..36365e0d7 100644 --- a/tornado/test/httpserver_test.py +++ b/tornado/test/httpserver_test.py @@ -562,7 +562,7 @@ class UnixSocketTest(AsyncTestCase): self.stream.write(b"GET /hello HTTP/1.0\r\n\r\n") self.stream.read_until(b"\r\n", self.stop) response = self.wait() - self.assertEqual(response, b"HTTP/1.0 200 OK\r\n") + self.assertEqual(response, b"HTTP/1.1 200 OK\r\n") self.stream.read_until(b"\r\n\r\n", self.stop) headers = HTTPHeaders.parse(self.wait().decode('latin1')) self.stream.read_bytes(int(headers["Content-Length"]), self.stop) @@ -636,7 +636,7 @@ class KeepAliveTest(AsyncHTTPTestCase): def read_headers(self): self.stream.read_until(b'\r\n', self.stop) first_line = self.wait() - self.assertTrue(first_line.startswith(self.http_version + b' 200'), first_line) + self.assertTrue(first_line.startswith(b'HTTP/1.1 200'), first_line) self.stream.read_until(b'\r\n\r\n', self.stop) header_bytes = self.wait() headers = HTTPHeaders.parse(header_bytes.decode('latin1')) diff --git a/tornado/test/iostream_test.py b/tornado/test/iostream_test.py index b0e94fb20..c622bb40f 100644 --- a/tornado/test/iostream_test.py +++ b/tornado/test/iostream_test.py @@ -57,7 +57,7 @@ class TestIOStreamWebMixin(object): stream.read_until_close(self.stop) data = self.wait() - self.assertTrue(data.startswith(b"HTTP/1.0 200")) + self.assertTrue(data.startswith(b"HTTP/1.1 200")) self.assertTrue(data.endswith(b"Hello")) def test_read_zero_bytes(self): @@ -70,7 +70,7 @@ class TestIOStreamWebMixin(object): # normal read self.stream.read_bytes(9, self.stop) data = self.wait() - self.assertEqual(data, b"HTTP/1.0 ") + self.assertEqual(data, b"HTTP/1.1 ") # zero bytes self.stream.read_bytes(0, self.stop) @@ -125,7 +125,7 @@ class TestIOStreamWebMixin(object): self.assertIs(connect_result, stream) yield stream.write(b"GET / HTTP/1.0\r\n\r\n") first_line = yield stream.read_until(b"\r\n") - self.assertEqual(first_line, b"HTTP/1.0 200 OK\r\n") + self.assertEqual(first_line, b"HTTP/1.1 200 OK\r\n") # callback=None is equivalent to no callback. header_data = yield stream.read_until(b"\r\n\r\n", callback=None) headers = HTTPHeaders.parse(header_data.decode('latin1')) diff --git a/tornado/web.py b/tornado/web.py index 2d1dac0fd..038d424e8 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -840,7 +840,7 @@ class RequestHandler(object): for cookie in self._new_cookie.values(): self.add_header("Set-Cookie", cookie.OutputString(None)) - start_line = httputil.ResponseStartLine(self.request.version, + start_line = httputil.ResponseStartLine('', self._status_code, self._reason) return self.request.connection.write_headers(