]>
git.ipfire.org Git - ipfire.org.git/blob - src/backend/ratelimit.py
7 class RateLimiter(misc
.Object
):
8 def handle_request(self
, request
, handler
, minutes
, limit
):
9 return RateLimiterRequest(self
.backend
, request
, handler
,
10 minutes
=minutes
, limit
=limit
)
13 class RateLimiterRequest(misc
.Object
):
16 def init(self
, request
, handler
, minutes
, limit
):
17 self
.request
= request
18 self
.handler
= handler
21 self
.minutes
= minutes
24 self
.now
= datetime
.datetime
.utcnow()
26 # Fetch the current counter value from the cache
27 self
.counter
= self
.get_counter()
29 # Increment the rate-limiting counter
30 self
.increment_counter()
32 # Write the header if we are not limited
33 if not self
.is_ratelimited():
36 def is_ratelimited(self
):
38 Returns True if the request is prohibited by the rate limiter
40 # The client is rate-limited when more requests have been
41 # received than allowed.
42 return self
.counter
>= self
.limit
44 def get_counter(self
):
46 Returns the number of requests that have been done in
49 keys
= self
.get_keys_to_check()
51 res
= self
.memcache
.get_multi(keys
)
53 return sum((int(e
) for e
in res
.values()))
57 def write_headers(self
):
58 # Send the limit to the user
59 self
.handler
.set_header("X-Rate-Limit-Limit", self
.limit
)
61 # Send the user how many requests are left for this time window
62 self
.handler
.set_header("X-Rate-Limit-Remaining",
63 self
.limit
- self
.counter
)
65 expires
= self
.now
+ datetime
.timedelta(seconds
=self
.expires_after
)
66 self
.handler
.set_header("X-Rate-Limit-Reset", expires
.strftime("%s"))
69 key_prefix
= self
.get_key_prefix()
71 return "%s-%s" % (key_prefix
, self
.now
.strftime("%Y-%m-%d-%H:%M"))
73 def get_keys_to_check(self
):
74 key_prefix
= self
.get_key_prefix()
77 for minute
in range(self
.minutes
+ 1):
78 when
= self
.now
- datetime
.timedelta(minutes
=minute
)
80 key
= "%s-%s" % (key_prefix
, when
.strftime("%Y-%m-%d-%H:%M"))
85 def get_key_prefix(self
):
86 return "-".join((self
.prefix
, self
.request
.host
, self
.request
.path
,
87 self
.request
.method
, self
.request
.remote_ip
,))
89 def increment_counter(self
):
92 # Add the key or increment if it already exists
93 if not self
.memcache
.add(key
, "1", self
.expires_after
):
94 self
.memcache
.incr(key
)
97 def expires_after(self
):
99 Returns the number of seconds after which the counter has reset.
101 return (self
.minutes
+ 1) * 60