Skip to content

Middleware factory is run for every single request #2225

Closed
@butla

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

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions