From e33d59207033eea1dd77825dc40accd1d002c542 Mon Sep 17 00:00:00 2001 From: pawciobiel Date: Fri, 16 Feb 2024 14:35:36 +0100 Subject: [PATCH] web: do not send content on HTTPError(204) Change `write_error()` to not send body if status code in (204, 304) or (100 <= status code < 200) - refactored into `RequestHandler._should_not_send_content(status_code)` Fixes #3360 --- tornado/test/web_test.py | 12 ++++++++++++ tornado/web.py | 15 ++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/tornado/test/web_test.py b/tornado/test/web_test.py index 351a2a8971..4173dd6d65 100644 --- a/tornado/test/web_test.py +++ b/tornado/test/web_test.py @@ -1691,6 +1691,18 @@ def test_204_headers(self): self.assertNotIn("Transfer-Encoding", response.headers) +class Header204viaHTTPErrorTest(SimpleHandlerTestCase): + class Handler(RequestHandler): + def get(self): + raise HTTPError(204) + + def test_204_headers_via_httperror(self): + response = self.fetch("/") + self.assertEqual(response.code, 204) + self.assertNotIn("Content-Length", response.headers) + self.assertNotIn("Transfer-Encoding", response.headers) + + class Header304Test(SimpleHandlerTestCase): class Handler(RequestHandler): def get(self): diff --git a/tornado/web.py b/tornado/web.py index 30cef30bd6..93ee6df85a 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -1222,6 +1222,10 @@ def flush(self, include_footers: bool = False) -> "Future[None]": future.set_result(None) return future + def _should_not_send_content(self, status_code: int) -> bool: + """Check if we should not send body content for given `status_code`""" + return status_code in (204, 304) or (100 <= status_code < 200) + def finish(self, chunk: Optional[Union[str, bytes, dict]] = None) -> "Future[None]": """Finishes this response, ending the HTTP request. @@ -1255,10 +1259,9 @@ def finish(self, chunk: Optional[Union[str, bytes, dict]] = None) -> "Future[Non if self.check_etag_header(): self._write_buffer = [] self.set_status(304) - if self._status_code in (204, 304) or (100 <= self._status_code < 200): - assert not self._write_buffer, ( - "Cannot send body with %s" % self._status_code - ) + if self._should_not_send_content(self._status_code): + if self._write_buffer: + raise RuntimeError(f"Cannot send body with status code HTTP{self._status_code}") self._clear_representation_headers() elif "Content-Length" not in self._headers: content_length = sum(len(part) for part in self._write_buffer) @@ -1349,7 +1352,9 @@ def write_error(self, status_code: int, **kwargs: Any) -> None: the "current" exception for purposes of methods like ``sys.exc_info()`` or ``traceback.format_exc``. """ - if self.settings.get("serve_traceback") and "exc_info" in kwargs: + if self._should_not_send_content(status_code): + self.finish() + elif self.settings.get("serve_traceback") and "exc_info" in kwargs: # in debug mode, try to send a traceback self.set_header("Content-Type", "text/plain") for line in traceback.format_exception(*kwargs["exc_info"]):