1616from e2b .api .metadata import default_headers
1717from e2b .exceptions import AuthenticationException
1818from e2b .volume .client .client import AuthenticatedClient as AsyncVolumeApiClient
19- from e2b .volume .connection_config import FILE_TIMEOUT , VolumeConnectionConfig
19+ from e2b .volume .connection_config import READ_TIMEOUT , VolumeConnectionConfig
2020
2121
22- def get_api_client (config : VolumeConnectionConfig , ** kwargs ) -> AsyncVolumeApiClient :
22+ def get_api_client (
23+ config : VolumeConnectionConfig , * , for_streaming : bool = False , ** kwargs
24+ ) -> AsyncVolumeApiClient :
2325 if config .access_token is None :
2426 raise AuthenticationException (
2527 "Volume token is required for volume content operations. "
@@ -46,33 +48,41 @@ def get_api_client(config: VolumeConnectionConfig, **kwargs) -> AsyncVolumeApiCl
4648 httpx_args = {
4749 # The proxy lives in the cached transport; passing `proxy` here too
4850 # would mount a fresh, never-closed proxy transport per client.
49- "transport" : get_transport (config ),
51+ "transport" : get_transport (config , for_streaming = for_streaming ),
5052 "event_hooks" : make_async_logging_event_hooks (config .logger ),
5153 },
5254 ** kwargs ,
5355 )
5456
5557
5658_transport_lock = threading .Lock ()
57- # One transport (= one connection pool) per proxy; None is the direct pool.
58- # pyqwest's I/O runs on its own Rust runtime, so unlike the httpx transport
59- # this replaced, the transport is not bound to an event loop and the cache
60- # is process-global rather than per-loop.
61- _transports : Dict [Optional [ProxyConfig ], AsyncApiPyqwestTransport ] = {}
59+ # One transport (= one connection pool) per ( proxy, streaming) pair ; None is
60+ # the direct pool. pyqwest's I/O runs on its own Rust runtime, so unlike the
61+ # httpx transport this replaced, the transport is not bound to an event loop
62+ # and the cache is process-global rather than per-loop.
63+ _transports : Dict [tuple [ Optional [ProxyConfig ], bool ], AsyncApiPyqwestTransport ] = {}
6264
6365
64- def get_transport (config : VolumeConnectionConfig ) -> AsyncApiPyqwestTransport :
65- """The shared pyqwest-backed httpx transport for volume content API calls.
66+ def get_transport (
67+ config : VolumeConnectionConfig , * , for_streaming : bool = False
68+ ) -> AsyncApiPyqwestTransport :
69+ """The shared pyqwest-backed httpx transports for volume content API calls.
6670
67- ``read_timeout`` is the idle bound on every read: it resets after each
68- successful read, so it caps how long a streamed download may stall
69- without limiting total transfer time. It is fixed per transport — the
70- adapter's per-request timeouts are whole-request deadlines, and the sync
71- adapter does not bound body reads at all.
71+ The streaming transport carries ``read_timeout``, the idle bound on every
72+ read: it resets after each successful read, so it caps how long a
73+ streamed download may stall without limiting total transfer time. It is
74+ fixed per transport — the adapter's per-request timeouts are
75+ whole-request deadlines, and the sync adapter does not bound body reads
76+ at all. Only streamed downloads use it: reqwest's read timer keeps
77+ running while a request body is sent and while waiting for the response
78+ head, so on the regular transport it would cut off uploads and slow
79+ unary responses longer than the idle bound (those stay bounded by their
80+ whole-request deadlines instead).
7281 """
7382 proxy = proxy_to_config (config .proxy )
83+ key = (proxy , for_streaming )
7484 with _transport_lock :
75- transport = _transports .get (proxy )
85+ transport = _transports .get (key )
7686 if transport is None :
7787 transport = AsyncApiPyqwestTransport (
7888 ConnectionRetryTransport (
@@ -81,10 +91,10 @@ def get_transport(config: VolumeConnectionConfig) -> AsyncApiPyqwestTransport:
8191 proxy = proxy .to_pyqwest () if proxy is not None else None ,
8292 pool_idle_timeout = pool_idle_timeout ,
8393 pool_max_idle_per_host = pool_max_idle_per_host ,
84- read_timeout = FILE_TIMEOUT ,
94+ read_timeout = READ_TIMEOUT if for_streaming else None ,
8595 ),
8696 max_retries = connection_retries ,
8797 )
8898 )
89- _transports [proxy ] = transport
99+ _transports [key ] = transport
90100 return transport
0 commit comments