Skip to content

Commit

Permalink
web: do not send content on HTTPError(204)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
pawciobiel committed Jun 14, 2024
1 parent bdfc017 commit e33d592
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 5 deletions.
12 changes: 12 additions & 0 deletions tornado/test/web_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
15 changes: 10 additions & 5 deletions tornado/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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"]):
Expand Down

0 comments on commit e33d592

Please sign in to comment.