| """Client middleware support.""" |
|
|
| from collections.abc import Awaitable, Callable, Sequence |
|
|
| from .client_reqrep import ClientRequest, ClientResponse |
|
|
| __all__ = ("ClientMiddlewareType", "ClientHandlerType", "build_client_middlewares") |
|
|
| |
| ClientHandlerType = Callable[[ClientRequest], Awaitable[ClientResponse]] |
|
|
| |
| ClientMiddlewareType = Callable[ |
| [ClientRequest, ClientHandlerType], Awaitable[ClientResponse] |
| ] |
|
|
|
|
| def build_client_middlewares( |
| handler: ClientHandlerType, |
| middlewares: Sequence[ClientMiddlewareType], |
| ) -> ClientHandlerType: |
| """ |
| Apply middlewares to request handler. |
| |
| The middlewares are applied in reverse order, so the first middleware |
| in the list wraps all subsequent middlewares and the handler. |
| |
| This implementation avoids using partial/update_wrapper to minimize overhead |
| and doesn't cache to avoid holding references to stateful middleware. |
| """ |
| |
| if len(middlewares) == 1: |
| middleware = middlewares[0] |
|
|
| async def single_middleware_handler(req: ClientRequest) -> ClientResponse: |
| return await middleware(req, handler) |
|
|
| return single_middleware_handler |
|
|
| |
| current_handler = handler |
|
|
| for middleware in reversed(middlewares): |
| |
| def make_wrapper( |
| mw: ClientMiddlewareType, next_h: ClientHandlerType |
| ) -> ClientHandlerType: |
| async def wrapped(req: ClientRequest) -> ClientResponse: |
| return await mw(req, next_h) |
|
|
| return wrapped |
|
|
| current_handler = make_wrapper(middleware, current_handler) |
|
|
| return current_handler |
|
|