client_middlewares.py 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. """Client middleware support."""
  2. from collections.abc import Awaitable, Callable, Sequence
  3. from .client_reqrep import ClientRequest, ClientResponse
  4. __all__ = ("ClientMiddlewareType", "ClientHandlerType", "build_client_middlewares")
  5. # Type alias for client request handlers - functions that process requests and return responses
  6. ClientHandlerType = Callable[[ClientRequest], Awaitable[ClientResponse]]
  7. # Type for client middleware - similar to server but uses ClientRequest/ClientResponse
  8. ClientMiddlewareType = Callable[
  9. [ClientRequest, ClientHandlerType], Awaitable[ClientResponse]
  10. ]
  11. def build_client_middlewares(
  12. handler: ClientHandlerType,
  13. middlewares: Sequence[ClientMiddlewareType],
  14. ) -> ClientHandlerType:
  15. """
  16. Apply middlewares to request handler.
  17. The middlewares are applied in reverse order, so the first middleware
  18. in the list wraps all subsequent middlewares and the handler.
  19. This implementation avoids using partial/update_wrapper to minimize overhead
  20. and doesn't cache to avoid holding references to stateful middleware.
  21. """
  22. # Optimize for single middleware case
  23. if len(middlewares) == 1:
  24. middleware = middlewares[0]
  25. async def single_middleware_handler(req: ClientRequest) -> ClientResponse:
  26. return await middleware(req, handler)
  27. return single_middleware_handler
  28. # Build the chain for multiple middlewares
  29. current_handler = handler
  30. for middleware in reversed(middlewares):
  31. # Create a new closure that captures the current state
  32. def make_wrapper(
  33. mw: ClientMiddlewareType, next_h: ClientHandlerType
  34. ) -> ClientHandlerType:
  35. async def wrapped(req: ClientRequest) -> ClientResponse:
  36. return await mw(req, next_h)
  37. return wrapped
  38. current_handler = make_wrapper(middleware, current_handler)
  39. return current_handler