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