Middleware factory is run for every single request #2225
Description
Long story short
Maybe that's just my unwaranted assumption, but I thought that a middleware factory should run once for every handler, creating the wrapped handlers. Turns out, it's run for every request. This seems to be terribly inefficient.
Is this an expected behaviour, something that I'm seeing for some strange reasons, or a regression in the library?
Expected behaviour
Run each middleware factory for each handler at one point, at the start of application's lifecycle. Let the new handlers handle requests from that point without being constantly recreated.
Actual behaviour
Middleware factory is run for every request to a handler.
Steps to reproduce
What I'm creating is a middleware that will parse JWT tokens. I need a mechanism to exculde this parsing on some handlers, since some will be accesses without authorization. I didn't use auihttp_security, since it imposes structure that I don't need (I don't have sessions, for example). I may open-source this code later.
The important bits of my code (with some alterations):
# authorization.py
from functools import wraps
from aiohttp import web
import jwt
NON_SECURED_HANDLERS_KEY = "set of endpoints that don't expect a token"
USER_ID_KEY = 'user ID given by token middleware'
async def token_reading_middleware(app, handler):
"""Reads the token from the authorization header and determines the user ID based on the
scope inside it.
Raises 401 if there's something wrong with the header or it's missing.
This check can be disabled by adding the function to the set of non secured handlers
(on the application object, under `NON_SECURED_HANDLERS_KEY`).
"""
# assume that APP_CONFIG is imported
app_secret = app[APP_CONFIG].app_secret
@wraps(handler)
async def middleware_handler(request):
"""Adds the user ID parsed from the authorization header to request or raises 401 error.
"""
# let's say that this function is implemented somewhere and returns 401 is something's wrong with the header
user_id = _get_user_id_from_auth_header(request, app_secret)
request[USER_ID_KEY] = user_id
return await handler(request)
# this is to tell us that the factory code has been run
print('XXX', flush=True)
# let's say that this function determines if the handler is esxccepted from security or not
if _is_except_from_security(handler, app):
return handler
return middleware_handler
# app.py
from aiohttp import web
async def _health_check(_):
return web.HTTPNoContent()
loop = asyncio.get_event_loop()
app = web.Application(
loop=loop,
middlewares=[authorization.token_reading_middleware])
app.router.add_route('GET','/', _health_check)
web.run_app(
app,
host='0.0.0.0',
port=8080,
loop=loop,
)After starting the app (python app.py) I call http://localhost:8080 consecutively. I'm getting 401s because I don;t pass the authorization header.
The problem is, that he application keeps printing 'XXX' for every request, to I know that it jumps through all the hoops of preparing the wrapper and wrapping the handler every time.
Your environment
AioHTTP 2.2.5
Python 3.6.2
Kubuntu 16.04.3 LTS x64