def test_window_and_concurrent_limit(self): """Test that if there is a window limit and a concurrent limit, the FIXED_WINDOW limit takes precedence""" return_val = above_rate_limit_check("xar", RateLimit(0, 100, 0), "request_uid") assert return_val.rate_limit_type == RateLimitType.FIXED_WINDOW assert return_val.concurrent_remaining is None
def test_above_rate_limit_check(self): with freeze_time("2000-01-01"): expected_reset_time = int(time() + 100) return_val = above_rate_limit_check("foo", RateLimit(10, 100), "request_uid") assert return_val == RateLimitMeta( rate_limit_type=RateLimitType.NOT_LIMITED, current=1, limit=10, window=100, reset_time=expected_reset_time, remaining=9, concurrent_limit=None, concurrent_requests=None, ) for i in range(10): return_val = above_rate_limit_check("foo", RateLimit(10, 100), f"request_uid{i}") assert return_val == RateLimitMeta( rate_limit_type=RateLimitType.FIXED_WINDOW, current=11, limit=10, window=100, reset_time=expected_reset_time, remaining=0, concurrent_limit=None, concurrent_requests=None, ) for i in range(10): return_val = above_rate_limit_check("bar", RateLimit(120, 100, 9), f"request_uid{i}") assert return_val == RateLimitMeta( rate_limit_type=RateLimitType.CONCURRENT, current=10, limit=120, window=100, reset_time=expected_reset_time, remaining=110, concurrent_limit=9, concurrent_requests=9, )
def test_above_rate_limit_check(self): with freeze_time("2000-01-01"): expected_reset_time = int(time() + 100) return_val = above_rate_limit_check("foo", RateLimit(10, 100)) assert return_val == RateLimitMeta( is_limited=False, current=1, limit=10, window=100, reset_time=expected_reset_time, remaining=9, ) for _ in range(10): return_val = above_rate_limit_check("foo", RateLimit(10, 100)) assert return_val == RateLimitMeta( is_limited=True, current=11, limit=10, window=100, reset_time=expected_reset_time, remaining=0, )
def process_view(self, request: Request, view_func, view_args, view_kwargs) -> Response | None: """Check if the endpoint call will violate.""" try: # TODO: put these fields into their own object request.will_be_rate_limited = False request.rate_limit_category = None request.rate_limit_uid = uuid.uuid4().hex request.rate_limit_key = get_rate_limit_key(view_func, request) if request.rate_limit_key is None: return category_str = request.rate_limit_key.split(":", 1)[0] request.rate_limit_category = category_str rate_limit = get_rate_limit_value( http_method=request.method, endpoint=view_func.view_class, category=RateLimitCategory(category_str), ) if rate_limit is None: return request.rate_limit_metadata = above_rate_limit_check( request.rate_limit_key, rate_limit, request.rate_limit_uid) # TODO: also limit by concurrent window once we have the data rate_limit_cond = (request.rate_limit_metadata.rate_limit_type != RateLimitType.NOT_LIMITED if ENFORCE_CONCURRENT_RATE_LIMITS else request.rate_limit_metadata.rate_limit_type == RateLimitType.FIXED_WINDOW) if rate_limit_cond: request.will_be_rate_limited = True enforce_rate_limit = getattr(view_func.view_class, "enforce_rate_limit", False) if enforce_rate_limit: return HttpResponse( { "detail": DEFAULT_ERROR_MESSAGE.format( limit=request.rate_limit_metadata.limit, window=request.rate_limit_metadata.window, ) }, status=429, ) except Exception: logging.exception( "Error during rate limiting, failing open. THIS SHOULD NOT HAPPEN" )
def process_view(self, request: Request, view_func, view_args, view_kwargs) -> Response | None: """Check if the endpoint call will violate.""" request.will_be_rate_limited = False request.rate_limit_category = None key = get_rate_limit_key(view_func, request) if key is None: return category_str = key.split(":", 1)[0] request.rate_limit_category = category_str rate_limit = get_rate_limit_value( http_method=request.method, endpoint=view_func.view_class, category=RateLimitCategory(category_str), ) if rate_limit is None: return request.rate_limit_metadata = above_rate_limit_check(key, rate_limit) if request.rate_limit_metadata.is_limited: request.will_be_rate_limited = True enforce_rate_limit = getattr(view_func.view_class, "enforce_rate_limit", False) if enforce_rate_limit: return HttpResponse( { "detail": DEFAULT_ERROR_MESSAGE.format( limit=request.rate_limit_metadata.limit, window=request.rate_limit_metadata.window, ) }, status=429, )
def do_request(): uid = uuid.uuid4().hex meta = above_rate_limit_check("foo", RateLimit(10, 1, 3), uid) sleep(0.2) finish_request("foo", uid) return meta
def test_above_rate_limit_check(self): return_val = above_rate_limit_check("foo", RateLimit(10, 100)) assert return_val == dict(is_limited=False, current=1, limit=10, window=100)