Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug in dynamic route always results in cache_key 0:0 #41

Open
rbracco opened this issue Oct 6, 2023 · 0 comments
Open

Bug in dynamic route always results in cache_key 0:0 #41

rbracco opened this issue Oct 6, 2023 · 0 comments

Comments

@rbracco
Copy link

rbracco commented Oct 6, 2023

Bug

Code breaks for dynamic routes. The __call__method of depends/RateLimiter checks for matches via if route.path == request.scope["path"] and request.method in route.methods: in the block below

        route_index = 0
        dep_index = 0
        for i, route in enumerate(request.app.routes):
            if route.path == request.scope["path"] and request.method in route.methods:
                route_index = i
                for j, dependency in enumerate(route.dependencies):
                    if self is dependency.dependency:
                        dep_index = j
                        break

route.path is generic and for dynamic routes is like /path/to/{id} but request.scope["path"] is the actual realized path like /path/to/51 or /path/to/186 so they never match and because route_index and dep_index are both initalized to 0, they are 0 when the loop exits leading to key collisions for all dynamic routes (and also anything that coincidentally does match the 0th route and 0th dependency). The key for all dynamic routes will be f"{prefix}:{identifier}:0:0".

Proposed Fix

route.path should be checked against request.scope["route"].path (instead of against request.scope["path"])

        def _get_indices(self, request):
        request_path = request.scope["route"].path
        for route_index, route in enumerate(request.app.routes):
            if route.path == request_path:
                if request.method in route.methods:
                    for dep_index, dependency in enumerate(route.dependencies):
                        if self is dependency.dependency:
                            return route_index, dep_index
                // Debatable how to handle this case, I just raise a ValueError
                raise ValueError(f"No matching dependency found for dependency of route {request_path}")
        raise ValueError(f"No matching route found for route {request_path}")

    async def __call__(self, request: Request, response: Response):
        if not FastAPILimiter.redis:
            raise Exception("You must call FastAPILimiter.init in startup event of fastapi!")
        route_index, dep_index = self._get_indices(request)

Also in the current version there are key collisions for different Methods on the same route, this can be fixed by including the request.method in the key. Here is how I chose to use my key as it makes it clear what's what in case I ever have to revisit in the future and debug inside redis.

key = f"{FastAPILimiter.prefix}:{user_identifier}:route{route_index}-dep{dep_index}-{request.method}"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant