def _init_limiter(self): try: redis_pool = get_redis_pool(db=settings["RATE_LIMITER_REDIS_DB"]) except RedisNotConfigured: logger.warning( "Redis URL not configured: rate limiter not started") self._limiter = None else: # If a redis is configured, then we use the corresponding redis service in the rate # limiter. self._limiter = RateLimiter( resource=self.resource, max_requests=self.max_requests, expire=self.expire, redis_pool=redis_pool, )
def setUp(self): """ Initialises Rate Limit class and delete all keys from Redis. """ self.rate_limit = RateLimit(resource='test', client='localhost', max_requests=10) self.rate_limit._reset()
class IdunnRateLimiter: def __init__(self, resource, max_requests, expire): self.resource = resource self.max_requests = max_requests self.expire = expire self._init_limiter() def _init_limiter(self): try: redis_pool = get_redis_pool(db=settings["RATE_LIMITER_REDIS_DB"]) except RedisNotConfigured: logger.warning( "Redis URL not configured: rate limiter not started") self._limiter = None else: """ If a redis is configured, then we use the corresponding redis service in the rate limiter. """ self._limiter = RateLimiter( resource=self.resource, max_requests=self.max_requests, expire=self.expire, redis_pool=redis_pool, ) def limit(self, client, ignore_redis_error=False): if self._limiter is None: return dummy_limit() @contextmanager def limit(): try: with self._limiter.limit(client): yield except RedisError as e: if ignore_redis_error: logger.warning( "Ignoring RedisError in rate limiter for %s", self._limiter.resource, exc_info=True, ) yield else: raise return limit() def check_limit_per_client(self, request): client_id = request.headers.get("x-client-hash") or "default" try: with self.limit(client=client_id, ignore_redis_error=True): pass except TooManyRequestsException: raise HTTPException(status_code=429, detail="Too Many Requests")
def test_limit_10_using_rate_limiter(self): """ Should raise TooManyRequests Exception when trying to increment for the eleventh time. """ self.rate_limit = RateLimiter(resource='test', max_requests=10, expire=2).limit(client='localhost') self.assertEqual(self.rate_limit.get_usage(), 0) self.assertEqual(self.rate_limit.has_been_reached(), False) self._make_10_requests() self.assertEqual(self.rate_limit.get_usage(), 10) self.assertEqual(self.rate_limit.has_been_reached(), True) with self.assertRaises(TooManyRequests): with self.rate_limit: pass self.assertEqual(self.rate_limit.get_usage(), 11) self.assertEqual(self.rate_limit.has_been_reached(), True)
def test_not_expired(self): """ Should raise TooManyRequests Exception when the expire time has not been reached yet. """ self.rate_limit = RateLimit(resource='test', client='localhost', max_requests=10, expire=2) self._make_10_requests() time.sleep(1) with self.assertRaises(TooManyRequests): with self.rate_limit: pass
def get_limiter(cls): if cls._limiter is None: from app import settings try: redis_pool = get_redis_pool(settings, db=settings['WIKI_API_REDIS_DB']) except RedisNotConfigured: logger.warning( "Redis URL not configured: rate limiter not started") cls._limiter = DISABLED_STATE else: """ If a redis is configured, then we use the corresponding redis service in the rate limiter. """ max_calls = int(settings['WIKI_API_RL_MAX_CALLS']) redis_period = int(settings['WIKI_API_RL_PERIOD']) cls._limiter = RateLimiter(resource='WikipediaAPI', max_requests=max_calls, expire=redis_period, redis_pool=redis_pool) return cls._limiter
class TestRedisRateLimit(unittest.TestCase): def setUp(self): """ Initialises Rate Limit class and delete all keys from Redis. """ self.rate_limit = RateLimit(resource='test', client='localhost', max_requests=10) self.rate_limit._reset() def _make_10_requests(self): """ Increments usage ten times. """ for x in range(0, 10): with self.rate_limit: pass def test_limit_10_max_request(self): """ Should raise TooManyRequests Exception when trying to increment for the eleventh time. """ self.assertEqual(self.rate_limit.get_usage(), 0) self.assertEqual(self.rate_limit.has_been_reached(), False) self._make_10_requests() self.assertEqual(self.rate_limit.get_usage(), 10) self.assertEqual(self.rate_limit.has_been_reached(), True) with self.assertRaises(TooManyRequests): with self.rate_limit: pass self.assertEqual(self.rate_limit.get_usage(), 11) self.assertEqual(self.rate_limit.has_been_reached(), True) def test_expire(self): """ Should not raise TooManyRequests Exception when trying to increment for the eleventh time after the expire time. """ self._make_10_requests() time.sleep(1) with self.rate_limit: pass def test_not_expired(self): """ Should raise TooManyRequests Exception when the expire time has not been reached yet. """ self.rate_limit = RateLimit(resource='test', client='localhost', max_requests=10, expire=2) self._make_10_requests() time.sleep(1) with self.assertRaises(TooManyRequests): with self.rate_limit: pass def test_limit_10_using_rate_limiter(self): """ Should raise TooManyRequests Exception when trying to increment for the eleventh time. """ self.rate_limit = RateLimiter(resource='test', max_requests=10, expire=2).limit(client='localhost') self.assertEqual(self.rate_limit.get_usage(), 0) self.assertEqual(self.rate_limit.has_been_reached(), False) self._make_10_requests() self.assertEqual(self.rate_limit.get_usage(), 10) self.assertEqual(self.rate_limit.has_been_reached(), True) with self.assertRaises(TooManyRequests): with self.rate_limit: pass self.assertEqual(self.rate_limit.get_usage(), 11) self.assertEqual(self.rate_limit.has_been_reached(), True)