From 3e9bbae96557baad773f315dd1385b800f93bc03 Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Tue, 21 Apr 2026 10:00:22 +0200 Subject: [PATCH] fix: Prevent `_prepare_request_call` from mutating caller's headers dict Copy the caller-provided `headers` up-front so `Content-Type`/`Content-Encoding` additions do not leak back into a shared dict reused across HTTP calls. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/apify_client/_http_clients/_base.py | 3 +-- tests/unit/test_http_clients.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/apify_client/_http_clients/_base.py b/src/apify_client/_http_clients/_base.py index 3ecea4ce..507ae432 100644 --- a/src/apify_client/_http_clients/_base.py +++ b/src/apify_client/_http_clients/_base.py @@ -204,8 +204,7 @@ def _prepare_request_call( if json is not None and data is not None: raise ValueError('Cannot pass both "json" and "data" parameters at the same time!') - if not headers: - headers = {} + headers = dict(headers) if headers else {} # Dump JSON data to string so it can be gzipped. if json is not None: diff --git a/tests/unit/test_http_clients.py b/tests/unit/test_http_clients.py index 7a7b4641..93e942bd 100644 --- a/tests/unit/test_http_clients.py +++ b/tests/unit/test_http_clients.py @@ -380,6 +380,24 @@ def test_prepare_request_call_with_params() -> None: assert params == {'limit': 10, 'flag': 'true'} +def test_prepare_request_call_does_not_mutate_caller_headers() -> None: + """Test _prepare_request_call does not mutate the caller's headers dict. + + A caller that reuses a shared headers dict across calls must not see stale + `Content-Type`/`Content-Encoding` headers leak in from a prior JSON/body call. + """ + client = _ConcreteHttpClient() + + caller_headers = {'x-trace-id': 'abc-123'} + original = dict(caller_headers) + + client._prepare_request_call(headers=caller_headers, json={'x': 1}) + assert caller_headers == original + + client._prepare_request_call(headers=caller_headers, data='payload') + assert caller_headers == original + + def test_build_url_with_params_none() -> None: """Test _build_url_with_params with None params.""" client = _ConcreteHttpClient()