-
-
Save smowtion/808f6ad72f395d1df27df7f6fc9ba0a5 to your computer and use it in GitHub Desktop.
Simple, rude, rate limiter (using memcache).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* Very simple, crude, rate limiter */ | |
/* | |
Assumes _MEMCACHEDSITEPREFIX, _MEMCACHEDHOST and _MEMCACHEDPORT are `defined`: | |
define('_MEMCACHEDHOST', '127.0.0.1'); | |
define('_MEMCACHEDPORT', 11211); | |
define('_MEMCACHEDSITEPREFIX', 'mysite'); | |
*/ | |
class RateLimiter { | |
private $rate_limit_cycle; //Seconds | |
private $cycle_req_quota; //Reqs/cycle | |
function RateLimiter($rate_limit_cycle = 86400, $cycle_req_quota = 5000) { | |
$this->rate_limit_cycle = $rate_limit_cycle; | |
$this->cycle_req_quota = $cycle_req_quota; | |
} | |
public function IsLimited($key, $prolong_on_rate_limit_exceeded = false) { | |
$memcache_id = _MEMCACHEDSITEPREFIX . '.ratelimit.' . $key; | |
$memcache = new Memcache(); | |
$memcache->connect(_MEMCACHEDHOST, _MEMCACHEDPORT); | |
$current_hits = intval($memcache->get($memcache_id)); | |
if ($current_hits >= $this->cycle_req_quota) { | |
if ($prolong_on_rate_limit_exceeded) { | |
//When rate limit is hit we prolong the key to the rate_limit_cycle number of seconds ("extra punishment") | |
$memcache->set($memcache_id, $this->cycle_req_quota, 0, $this->rate_limit_cycle); | |
} | |
return true; | |
} | |
if ($current_hits == 0) | |
$memcache->set($memcache_id, 1, 0, $this->rate_limit_cycle); | |
else | |
$memcache->increment($memcache_id); | |
return false; | |
} | |
//Returns if the request is rate limited; returning an error etc. is up to you | |
public static function CheckLimit($key, $rate_limit_cycle = 86400, $cycle_req_quota = 5000, $prolong_on_rate_limit_exceeded = false) { | |
$r = new RateLimiter($rate_limit_cycle, $cycle_req_quota, $prolong_on_rate_limit_exceeded); | |
return $r->IsLimited($key); | |
} | |
//Checks the rate limit for a specific key and exits with some HTTP headers when the rate limit is hit | |
public static function CheckLimitExit($key, $rate_limit_cycle = 86400, $cycle_req_quota = 5000, $prolong_on_rate_limit_exceeded = false) { | |
if (RateLimiter::CheckLimit($key, $rate_limit_cycle, $cycle_req_quota, $prolong_on_rate_limit_exceeded)) { | |
header('HTTP/1.1 429 Too Many Requests'); | |
header(sprintf('Retry-After: %d', $rate_limit_cycle)); | |
header('Connection: close'); | |
echo '<h1>Too many requests</h1>'; | |
exit(); | |
} | |
} | |
//RateLimiter::CheckLimitByIPExit(); //5000 requests per day | |
//RateLimiter::CheckLimitByIPExit(null, 10, 5); //5 requests per 10 seconds | |
//RateLimiter::CheckLimitByIPExit("somemethodname", 10, 5); //Same as above, this time for a specific method | |
public static function CheckLimitByIPExit($key = null, $rate_limit_cycle = 86400, $cycle_req_quota = 5000, $prolong_on_rate_limit_exceeded = false) { | |
RateLimiter::CheckLimitExit((isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null) . $key, $rate_limit_cycle, $cycle_req_quota, $prolong_on_rate_limit_exceeded); | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment