fix(http): don't let a non-iterable header buffer crash end_headers

advertise_connection_close() guarded its two attribute reads but not the
dedup scan over _headers_buffer. A stub whose buffer is truthy and
non-iterable (a bare Mock) raised TypeError from inside end_headers(),
which _handle_write's generic except would swallow into a spurious 500 --
the same failure class as the missing close_connection guard.
This commit is contained in:
Rodrigo Gomes da Silva authored and n committed 2026-09-22 12:06:09 +00:00
1 parent a93e8ba890
commit 304815228a
2 files changed
+27 -2

No files matched your search

+7 -2
View File
@@ -236,11 +236,16 @@ def advertise_connection_close(handler) -> None:
Both attribute reads are defensive: partially-built handler stubs in the
test suite carry neither ``close_connection`` nor ``_headers_buffer``, and
header emission must not depend on their presence.
header emission must not depend on their presence. The buffer is also
type-checked before it is scanned -- a stub whose ``_headers_buffer`` is a
truthy non-iterable (a bare ``Mock``) would otherwise raise ``TypeError``
from inside ``end_headers()``, which ``_handle_write`` would swallow into a
spurious 500.
"""
if not getattr(handler, 'close_connection', False):
return
for raw in getattr(handler, '_headers_buffer', None) or ():
buffered = getattr(handler, '_headers_buffer', None)
for raw in buffered if isinstance(buffered, (list, tuple)) else ():
if raw[:11].lower() == b'connection:':
return
handler.send_header('Connection', 'close')
@@ -228,6 +228,26 @@ def test_end_headers_on_handler_stub_without_close_connection(monkeypatch):
assert "Connection" not in dict(sent), "nothing was armed, so nothing to advertise"
def test_advertise_close_tolerates_a_non_iterable_header_buffer():
"""The dedup scan must be as defensive as the two attribute reads.
A bare ``Mock`` answers every attribute, so ``_headers_buffer`` comes back
truthy but non-iterable. Iterating it unguarded raised
``TypeError: 'Mock' object is not iterable`` from inside ``end_headers()``,
which ``_handle_write``'s generic ``except`` turns into a spurious 500 —
the same crash-inside-end_headers class as the missing
``close_connection`` guard above.
"""
from unittest.mock import Mock
from api.helpers import advertise_connection_close
handler = Mock()
advertise_connection_close(handler) # must not raise
handler.send_header.assert_called_once_with("Connection", "close")
def test_check_auth_or_close_on_handler_stub_without_close_connection(monkeypatch):
"""check_auth_or_close must not invent close_connection on a stub.