mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
added ratelimiter tests
This commit is contained in:
parent
51ac297c59
commit
5f739c111e
@ -26,6 +26,11 @@ class RateLimitMiddleware implements HTTPMiddleware
|
||||
*/
|
||||
private $decay = 1;
|
||||
|
||||
/**
|
||||
* @var RateLimiter|null
|
||||
*/
|
||||
private $rateLimiter;
|
||||
|
||||
/**
|
||||
* @param HTTPRequest $request
|
||||
* @param callable $delegate
|
||||
@ -33,11 +38,13 @@ class RateLimitMiddleware implements HTTPMiddleware
|
||||
*/
|
||||
public function process(HTTPRequest $request, callable $delegate)
|
||||
{
|
||||
$limiter = RateLimiter::create(
|
||||
$this->getKeyFromRequest($request),
|
||||
$this->getMaxAttempts(),
|
||||
$this->getDecay()
|
||||
);
|
||||
if (!$limiter = $this->getRateLimiter()) {
|
||||
$limiter = RateLimiter::create(
|
||||
$this->getKeyFromRequest($request),
|
||||
$this->getMaxAttempts(),
|
||||
$this->getDecay()
|
||||
);
|
||||
}
|
||||
if ($limiter->canAccess()) {
|
||||
$limiter->hit();
|
||||
$response = $delegate($request);
|
||||
@ -140,4 +147,22 @@ class RateLimitMiddleware implements HTTPMiddleware
|
||||
{
|
||||
return $this->decay;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RateLimiter $rateLimiter
|
||||
* @return $this
|
||||
*/
|
||||
public function setRateLimiter($rateLimiter)
|
||||
{
|
||||
$this->rateLimiter = $rateLimiter;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RateLimiter|null
|
||||
*/
|
||||
public function getRateLimiter()
|
||||
{
|
||||
return $this->rateLimiter;
|
||||
}
|
||||
}
|
||||
|
@ -190,13 +190,14 @@ class DBDatetime extends DBDate implements TemplateGlobalProvider
|
||||
*/
|
||||
public static function set_mock_now($datetime)
|
||||
{
|
||||
if ($datetime instanceof DBDatetime) {
|
||||
self::$mock_now = $datetime;
|
||||
} elseif (is_string($datetime)) {
|
||||
self::$mock_now = DBField::create_field('Datetime', $datetime);
|
||||
} else {
|
||||
throw new InvalidArgumentException('DBDatetime::set_mock_now(): Wrong format: ' . $datetime);
|
||||
if (!$datetime instanceof DBDatetime) {
|
||||
$value = $datetime;
|
||||
$datetime = DBField::create_field('Datetime', $datetime);
|
||||
if ($datetime === false) {
|
||||
throw new InvalidArgumentException('DBDatetime::set_mock_now(): Wrong format: ' . $value);
|
||||
}
|
||||
}
|
||||
self::$mock_now = $datetime;
|
||||
}
|
||||
|
||||
/**
|
||||
|
18
tests/php/Control/Middleware/Control/TestController.php
Normal file
18
tests/php/Control/Middleware/Control/TestController.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Control\Tests\Middleware\Control;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
|
||||
class TestController extends Controller
|
||||
{
|
||||
public function index($request)
|
||||
{
|
||||
return "Success";
|
||||
}
|
||||
|
||||
public function Link($action = null)
|
||||
{
|
||||
return Controller::join_links('TestController', $action);
|
||||
}
|
||||
}
|
68
tests/php/Control/Middleware/RateLimitMiddlewareTest.php
Normal file
68
tests/php/Control/Middleware/RateLimitMiddlewareTest.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Control\Tests\Middleware;
|
||||
|
||||
use SilverStripe\Control\Middleware\RateLimitMiddleware;
|
||||
use SilverStripe\Control\Middleware\RequestHandlerMiddlewareAdapter;
|
||||
use SilverStripe\Control\Tests\Middleware\Control\TestController;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\FunctionalTest;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
|
||||
class RateLimitMiddlewareTest extends FunctionalTest
|
||||
{
|
||||
|
||||
protected static $extra_controllers = [
|
||||
TestController::class,
|
||||
];
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
DBDatetime::set_mock_now('2017-09-27 00:00:00');
|
||||
Config::modify()->set(Injector::class, 'TestRateLimitMiddleware', [
|
||||
'class' => RateLimitMiddleware::class,
|
||||
'properties' => [
|
||||
'ExtraKey' => 'test',
|
||||
'MaxAttempts' => 2,
|
||||
'Decay' => 1,
|
||||
],
|
||||
]);
|
||||
Config::modify()->set(Injector::class, 'RateLimitTestController', [
|
||||
'class' => RequestHandlerMiddlewareAdapter::class,
|
||||
'properties' => [
|
||||
'RequestHandler' => '%$' . TestController::class,
|
||||
'Middlewares' => [
|
||||
'%$TestRateLimitMiddleware'
|
||||
],
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getExtraRoutes()
|
||||
{
|
||||
$rules = parent::getExtraRoutes();
|
||||
$rules['TestController//$Action/$ID/$OtherID'] = '%$RateLimitTestController';
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function testRequest()
|
||||
{
|
||||
$response = $this->get('TestController');
|
||||
$this->assertFalse($response->isError());
|
||||
$this->assertEquals(2, $response->getHeader('X-RateLimit-Limit'));
|
||||
$this->assertEquals(1, $response->getHeader('X-RateLimit-Remaining'));
|
||||
$this->assertEquals(DBDatetime::now()->getTimestamp() + 60, $response->getHeader('X-RateLimit-Reset'));
|
||||
$this->assertEquals('Success', $response->getBody());
|
||||
$response = $this->get('TestController');
|
||||
$this->assertFalse($response->isError());
|
||||
$this->assertEquals(0, $response->getHeader('X-RateLimit-Remaining'));
|
||||
$response = $this->get('TestController');
|
||||
$this->assertTrue($response->isError());
|
||||
$this->assertEquals(429, $response->getStatusCode());
|
||||
$this->assertEquals(60, $response->getHeader('retry-after'));
|
||||
$this->assertNotEquals('Success', $response->getBody());
|
||||
}
|
||||
|
||||
}
|
129
tests/php/Core/Cache/RateLimiterTest.php
Normal file
129
tests/php/Core/Cache/RateLimiterTest.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Cache;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use SilverStripe\Core\Cache\RateLimiter;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
use Symfony\Component\Cache\Simple\ArrayCache;
|
||||
|
||||
class RateLimiterTest extends SapphireTest
|
||||
{
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
DBDatetime::set_mock_now('2017-09-27 00:00:00');
|
||||
}
|
||||
|
||||
public function testConstruct()
|
||||
{
|
||||
$cache = new ArrayCache();
|
||||
$rateLimiter = new RateLimiter(
|
||||
'test',
|
||||
5,
|
||||
1
|
||||
);
|
||||
$rateLimiter->setCache($cache);
|
||||
$this->assertEquals('test', $rateLimiter->getIdentifier());
|
||||
$this->assertEquals(5, $rateLimiter->getMaxAttempts());
|
||||
$this->assertEquals(1, $rateLimiter->getDecay());
|
||||
}
|
||||
|
||||
public function testGetNumberOfAttempts()
|
||||
{
|
||||
$cache = new ArrayCache();
|
||||
$rateLimiter = new RateLimiter(
|
||||
'test',
|
||||
5,
|
||||
1
|
||||
);
|
||||
$rateLimiter->setCache($cache);
|
||||
for ($i = 0; $i < 7; ++$i) {
|
||||
$this->assertEquals($i, $rateLimiter->getNumAttempts());
|
||||
$rateLimiter->hit();
|
||||
}
|
||||
}
|
||||
|
||||
public function testGetNumAttemptsRemaining()
|
||||
{
|
||||
$cache = new ArrayCache();
|
||||
$rateLimiter = new RateLimiter(
|
||||
'test',
|
||||
1,
|
||||
1
|
||||
);
|
||||
$rateLimiter->setCache($cache);
|
||||
$this->assertEquals(1, $rateLimiter->getNumAttemptsRemaining());
|
||||
$rateLimiter->hit();
|
||||
$this->assertEquals(0, $rateLimiter->getNumAttemptsRemaining());
|
||||
$rateLimiter->hit();
|
||||
$this->assertEquals(0, $rateLimiter->getNumAttemptsRemaining());
|
||||
|
||||
}
|
||||
|
||||
public function testGetTimeToReset()
|
||||
{
|
||||
$cache = new ArrayCache();
|
||||
$rateLimiter = new RateLimiter(
|
||||
'test',
|
||||
1,
|
||||
1
|
||||
);
|
||||
$rateLimiter->setCache($cache);
|
||||
$this->assertEquals(0, $rateLimiter->getTimeToReset());
|
||||
$rateLimiter->hit();
|
||||
$this->assertEquals(60, $rateLimiter->getTimeToReset());
|
||||
DBDatetime::set_mock_now(DBDatetime::now()->getTimestamp() + 30);
|
||||
$this->assertEquals(30, $rateLimiter->getTimeToReset());
|
||||
}
|
||||
|
||||
public function testClearAttempts()
|
||||
{
|
||||
$cache = new ArrayCache();
|
||||
$rateLimiter = new RateLimiter(
|
||||
'test',
|
||||
1,
|
||||
1
|
||||
);
|
||||
$rateLimiter->setCache($cache);
|
||||
for ($i = 0; $i < 5; ++$i) {
|
||||
$rateLimiter->hit();
|
||||
}
|
||||
$this->assertEquals(5, $rateLimiter->getNumAttempts());
|
||||
$rateLimiter->clearAttempts();
|
||||
$this->assertEquals(0, $rateLimiter->getNumAttempts());
|
||||
}
|
||||
|
||||
public function testHit()
|
||||
{
|
||||
$cache = new ArrayCache();
|
||||
$rateLimiter = new RateLimiter(
|
||||
'test',
|
||||
1,
|
||||
1
|
||||
);
|
||||
$rateLimiter->setCache($cache);
|
||||
$this->assertFalse($cache->has('test'));
|
||||
$this->assertFalse($cache->has('test-timer'));
|
||||
$rateLimiter->hit();
|
||||
$this->assertTrue($cache->has('test'));
|
||||
$this->assertTrue($cache->has('test-timer'));
|
||||
}
|
||||
|
||||
public function testCanAccess()
|
||||
{
|
||||
$cache = new ArrayCache();
|
||||
$rateLimiter = new RateLimiter(
|
||||
'test',
|
||||
1,
|
||||
1
|
||||
);
|
||||
$rateLimiter->setCache($cache);
|
||||
$this->assertTrue($rateLimiter->canAccess());
|
||||
$rateLimiter->hit();
|
||||
$this->assertFalse($rateLimiter->canAccess());
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user