Skip to content

Commit

Permalink
Merge pull request #16 from jaytaph/exception
Browse files Browse the repository at this point in the history
Exception
  • Loading branch information
jaytaph committed Nov 17, 2014
2 parents 9c1e46e + 17794ee commit e5990ef
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 0 deletions.
12 changes: 12 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;

/**
* This is the class that validates and merges configuration from your app/config files
Expand Down Expand Up @@ -41,6 +42,17 @@ public function getConfigTreeBuilder()
->defaultValue(static::HTTP_TOO_MANY_REQUESTS)
->info('The HTTP status code to return when a client hits the rate limit')
->end()
->scalarNode('rate_response_exception')
->defaultNull()
->info('Optional exception class that will be returned when a client hits the rate limit')
->validate()
->always(function ($item) {
if (! is_subclass_of($item, '\Exception')) {
throw new InvalidConfigurationException(sprintf("'%s' must inherit the \\Exception class", $item));
}
})
->end()
->end()
->scalarNode('rate_response_message')
->defaultValue('You exceeded the rate limit')
->info('The HTTP message to return when a client hits the rate limit')
Expand Down
1 change: 1 addition & 0 deletions DependencyInjection/NoxlogicRateLimitExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function load(array $configs, ContainerBuilder $container)

private function loadServices(ContainerBuilder $container, array $config)
{
$container->setParameter('noxlogic_rate_limit.rate_response_exception', $config['rate_response_exception']);
$container->setParameter('noxlogic_rate_limit.rate_response_code', $config['rate_response_code']);
$container->setParameter('noxlogic_rate_limit.rate_response_message', $config['rate_response_message']);

Expand Down
7 changes: 7 additions & 0 deletions EventListener/RateLimitAnnotationListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ public function onKernelController(FilterControllerEvent $event)

// When we exceeded our limit, return a custom error response
if ($rateLimitInfo->getCalls() > $rateLimitInfo->getLimit()) {

// Throw an exception if configured.
if ($this->getParameter('rate_response_exception')) {
$class = $this->getParameter('rate_response_exception');
throw new $class($this->getParameter('rate_response_message'), $this->getParameter('rate_response_code'));
}

$message = $this->getParameter('rate_response_message');
$code = $this->getParameter('rate_response_code');
$event->setController(function () use ($message, $code) {
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,13 @@ class RateLimitGenerateKeyListener

Make sure to generate a key based on what is rate limited in your controllers.


## Throwing exceptions

Instead of returning a Response object when a rate limit has exceeded, it's also possible to throw an exception. This
allows you to easily handle the rate limit on another level, for instance by capturing the ``kernel.exception`` event.


## Running tests

If you want to run the tests use:
Expand Down
4 changes: 4 additions & 0 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
<argument>rate_response_message</argument>
<argument>%noxlogic_rate_limit.rate_response_message%</argument>
</call>
<call method="setParameter">
<argument>rate_response_exception</argument>
<argument>%noxlogic_rate_limit.rate_response_exception%</argument>
</call>
</service>

<service id="noxlogic_rate_limit.header_modification_listener" class="%noxlogic_rate_limit.header_modification_listener.class%">
Expand Down
22 changes: 22 additions & 0 deletions Tests/DependencyInjection/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public function testUnconfiguredConfiguration()
'redis_client' => 'default_client',
'memcache_client' => 'default',
'rate_response_code' => 429,
'rate_response_exception' => null,
'rate_response_message' => 'You exceeded the rate limit',
'display_headers' => true,
'headers' => array(
Expand Down Expand Up @@ -126,4 +127,25 @@ public function testDefaultPathLimitMethods()
$this->assertArrayHasKey('path_limits', $configuration);
$this->assertEquals($pathLimits, $configuration['path_limits']);
}

/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
*/
public function testMustBeBasedOnExceptionClass()
{
$configuration = $this->getConfigs(array('rate_response_exception' => '\StdClass'));
}

/**
*
*/
public function testMustBeBasedOnExceptionClass2()
{
$configuration = $this->getConfigs(array('rate_response_exception' => '\InvalidArgumentException'));

# no exception triggered is ok.
$this->assertTrue(true);
}


}
53 changes: 53 additions & 0 deletions Tests/EventListener/RateLimitAnnotationListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Noxlogic\RateLimitBundle\Tests\TestCase;
use ReflectionMethod;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;

Expand Down Expand Up @@ -326,4 +327,56 @@ protected function createListener($expects)
$this->mockPathLimitProcessor
);
}


/**
* @expectedException \BadFunctionCallException
* @expectedExceptionCode 123
* @expectedExceptionMessage a message
*/
public function testRateLimitThrottlingWithException()
{
$listener = $this->createListener($this->any());
$listener->setParameter('rate_response_exception', '\BadFunctionCallException');
$listener->setParameter('rate_response_code', 123);
$listener->setParameter('rate_response_message', 'a message');

$event = $this->createEvent();
$event->getRequest()->attributes->set('_x-rate-limit', array(
new RateLimit(array('limit' => 5, 'period' => 3)),
));

// Throttled
$storage = $this->getMockStorage();
$storage->createMockRate(':Noxlogic\RateLimitBundle\EventListener\Tests\MockController:mockAction', 5, 10, 6);
$listener->onKernelController($event);
}

public function testRateLimitThrottlingWithMessages()
{
$listener = $this->createListener($this->any());
$listener->setParameter('rate_response_code', 123);
$listener->setParameter('rate_response_message', 'a message');

$event = $this->createEvent();
$event->getRequest()->attributes->set('_x-rate-limit', array(
new RateLimit(array('limit' => 5, 'period' => 3)),
));

// Throttled
$storage = $this->getMockStorage();
$storage->createMockRate(':Noxlogic\RateLimitBundle\EventListener\Tests\MockController:mockAction', 5, 10, 6);

/** @var Response $response */
$listener->onKernelController($event);

// Call the controller, it will return a response object
$a = $event->getController();
$response = $a();

$this->assertEquals($response->getStatusCode(), 123);
$this->assertEquals($response->getContent(), "a message");
}


}

0 comments on commit e5990ef

Please sign in to comment.