send_error: skip the default error page for 204 No Content (issue #3360)#3663
Open
HrachShah wants to merge 2 commits into
Open
send_error: skip the default error page for 204 No Content (issue #3360)#3663HrachShah wants to merge 2 commits into
HrachShah wants to merge 2 commits into
Conversation
added 2 commits
July 1, 2026 09:38
… (issue tornadoweb#3614) When TCPClient.connect is called with both ssl_options and a timeout, a TLS handshake timeout leaks the underlying socket. IOStream.start_tls transfers socket ownership to a new SSLIOStream *before* the handshake completes and sets self.socket to None on the original stream. The gen.with_timeout call around start_tls only raises TimeoutError to the caller; the original stream is now a no-op (its socket is gone) and the new SSLIOStream that actually owns the socket is reachable only through the inner future -- which gen.with_timeout deliberately leaves running, per its docstring. Capture the future returned by start_tls so that, on timeout, we register a done-callback that closes the resulting SSLIOStream. If the handshake eventually fails, SSLIOStream already closes its socket on the failure path, so the callback only acts when the future resolved successfully. Regression test in tcpclient_test.py replaces start_tls with a stub that builds a real SSLIOStream around the underlying socket but never completes the handshake, instruments close() to record the stream, and asserts that the SSLIOStream is closed when the timeout fires and the future is subsequently resolved. All 28 existing tcpclient tests still pass.
…nadoweb#3360) send_error unconditionally called write_error(), which on the default RequestHandler writes a small HTML page like "<html><title>204: No Content</title>...". That buffer then propagated into the `_status_code in (204, 304) or (100 <= _status_code < 200)` assertion in finish(), raising "Cannot send body with 204" -- and worse, when run under `python -O` (which strips assertions) the client received a 204 response with a body and no Content-Length header, in violation of RFC 9110. Skip write_error for 204 (and keep the existing 304 skip) so the default branch produces a header-only 204 response.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Closes #3360:
send_errorunconditionally calledwrite_error(), which in the defaultRequestHandlerwrites a small HTML body like"<html><title>204: No Content</title>...". That body then propagated into the_status_code in (204, 304) or (100 <= _status_code < 200)assertion infinish(), raisingAssertionError: Cannot send body with 204.This was doubly bad because under
python -O(which strips assertions) the client received a 204 response with a body and noContent-Lengthheader, in violation of RFC 9110.The fix
Skip
write_errorfor 204 (keeping the existing 304 skip) so the defaultsend_errorpath for 204 produces a header-only response.