Mercurial > public > sg101
comparison gpp/antispam/rate_limit.py @ 479:32cec6cd8808
Refactor RateLimiter so that if Redis is not running, everything still runs normally (minus the rate limiting protection). My assumption that creating a Redis connection would throw an exception if Redis wasn't running was wrong. The exceptions actually occur when you issue a command. This is for #224.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sun, 25 Sep 2011 00:49:05 +0000 |
parents | 5e826e232932 |
children | 6f5fff924877 |
comparison
equal
deleted
inserted
replaced
478:d280b27fed17 | 479:32cec6cd8808 |
---|---|
15 HOST = getattr(settings, 'RATE_LIMIT_REDIS_HOST', 'localhost') | 15 HOST = getattr(settings, 'RATE_LIMIT_REDIS_HOST', 'localhost') |
16 PORT = getattr(settings, 'RATE_LIMIT_REDIS_PORT', 6379) | 16 PORT = getattr(settings, 'RATE_LIMIT_REDIS_PORT', 6379) |
17 DB = getattr(settings, 'RATE_LIMIT_REDIS_DB', 0) | 17 DB = getattr(settings, 'RATE_LIMIT_REDIS_DB', 0) |
18 | 18 |
19 | 19 |
20 # This exception is thrown upon any Redis error. This insulates client code from | |
21 # knowing that we are using Redis and will allow us to use something else in the | |
22 # future. | |
20 class RateLimiterUnavailable(Exception): | 23 class RateLimiterUnavailable(Exception): |
21 pass | 24 pass |
22 | 25 |
23 | 26 |
24 def _make_key(ip): | 27 def _make_key(ip): |
60 | 63 |
61 """ | 64 """ |
62 key = _make_key(ip) | 65 key = _make_key(ip) |
63 conn = _get_connection() | 66 conn = _get_connection() |
64 | 67 |
65 conn.setex(key, count, _to_seconds(interval)) | 68 try: |
69 conn.setex(key, count, _to_seconds(interval)) | |
70 except redis.RedisError, e: | |
71 logger.error("rate limit (block_ip): %s" % e) | |
72 raise RateLimiterUnavailable | |
73 | |
66 logger.info("Rate limiter blocked IP %s; %d / %s", ip, count, interval) | 74 logger.info("Rate limiter blocked IP %s; %d / %s", ip, count, interval) |
67 | 75 |
68 | 76 |
69 class RateLimiter(object): | 77 class RateLimiter(object): |
70 """ | 78 """ |
82 def is_blocked(self): | 90 def is_blocked(self): |
83 """ | 91 """ |
84 Return True if the IP is blocked, and false otherwise. | 92 Return True if the IP is blocked, and false otherwise. |
85 | 93 |
86 """ | 94 """ |
87 val = self.conn.get(self.key) | 95 try: |
96 val = self.conn.get(self.key) | |
97 except redis.RedisError, e: | |
98 logger.error("RateLimiter (is_blocked): %s" % e) | |
99 raise RateLimiterUnavailable | |
100 | |
88 try: | 101 try: |
89 val = int(val) if val else 0 | 102 val = int(val) if val else 0 |
90 except ValueError: | 103 except ValueError: |
91 return False | 104 return False |
92 | 105 |
103 otherwise. If the set_point is exceeded for the first time, the counter | 116 otherwise. If the set_point is exceeded for the first time, the counter |
104 associated with the IP is set to expire according to the lockout | 117 associated with the IP is set to expire according to the lockout |
105 parameter. | 118 parameter. |
106 | 119 |
107 """ | 120 """ |
108 val = self.conn.incr(self.key) | 121 try: |
122 val = self.conn.incr(self.key) | |
109 | 123 |
110 # Set expire time, if necessary. | 124 # Set expire time, if necessary. |
111 # If this is the first time, set it according to interval. | 125 # If this is the first time, set it according to interval. |
112 # If the set_point has just been exceeded, set it according to lockout. | 126 # If the set_point has just been exceeded, set it according to lockout. |
113 if val == 1: | 127 if val == 1: |
114 self.conn.expire(self.key, _to_seconds(self.interval)) | 128 self.conn.expire(self.key, _to_seconds(self.interval)) |
115 elif val == self.set_point: | 129 elif val == self.set_point: |
116 self.conn.expire(self.key, _to_seconds(self.lockout)) | 130 self.conn.expire(self.key, _to_seconds(self.lockout)) |
117 | 131 |
118 tripped = val >= self.set_point | 132 tripped = val >= self.set_point |
119 | 133 |
120 if tripped: | 134 if tripped: |
121 logger.info("Rate limiter tripped for %s; counter = %d", self.ip, val) | 135 logger.info("Rate limiter tripped for %s; counter = %d", self.ip, val) |
136 return tripped | |
122 | 137 |
123 return tripped | 138 except redis.RedisError, e: |
139 logger.error("RateLimiter (incr): %s" % e) | |
140 raise RateLimiterUnavailable |