6868 WSMessageTypeError ,
6969 WSServerHandshakeError ,
7070)
71- from .client_middlewares import ClientMiddlewareType , build_client_middlewares
71+ from .client_middlewares import ClientMiddlewareType , _cached_build_client_middlewares
7272from .client_reqrep import (
7373 SSL_ALLOWED_TYPES ,
7474 ClientRequest ,
7575 ClientResponse ,
76+ ClientTimeout ,
7677 Fingerprint ,
7778 RequestInfo ,
79+ ResponseParams ,
7880)
7981from .client_ws import (
8082 DEFAULT_WS_CLIENT_TIMEOUT ,
9092 HTTP_AND_EMPTY_SCHEMA_SET ,
9193 TimeoutHandle ,
9294 _auth_header_from_netrc ,
93- frozen_dataclass_decorator ,
9495 get_env_proxy_for_url ,
9596 netrc_from_env ,
9697 sentinel ,
@@ -214,52 +215,6 @@ class _WSConnectOptions(TypedDict, total=False):
214215 max_msg_size : int
215216
216217
217- @frozen_dataclass_decorator
218- class ClientTimeout :
219- total : float | None = 5 * 60 # 5 minute default timeout
220- connect : float | None = None
221- sock_read : float | None = None
222- sock_connect : float | None = None
223- ceil_threshold : float = 5
224-
225- # pool_queue_timeout: Optional[float] = None
226- # dns_resolution_timeout: Optional[float] = None
227- # socket_connect_timeout: Optional[float] = None
228- # connection_acquiring_timeout: Optional[float] = None
229- # new_connection_timeout: Optional[float] = None
230- # http_header_timeout: Optional[float] = None
231- # response_body_timeout: Optional[float] = None
232-
233- # to create a timeout specific for a single request, either
234- # - create a completely new one to overwrite the default
235- # - or use https://docs.python.org/3/library/dataclasses.html#dataclasses.replace
236- # to overwrite the defaults
237-
238- def __post_init__ (self ) -> None :
239- # Ensure total is never lower than a more specific timeout, otherwise
240- # the latter would be silently capped by total and rendered useless.
241- # total=None means the user explicitly disabled the total timeout.
242- if self .total is None :
243- return
244- object .__setattr__ (
245- self ,
246- "total" ,
247- max (
248- self .total ,
249- self .connect or 0 ,
250- self .sock_read or 0 ,
251- self .sock_connect or 0 ,
252- ),
253- )
254-
255- if self .total == 0 :
256- raise ValueError (
257- "total timeout must be a positive number or None to disable, "
258- "got 0. Using 0 to disable timeouts is no longer supported, "
259- "use None instead."
260- )
261-
262-
263218# https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2
264219IDEMPOTENT_METHODS = frozenset ({"GET" , "HEAD" , "OPTIONS" , "TRACE" , "PUT" , "DELETE" })
265220
@@ -271,6 +226,31 @@ def __post_init__(self) -> None:
271226_CharsetResolver = Callable [[ClientResponse , bytes ], str ]
272227
273228
229+ # Module-level (not a closure) so it has a stable identity for the
230+ # ``_cached_build_client_middlewares`` cache key.
231+ async def _connect_and_send_request (req : ClientRequest ) -> ClientResponse :
232+ connector = req ._session ._connector
233+ assert connector is not None
234+ try :
235+ conn = await connector .connect (req , traces = req ._traces , timeout = req ._timeout )
236+ except asyncio .TimeoutError as exc :
237+ raise ConnectionTimeoutError (f"Connection timeout to host { req .url } " ) from exc
238+
239+ assert conn .protocol is not None
240+ conn .protocol .set_response_params (** req ._response_params )
241+ try :
242+ resp = await req ._send (conn )
243+ try :
244+ await resp .start (conn )
245+ except BaseException :
246+ resp .close ()
247+ raise
248+ except BaseException :
249+ conn .close ()
250+ raise
251+ return resp
252+
253+
274254@final
275255class ClientSession :
276256 """First-class interface for making HTTP requests."""
@@ -428,7 +408,7 @@ def __init__(
428408
429409 self ._default_proxy = proxy
430410 self ._retry_connection : bool = True
431- self ._middlewares = middlewares
411+ self ._middlewares = tuple ( middlewares )
432412
433413 def __init_subclass__ (cls : type ["ClientSession" ]) -> None :
434414 raise TypeError (
@@ -674,6 +654,19 @@ async def _request(
674654 {hdrs .PROXY_AUTHORIZATION : env_proxy_auth }
675655 )
676656
657+ response_params : ResponseParams = {
658+ "timer" : timer ,
659+ "skip_payload" : method in EMPTY_BODY_METHODS ,
660+ "read_until_eof" : read_until_eof ,
661+ "auto_decompress" : auto_decompress ,
662+ "read_timeout" : real_timeout .sock_read ,
663+ "read_bufsize" : read_bufsize ,
664+ "timeout_ceil_threshold" : self ._connector ._timeout_ceil_threshold ,
665+ "max_line_size" : max_line_size ,
666+ "max_field_size" : max_field_size ,
667+ "max_headers" : max_headers ,
668+ }
669+
677670 req = self ._request_class (
678671 method ,
679672 url ,
@@ -689,7 +682,9 @@ async def _request(
689682 loop = self ._loop ,
690683 response_class = self ._response_class ,
691684 proxy = proxy_ ,
685+ response_params = response_params ,
692686 timer = timer ,
687+ timeout = real_timeout ,
693688 session = self ,
694689 ssl = ssl ,
695690 server_hostname = server_hostname ,
@@ -698,52 +693,13 @@ async def _request(
698693 trust_env = self .trust_env ,
699694 )
700695
701- async def _connect_and_send_request (
702- req : ClientRequest ,
703- ) -> ClientResponse :
704- # connection timeout
705- assert self ._connector is not None
706- try :
707- conn = await self ._connector .connect (
708- req , traces = traces , timeout = real_timeout
709- )
710- except asyncio .TimeoutError as exc :
711- raise ConnectionTimeoutError (
712- f"Connection timeout to host { req .url } "
713- ) from exc
714-
715- assert conn .protocol is not None
716- conn .protocol .set_response_params (
717- timer = timer ,
718- skip_payload = req .method in EMPTY_BODY_METHODS ,
719- read_until_eof = read_until_eof ,
720- auto_decompress = auto_decompress ,
721- read_timeout = real_timeout .sock_read ,
722- read_bufsize = read_bufsize ,
723- timeout_ceil_threshold = self ._connector ._timeout_ceil_threshold ,
724- max_line_size = max_line_size ,
725- max_field_size = max_field_size ,
726- max_headers = max_headers ,
727- )
728- try :
729- resp = await req ._send (conn )
730- try :
731- await resp .start (conn )
732- except BaseException :
733- resp .close ()
734- raise
735- except BaseException :
736- conn .close ()
737- raise
738- return resp
739-
740696 # Apply middleware (if any) - per-request middleware overrides session middleware
741697 effective_middlewares = (
742- self ._middlewares if middlewares is None else middlewares
698+ self ._middlewares if middlewares is None else tuple ( middlewares )
743699 )
744700
745701 if effective_middlewares :
746- handler = build_client_middlewares (
702+ handler = _cached_build_client_middlewares (
747703 _connect_and_send_request , effective_middlewares
748704 )
749705 else :
0 commit comments