3232)
3333
3434
35+ _MAX_RESPONSE_BODY_BYTES_INVALID = "max_response_body_bytes must be >= 1"
36+
37+
38+ def _validate_max_response_body_bytes (cap : int | None ) -> None :
39+ """Reject a non-None cap below 1. None means unbounded (the default)."""
40+ if cap is not None and cap < 1 :
41+ raise ValueError (_MAX_RESPONSE_BODY_BYTES_INVALID )
42+
43+
3544def _parse_content_length (raw : str | None ) -> int | None :
3645 """Return a non-negative int Content-Length, or None for missing/garbage. Never raises."""
3746 if raw is None :
@@ -179,7 +188,7 @@ class AsyncClient:
179188 _decoders : tuple [ResponseDecoder , ...]
180189 _user_middleware : tuple [AsyncMiddleware , ...]
181190 _dispatch : AsyncNext
182- _max_error_body_bytes : int | None
191+ _max_response_body_bytes : int | None
183192
184193 def __init__ ( # noqa: PLR0913 — wide constructor is the cost of a single-call API
185194 self ,
@@ -194,8 +203,9 @@ def __init__( # noqa: PLR0913 — wide constructor is the cost of a single-call
194203 httpx2_client : httpx2 .AsyncClient | None = None ,
195204 decoders : Sequence [ResponseDecoder ] | None = None ,
196205 middleware : Sequence [AsyncMiddleware ] = (),
197- max_error_body_bytes : int | None = None ,
206+ max_response_body_bytes : int | None = None ,
198207 ) -> None :
208+ _validate_max_response_body_bytes (max_response_body_bytes )
199209 if httpx2_client is not None :
200210 forwarded = {
201211 "base_url" : base_url ,
@@ -233,12 +243,20 @@ def __init__( # noqa: PLR0913 — wide constructor is the cost of a single-call
233243 self ._decoder_resolver = _DecoderResolver (self ._decoders )
234244 self ._user_middleware = tuple (middleware )
235245 self ._dispatch = compose_async (self ._user_middleware , self ._terminal )
236- self ._max_error_body_bytes = max_error_body_bytes
246+ self ._max_response_body_bytes = max_response_body_bytes
237247
238248 async def _terminal (self , request : httpx2 .Request ) -> httpx2 .Response :
249+ cap = self ._max_response_body_bytes
239250 try :
240251 async with _httpx2_exception_mapper ():
241- response = await self ._httpx2_client .send (request )
252+ if cap is None :
253+ response = await self ._httpx2_client .send (request )
254+ else :
255+ streaming = await self ._httpx2_client .send (request , stream = True )
256+ try :
257+ response = await _read_capped_async (streaming , cap , request )
258+ finally :
259+ await streaming .aclose ()
242260 except RuntimeError as exc :
243261 if self ._httpx2_client .is_closed :
244262 raise TransportError (str (exc )) from exc
@@ -1100,12 +1118,12 @@ async def stream( # noqa: PLR0913, C901 — mirrors httpx2 per-method signature
11001118
11011119 async with _httpx2_exception_mapper (), self ._httpx2_client .stream (method , url , ** kwargs ) as response :
11021120 if HTTPStatus .BAD_REQUEST <= response .status_code < 600 : # noqa: PLR2004 — 600 is the synthetic upper bound for 5xx
1103- if self ._max_error_body_bytes is not None :
1121+ if self ._max_response_body_bytes is not None :
11041122 content_length = _parse_content_length (response .headers .get ("content-length" ))
1105- if content_length is not None and content_length > self ._max_error_body_bytes :
1123+ if content_length is not None and content_length > self ._max_response_body_bytes :
11061124 raise ResponseTooLargeError (
11071125 status_code = response .status_code ,
1108- limit = self ._max_error_body_bytes ,
1126+ limit = self ._max_response_body_bytes ,
11091127 content_length = content_length ,
11101128 reason = "declared" ,
11111129 )
@@ -1146,7 +1164,7 @@ class Client:
11461164 _decoders : tuple [ResponseDecoder , ...]
11471165 _user_middleware : tuple [Middleware , ...]
11481166 _dispatch : Next
1149- _max_error_body_bytes : int | None
1167+ _max_response_body_bytes : int | None
11501168
11511169 def __init__ ( # noqa: PLR0913 — wide constructor is the cost of a single-call API
11521170 self ,
@@ -1161,8 +1179,9 @@ def __init__( # noqa: PLR0913 — wide constructor is the cost of a single-call
11611179 httpx2_client : httpx2 .Client | None = None ,
11621180 decoders : Sequence [ResponseDecoder ] | None = None ,
11631181 middleware : Sequence [Middleware ] = (),
1164- max_error_body_bytes : int | None = None ,
1182+ max_response_body_bytes : int | None = None ,
11651183 ) -> None :
1184+ _validate_max_response_body_bytes (max_response_body_bytes )
11661185 if httpx2_client is not None :
11671186 forwarded = {
11681187 "base_url" : base_url ,
@@ -1200,12 +1219,20 @@ def __init__( # noqa: PLR0913 — wide constructor is the cost of a single-call
12001219 self ._decoder_resolver = _DecoderResolver (self ._decoders )
12011220 self ._user_middleware = tuple (middleware )
12021221 self ._dispatch = compose (self ._user_middleware , self ._terminal )
1203- self ._max_error_body_bytes = max_error_body_bytes
1222+ self ._max_response_body_bytes = max_response_body_bytes
12041223
12051224 def _terminal (self , request : httpx2 .Request ) -> httpx2 .Response :
1225+ cap = self ._max_response_body_bytes
12061226 try :
12071227 with _httpx2_exception_mapper_sync ():
1208- response = self ._httpx2_client .send (request )
1228+ if cap is None :
1229+ response = self ._httpx2_client .send (request )
1230+ else :
1231+ streaming = self ._httpx2_client .send (request , stream = True )
1232+ try :
1233+ response = _read_capped (streaming , cap , request )
1234+ finally :
1235+ streaming .close ()
12091236 except RuntimeError as exc :
12101237 if self ._httpx2_client .is_closed :
12111238 raise TransportError (str (exc )) from exc
@@ -2089,12 +2116,12 @@ def stream( # noqa: PLR0913, C901 — mirrors httpx2 per-method signatures; kwa
20892116
20902117 with _httpx2_exception_mapper_sync (), self ._httpx2_client .stream (method , url , ** kwargs ) as response :
20912118 if HTTPStatus .BAD_REQUEST <= response .status_code < 600 : # noqa: PLR2004 — 600 is the synthetic upper bound for 5xx
2092- if self ._max_error_body_bytes is not None :
2119+ if self ._max_response_body_bytes is not None :
20932120 content_length = _parse_content_length (response .headers .get ("content-length" ))
2094- if content_length is not None and content_length > self ._max_error_body_bytes :
2121+ if content_length is not None and content_length > self ._max_response_body_bytes :
20952122 raise ResponseTooLargeError (
20962123 status_code = response .status_code ,
2097- limit = self ._max_error_body_bytes ,
2124+ limit = self ._max_response_body_bytes ,
20982125 content_length = content_length ,
20992126 reason = "declared" ,
21002127 )
0 commit comments