72
72
WSMessageTypeError ,
73
73
WSServerHandshakeError ,
74
74
)
75
+ from .client_middlewares import ClientMiddlewareType , build_client_middlewares
75
76
from .client_reqrep import (
76
77
SSL_ALLOWED_TYPES ,
77
78
ClientRequest ,
@@ -193,6 +194,7 @@ class _RequestOptions(TypedDict, total=False):
193
194
auto_decompress : Union [bool , None ]
194
195
max_line_size : Union [int , None ]
195
196
max_field_size : Union [int , None ]
197
+ middlewares : Optional [Tuple [ClientMiddlewareType , ...]]
196
198
197
199
198
200
@frozen_dataclass_decorator
@@ -260,6 +262,7 @@ class ClientSession:
260
262
"_default_proxy" ,
261
263
"_default_proxy_auth" ,
262
264
"_retry_connection" ,
265
+ "_middlewares" ,
263
266
)
264
267
265
268
def __init__ (
@@ -292,6 +295,7 @@ def __init__(
292
295
max_line_size : int = 8190 ,
293
296
max_field_size : int = 8190 ,
294
297
fallback_charset_resolver : _CharsetResolver = lambda r , b : "utf-8" ,
298
+ middlewares : Optional [Tuple [ClientMiddlewareType , ...]] = None ,
295
299
) -> None :
296
300
# We initialise _connector to None immediately, as it's referenced in __del__()
297
301
# and could cause issues if an exception occurs during initialisation.
@@ -376,6 +380,7 @@ def __init__(
376
380
self ._default_proxy = proxy
377
381
self ._default_proxy_auth = proxy_auth
378
382
self ._retry_connection : bool = True
383
+ self ._middlewares = middlewares
379
384
380
385
def __init_subclass__ (cls : Type ["ClientSession" ]) -> None :
381
386
raise TypeError (
@@ -450,6 +455,7 @@ async def _request(
450
455
auto_decompress : Optional [bool ] = None ,
451
456
max_line_size : Optional [int ] = None ,
452
457
max_field_size : Optional [int ] = None ,
458
+ middlewares : Optional [Tuple [ClientMiddlewareType , ...]] = None ,
453
459
) -> ClientResponse :
454
460
# NOTE: timeout clamps existing connect and read timeouts. We cannot
455
461
# set the default to None because we need to detect if the user wants
@@ -642,32 +648,33 @@ async def _request(
642
648
trust_env = self .trust_env ,
643
649
)
644
650
645
- # connection timeout
646
- try :
647
- conn = await self ._connector .connect (
648
- req , traces = traces , timeout = real_timeout
651
+ # Core request handler - now includes connection logic
652
+ async def _connect_and_send_request (
653
+ req : ClientRequest ,
654
+ ) -> ClientResponse :
655
+ # connection timeout
656
+ assert self ._connector is not None
657
+ try :
658
+ conn = await self ._connector .connect (
659
+ req , traces = traces , timeout = real_timeout
660
+ )
661
+ except asyncio .TimeoutError as exc :
662
+ raise ConnectionTimeoutError (
663
+ f"Connection timeout to host { req .url } "
664
+ ) from exc
665
+
666
+ assert conn .protocol is not None
667
+ conn .protocol .set_response_params (
668
+ timer = timer ,
669
+ skip_payload = req .method in EMPTY_BODY_METHODS ,
670
+ read_until_eof = read_until_eof ,
671
+ auto_decompress = auto_decompress ,
672
+ read_timeout = real_timeout .sock_read ,
673
+ read_bufsize = read_bufsize ,
674
+ timeout_ceil_threshold = self ._connector ._timeout_ceil_threshold ,
675
+ max_line_size = max_line_size ,
676
+ max_field_size = max_field_size ,
649
677
)
650
- except asyncio .TimeoutError as exc :
651
- raise ConnectionTimeoutError (
652
- f"Connection timeout to host { url } "
653
- ) from exc
654
-
655
- assert conn .transport is not None
656
-
657
- assert conn .protocol is not None
658
- conn .protocol .set_response_params (
659
- timer = timer ,
660
- skip_payload = method in EMPTY_BODY_METHODS ,
661
- read_until_eof = read_until_eof ,
662
- auto_decompress = auto_decompress ,
663
- read_timeout = real_timeout .sock_read ,
664
- read_bufsize = read_bufsize ,
665
- timeout_ceil_threshold = self ._connector ._timeout_ceil_threshold ,
666
- max_line_size = max_line_size ,
667
- max_field_size = max_field_size ,
668
- )
669
-
670
- try :
671
678
try :
672
679
resp = await req .send (conn )
673
680
try :
@@ -678,6 +685,30 @@ async def _request(
678
685
except BaseException :
679
686
conn .close ()
680
687
raise
688
+ return resp
689
+
690
+ # Apply middleware (if any) - per-request middleware overrides session middleware
691
+ effective_middlewares = (
692
+ self ._middlewares if middlewares is None else middlewares
693
+ )
694
+
695
+ if effective_middlewares :
696
+ handler = build_client_middlewares (
697
+ _connect_and_send_request , effective_middlewares
698
+ )
699
+ else :
700
+ handler = _connect_and_send_request
701
+
702
+ try :
703
+ resp = await handler (req )
704
+ # Client connector errors should not be retried
705
+ except (
706
+ ConnectionTimeoutError ,
707
+ ClientConnectorError ,
708
+ ClientConnectorCertificateError ,
709
+ ClientConnectorSSLError ,
710
+ ):
711
+ raise
681
712
except (ClientOSError , ServerDisconnectedError ):
682
713
if retry_persistent_connection :
683
714
retry_persistent_connection = False
0 commit comments