From 9a32e1c48aa951f1f7f1f96ba1acb2e4d73d57c0 Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Fri, 2 Oct 2026 11:27:34 +0530 Subject: [PATCH] fix: preserve TLS verification context across retries --- chargebee/http_request.py | 2 ++ tests/test_http_request.py | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/chargebee/http_request.py b/chargebee/http_request.py index 37fdd1a..0e5a080 100644 --- a/chargebee/http_request.py +++ b/chargebee/http_request.py @@ -250,6 +250,7 @@ def _handle_response(request_args: dict, response: httpx.Response): def _make_request(request_args): """Make a synchronous HTTP request using httpx""" + request_args = request_args.copy() verify = request_args.pop("verify", True) with httpx.Client(verify=verify) as client: response = client.request(**request_args) @@ -258,6 +259,7 @@ def _make_request(request_args): async def _make_request_async(request_args): """Make an asynchronous HTTP request using httpx""" + request_args = request_args.copy() verify = request_args.pop("verify", True) async with httpx.AsyncClient(verify=verify) as client: response = await client.request(**request_args) diff --git a/tests/test_http_request.py b/tests/test_http_request.py index d0b131d..fe8874a 100644 --- a/tests/test_http_request.py +++ b/tests/test_http_request.py @@ -50,6 +50,46 @@ def make_mock_client(status_code=200, text="", headers={}, is_async=False): class RequestTests(unittest.TestCase): + def test_retry_preserves_tls_context(self): + from chargebee.http_request import request + + for asynchronous in (False, True): + with self.subTest(asynchronous=asynchronous): + client, response = make_mock_client( + text='{"message": "ok"}', is_async=asynchronous + ) + _, failure = make_mock_client( + 503, '{"api_error_code": "temporary_error"}' + ) + client.request.side_effect = [failure, response] + client_type = "httpx.AsyncClient" if asynchronous else "httpx.Client" + sleep_name = "sleep_async" if asynchronous else "sleep" + context = Mock() + with ( + patch(client_type) as factory, + patch( + "chargebee.http_request.ssl.create_default_context", + return_value=context, + ), + patch("chargebee.http_request." + sleep_name), + ): + if asynchronous: + factory.return_value.__aenter__.return_value = client + result = asyncio.run( + request( + "GET", "/test", MockEnvironment(), use_async_client=True + ) + ) + else: + factory.return_value.__enter__.return_value = client + result = request("GET", "/test", MockEnvironment()) + self.assertEqual(result[2], 200) + self.assertEqual(factory.call_count, 2) + for call in factory.call_args_list: + self.assertIs(call.kwargs["verify"], context) + for call in client.request.call_args_list: + self.assertNotIn("verify", call.kwargs) + @patch("httpx.Client") def test_successful_request(self, mock_client_class): mock_client, mock_response = make_mock_client(