Source code for request_limiter.decorators

import threading
from functools import wraps
from typing import Optional

from request_limiter.exceptions import LimitException
from request_limiter.strategy import LimitStrategy, LimitedIntervalStrategy


[docs]class RequestLimiterDecorator(object): """ A decorator class used to limit request rate to a function using a custom strategy or the default LimitedIntervalStrategy. """ def __init__(self, strategy: Optional[LimitStrategy] = None): """ :param strategy: A request limit strategy """ self.strategy = strategy or LimitedIntervalStrategy() def __call__(self, f): """ Returns a wrapped function that checks the strategy before invoking the function :param f: The function to be wrapped :return: Wrapped function """ # Run clean up daemon in background clean_task = threading.Thread(target=self.strategy.clean, daemon=True) clean_task.start() @wraps(f) def wrapper(*args, **kwargs): """ Checks and raises LimitException if the function reached the maximum allowed invocation """ with threading.RLock(): # Request in a tread safe way key = kwargs.pop('limit_key', None) if not self.strategy.allow(key=key): # Failed to allocate raise LimitException('Rate limit exceeded.', self.strategy) return f(*args, **kwargs) return wrapper
[docs]def django_request_limiter(f): """ Returns a wrapped function for django request handler function. It applies limit strategy based on request IP and returns 429. :param f: django request handler function decorated with request_limiter :return: wrapped function """ @wraps(f) def wrapper(request, *args, **kwargs): # Set the default limit per IP ip = request.META.get('REMOTE_ADDR') kwargs['limit_key'] = ip try: return f(request, *args, **kwargs) except LimitException as e: from django.http import HttpResponse body = "Rate limit exceeded. Try again in {} seconds".format(e.strategy.get_remaining(ip)) return HttpResponse(body, status=429) return wrapper