Skip to content

Commit

Permalink
Route specific rate limiting now works across many different routes.
Browse files Browse the repository at this point in the history
Signed-off-by: Jason Lewis <[email protected]>
  • Loading branch information
jasonlewis committed Nov 26, 2014
1 parent 38cfe29 commit febeb58
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/Http/Filter/RateLimitFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ protected function attachAfterFilter()
$this->router->after(function (Request $request, Response $response) {
$response->headers->set('X-RateLimit-Limit', $this->limiter->getThrottle()->getLimit());
$response->headers->set('X-RateLimit-Remaining', $this->limiter->getRemainingLimit());
$response->headers->set('X-RateLimit-Reset', $this->limiter->getRateLimitExpiration());
$response->headers->set('X-RateLimit-Reset', $this->limiter->getRateLimitReset());
});
}
}
45 changes: 41 additions & 4 deletions src/Http/RateLimit/RateLimiter.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ class RateLimiter
*/
protected $request;

/**
* The key prefix used when throttling route specific requests.
*
* @var string
*/
protected $keyPrefix;

/**
* Create a new rate limiter instance.
*
Expand Down Expand Up @@ -77,6 +84,8 @@ public function rateLimitRequest(Request $request, $limit = 0, $expires = 0)
if ($limit > 0 || $expires > 0) {
$this->throttle = new RouteSpecificThrottle(['limit' => $limit, 'expires' => $expires]);

$this->keyPrefix = md5($request->path());

// Otherwise we'll use the throttle that gives the consumer the largest
// amount of requests. If no matching throttle is found then rate
// limiting will not be imposed for the request.
Expand All @@ -90,11 +99,28 @@ public function rateLimitRequest(Request $request, $limit = 0, $expires = 0)
return;
}

$this->prepareCacheStore();

$this->cache('requests', 0, $this->throttle->getExpires());
$this->cache('expires', time() + ($this->throttle->getExpires() * 60), $this->throttle->getExpires());
$this->cache('expires', $this->throttle->getExpires(), $this->throttle->getExpires());
$this->cache('reset', time() + ($this->throttle->getExpires() * 60), $this->throttle->getExpires());
$this->increment('requests');
}

/**
* Prepare the cache store.
*
* @return void
*/
protected function prepareCacheStore()
{
if ($this->retrieve('expires') != $this->throttle->getExpires()) {
$this->forget('requests');
$this->forget('expires');
$this->forget('reset');
}
}

/**
* Determine if the rate limit has been exceeded.
*
Expand Down Expand Up @@ -125,7 +151,7 @@ protected function getMatchingThrottles()
*/
protected function key($key)
{
return sprintf('dingo.api.%s.%s', $key, $this->request->getClientIp());
return sprintf('dingo.api.%s.%s.%s', $this->keyPrefix, $key, $this->request->getClientIp());
}

/**
Expand Down Expand Up @@ -163,6 +189,17 @@ protected function increment($key)
$this->cache->increment($this->key($key));
}

/**
* Forget a key in the cache.
*
* @param string $key
* @return void
*/
protected function forget($key)
{
$this->cache->forget($this->key($key));
}

/**
* Determine if the request was rate limited.
*
Expand Down Expand Up @@ -200,9 +237,9 @@ public function getRemainingLimit()
*
* @return int
*/
public function getRateLimitExpiration()
public function getRateLimitReset()
{
return $this->retrieve('expires');
return $this->retrieve('reset');
}

/**
Expand Down

0 comments on commit febeb58

Please sign in to comment.