From ec1a20a3f6778d0df640762d8610111d21278eb0 Mon Sep 17 00:00:00 2001 From: disqualifier Date: Mon, 6 Jul 2026 19:28:40 -0400 Subject: [PATCH] fix: CurlCffi preserves duplicate Set-Cookie and get_cookies survives cross-domain names CIMultiDict(response.headers) consumed curl_cffi Headers.items(), which comma-joins duplicate header keys, collapsing multiple Set-Cookie lines into one corrupted value - now uses multi_items() so duplicates stay separate. get_cookies used dict(cookies.items()), which raises curl_cffi CookieConflict when the same name exists on two domains - now uses get_dict() (flattens, no raise). Noble.get_cookies prefers get_dict() where the jar exposes it (unverified live-gap: the noble extra isn't installed here). Signed-off-by: disqualifier --- src/aioweb_tls/backends.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/aioweb_tls/backends.py b/src/aioweb_tls/backends.py index 24fac08..e4a9c27 100644 --- a/src/aioweb_tls/backends.py +++ b/src/aioweb_tls/backends.py @@ -179,7 +179,7 @@ class CurlCffi: content = response.content if response.content is not None else b"" return Response( status_code=response.status_code, - headers=CIMultiDict(response.headers), + headers=CIMultiDict(response.headers.multi_items()), content=content, url=str(response.url), reason=getattr(response, "reason", None), @@ -208,8 +208,13 @@ class CurlCffi: session.cookies.set(name, value, domain=domain or "", path=path) def get_cookies(self, session) -> dict: - """all cookies stored in curl_cffi's cookie store""" - return dict(session.cookies.items()) + """all cookies stored in curl_cffi's cookie store + + uses get_dict() rather than dict(cookies.items()): items() raises curl_cffi + CookieConflict when the same name exists on two domains, get_dict() flattens + (last value wins) without raising. + """ + return session.cookies.get_dict() def clear_cookies(self, session) -> None: """clear curl_cffi's cookie store""" @@ -339,8 +344,15 @@ class Noble: session.cookies.set(name, value, domain=domain or "", path=path) def get_cookies(self, session) -> dict: - """all cookies stored in noble_tls's cookie jar""" - return dict(session.cookies.items()) + """all cookies stored in noble_tls's cookie jar + + prefers get_dict() when the jar exposes it (flattens cross-domain duplicate + names without raising, like curl_cffi); falls back to items() otherwise + """ + jar = session.cookies + if hasattr(jar, "get_dict"): + return jar.get_dict() + return dict(jar.items()) def clear_cookies(self, session) -> None: """clear noble_tls's cookie jar"""