From d527174a2b6a0b98d16990ffc6c0dbb073fa52ca Mon Sep 17 00:00:00 2001 From: disqualifier Date: Mon, 29 Jun 2026 01:10:25 -0400 Subject: [PATCH] fix: make session-default headers fully mutable; preview matches request in all cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit headers passed at construction were baked into aiohttp's ClientSession(headers=) (an immutable per-session map) AND merged in request(), a double path that made clear_headers()/update_headers() unable to remove or change what reached the wire. dropped headers= from the aiohttp session so _default_headers is our sole, mutable layer that request() and preview() both merge (defaults -> per-request -> overwrites). preview() now merges identically to request() even when explicit per-request headers are passed (it previously dropped session defaults in that case), so preview == wire in every case. clear_headers clears our defaults (not zero headers — per-request + overwrites still flow). Signed-off-by: disqualifier --- src/aioweb/session.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/aioweb/session.py b/src/aioweb/session.py index a0dc4b3..91ea3b8 100644 --- a/src/aioweb/session.py +++ b/src/aioweb/session.py @@ -95,9 +95,15 @@ class ExtendedSession: proxy/retry/preview logic in this class never touches the session object directly (only _raw_request, the cookie methods, and close do), so those features work unchanged on any backend. + + `headers` is the session-default header set; the default aiohttp backend + does NOT bake it into the ClientSession (which would copy it into an + immutable per-session map that update_headers/clear_headers can't touch). + instead `_default_headers` is our own mutable layer that request() and + preview() merge per call, so the mutable session-header API actually works. + a backend that needs the defaults baked at construction may use `headers`. """ return aiohttp.ClientSession( - headers=headers, timeout=aiohttp.ClientTimeout( total=timeout, connect=timeout / 2, @@ -233,10 +239,8 @@ class ExtendedSession: def preview(self, method, url, **kwargs): """build a RequestPreview for a request without sending it""" proxy = self._get_proxy(url, kwargs.pop("proxies", None)) - if kwargs.get("headers"): - headers = self._apply_overwrites(kwargs.pop("headers")) - else: - headers = dict(self.get_headers()) + merged = {**self._default_headers, **(kwargs.pop("headers", None) or {})} + headers = self._apply_overwrites(merged) timeout = kwargs.get("timeout") timeout_total = timeout if isinstance(timeout, (int, float)) else None