Skip to content
This repository was archived by the owner on May 25, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions py_clob_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
delete,
get,
post,
set_proxy,
drop_notifications_query_params,
add_balance_allowance_params_to_url,
add_order_scoring_params_to_url,
Expand Down Expand Up @@ -124,6 +125,7 @@ def __init__(
funder: str = None,
builder_config: BuilderConfig = None,
tick_size_ttl: float = 300.0,
proxy: Optional[str] = None,
):
"""
Initializes the clob client
Expand Down Expand Up @@ -159,6 +161,9 @@ def __init__(
self.__neg_risk = {}
self.__fee_rates = {}

# proxy
set_proxy(proxy)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unconditional set_proxy call destroys shared HTTP client

High Severity

set_proxy(proxy) is called unconditionally in __init__, even when proxy is None (the default). Since set_proxy always closes the existing module-level _http_client and creates a new one, every ClobClient instantiation — even without a proxy — destroys the shared HTTP client, dropping all keep-alive connections and HTTP/2 state. This is a regression for all existing users and can break in-flight requests if multiple ClobClient instances coexist.

Additional Locations (1)
Fix in Cursor Fix in Web


# RFQ client
self.rfq = RfqClient(self)

Expand Down
16 changes: 16 additions & 0 deletions py_clob_client/http_helpers/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@
_http_client = httpx.Client(http2=True)


def set_proxy(proxy: str = None):
"""
Reconfigures the shared HTTP client to use the given proxy.
Pass None or empty string to disable proxy.
"""
global _http_client

old_client = _http_client
kwargs = {"http2": True}
if proxy:
kwargs["proxy"] = proxy

_http_client = httpx.Client(**kwargs)
Comment thread
cursor[bot] marked this conversation as resolved.
old_client.close()


def overloadHeaders(method: str, headers: dict) -> dict:
if headers is None:
headers = dict()
Expand Down
39 changes: 39 additions & 0 deletions tests/http_helpers/test_proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from unittest import TestCase
from unittest.mock import patch, MagicMock

from py_clob_client.http_helpers.helpers import set_proxy


class TestProxy(TestCase):
@patch("py_clob_client.http_helpers.helpers.httpx.Client")
def test_set_proxy_creates_client_with_proxy(self, mock_client_cls):
"""set_proxy should create a new httpx.Client with the proxy param."""
old_client = MagicMock()
mock_client_cls.side_effect = [old_client, MagicMock()]

set_proxy("http://localhost:8080")

mock_client_cls.assert_called_with(http2=True, proxy="http://localhost:8080")
old_client.close.assert_called_once()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests assert on wrong mock object for close

Medium Severity

The module-level _http_client is a real httpx.Client created at import time, before the mock is applied. When set_proxy runs, the mocked httpx.Client() call returns side_effect[0] (the test's old_client MagicMock) as the new _http_client. Then .close() is called on the real original client, not the MagicMock. The assertion old_client.close.assert_called_once() checks the wrong object, so these tests would fail when run.

Additional Locations (2)
Fix in Cursor Fix in Web


@patch("py_clob_client.http_helpers.helpers.httpx.Client")
def test_set_proxy_none_creates_client_without_proxy(self, mock_client_cls):
"""set_proxy(None) should create a client without the proxy param."""
old_client = MagicMock()
mock_client_cls.side_effect = [old_client, MagicMock()]

set_proxy(None)

mock_client_cls.assert_called_with(http2=True)
old_client.close.assert_called_once()

@patch("py_clob_client.http_helpers.helpers.httpx.Client")
def test_set_proxy_empty_string_creates_client_without_proxy(self, mock_client_cls):
"""set_proxy('') should create a client without the proxy param."""
old_client = MagicMock()
mock_client_cls.side_effect = [old_client, MagicMock()]

set_proxy("")

mock_client_cls.assert_called_with(http2=True)
old_client.close.assert_called_once()