Skip to content

Commit

Permalink
Require cache hits to be minimally one second in duration. Update art…
Browse files Browse the repository at this point in the history
…isansdk/bench, license, readme. Added strict_types declaration. Fix bucket's drip reset. Fix CS. Fix parsing of bucket parameters when not string.
  • Loading branch information
Daniel LaBarge committed Aug 16, 2022
1 parent 9bc8edc commit 08abab7
Show file tree
Hide file tree
Showing 41 changed files with 349 additions and 201 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
.gitconfig
.google-services.json
.idea
.php_cs.cache
.php-cs-fixer.cache
.phpunit.result.cache
.profile
.viminfo
Expand Down
27 changes: 0 additions & 27 deletions .php_cs

This file was deleted.

1 change: 0 additions & 1 deletion .phpunit.result.cache

This file was deleted.

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018-2021 Artisans Collaborative (http://artisanscollaborative.com)
Copyright (c) 2018-2022 Artisan Made, Co. (http://artisanmade.io)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
23 changes: 8 additions & 15 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "artisansdk/ratelimiter",
"description": "A leaky bucket rate limiter and middleware controls for route-level granularity.",
"description": "A leaky bucket rate limiter and corresponding middleware with route-level granularity compatible with Laravel.",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Daniel LaBarge",
"email": "daniel@artisanscollaborative.com"
"email": "daniel@artisanmade.io"
}
],
"config": {
Expand All @@ -16,12 +16,12 @@
},
"require": {
"php": ">=7.0|>=8.0",
"illuminate/contracts": "~5.0 | ~6.0 | ~7.0 | ~8.0| ~9.0",
"nesbot/carbon": "~1.0 | ~2.0",
"illuminate/contracts": "~5.0|~6.0|~7.0|~8.0|~9.0",
"nesbot/carbon": "~1.0|~2.0",
"symfony/http-kernel": "~2.6|~3.3|~4.0|~5.0|~6.0"
},
"require-dev": {
"artisansdk/bench": "~1.0.0-rc1"
"artisansdk/bench": "~1.0.0-rc2"
},
"autoload": {
"psr-4": {
Expand All @@ -34,15 +34,8 @@
}
},
"scripts": {
"fix": "@php vendor/bin/bench fix src/ tests/ --cache=\".php_cs.cache\" --ansi",
"test": "@php vendor/bin/bench test src/ tests/ --cache=\".php_cs.cache\" --no-coverage --ansi",
"report": [
"[ -d reports ] || mkdir reports",
"@composer test",
"vendor/bin/phpmd \"src/\" html codesize,cleancode,unusedcode,naming --reportfile reports/messes.html --ignore-violations-on-exit",
"vendor/bin/pdepend --summary-xml=reports/stats.xml \"src/\"",
"vendor/bin/phploc --log-xml=reports/summary.xml src/",
"vendor/bin/phpcpd src/ --log-pmd=reports/duplications.xml --min-lines=3 --min-tokens=35"
]
"fix": "@php vendor/bin/bench fix src/ tests/ --cache=\".php-cs-fixer.cache\" --ansi",
"test": "@php vendor/bin/bench test src/ tests/ --cache=\".php-cs-fixer.cache\" --no-coverage --ansi",
"report": "@php vendor/bin/bench report src/ reports/ --min-lines=3 --min-tokens=35 --ansi"
}
}
33 changes: 25 additions & 8 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,37 @@ A leaky bucket rate limiter and corresponding middleware with route-level granul

## Table of Contents

- [Installation](#installation)
- [Usage Guide](#usage-guide)
- [Rate Limiter](#rate-limiter)
- [Table of Contents](#table-of-contents)
- [Installation](#installation)
- [Usage Guide](#usage-guide)
- [Overview of the Laravel Rate Limiter](#overview-of-the-laravel-rate-limiter)
- [Laravel's Implementation](#laravels-implementation)
- [Problem 1: Bursting Exploit](#problem-1-bursting-exploit)
- [Problem 2: No Granularity](#problem-2-no-granularity)
- [Problem 3: Not Extensible](#problem-3-not-extensible)
- [Understanding the Leaky Bucket Algorithm](#understanding-the-leaky-bucket-algorithm)
- [Leaky Bucket Implementation](#leaky-bucket-implementation)
- [Solution 1: Bursting Limit](#solution-1-bursting-limit)
- [Solution 2: Route-Level Granularity](#solution-2-route-level-granularity)
- [Solution 3: Extensible Key Resolvers](#solution-3-extensible-key-resolvers)
- [Bonus: Overflow Penalties](#bonus-overflow-penalties)
- [Different Rates for Guests vs. Authenticated Users](#different-rates-for-guests-vs-authenticated-users)
- [Different Rates for Different Users](#different-rates-for-different-users)
- [Handling the Rate Limit Exceptions](#handling-the-rate-limit-exceptions)
- [Setting a Custom Cache for the Rate Limiter](#setting-a-custom-cache-for-the-rate-limiter)
- [How Request Signature Resolvers Work](#how-request-signature-resolvers-work)
- [How Multiple Buckets Work](#how-multiple-buckets-work)
- [Using the Built In Resolvers](#using-the-built-in-resolvers)
- [Creating Custom Resolvers](#creating-custom-resolvers)
- [Setting a Custom Resolver as the Default](#setting-a-custom-resolver-as-the-default)
- [Using the Rate Limiter by Itself](#using-the-rate-limiter-by-itself)
- [Creating a Custom Rate Limiter](#creating-a-custom-rate-limiter)
- [Using the Bucket by Itself](#using-the-bucket-by-itself)
- [Running the Tests](#running-the-tests)
- [Licensing](#licensing)
- [Using the Evented Bucket](#using-the-evented-bucket)
- [Logging the Drips in the Bucket](#logging-the-drips-in-the-bucket)
- [Running the Tests](#running-the-tests)
- [Licensing](#licensing)

## Installation

Expand Down Expand Up @@ -658,7 +675,7 @@ $bucket = new Leaky('foo', 1, 0.016667); // bucket that overflows at more than 1

$bucket = (new Leaky('foo')) // instantiate the same bucket as above
->max(100) // $bucket->max() would return 100
->rate(3) // $bucket->rate() would return 10
->rate(10) // $bucket->rate() would return 10
->drips(50) // $bucket->drips() would return 50
->timer(time() - 10) // $bucket->timer() would get the time
->fill(10) // $bucket->remaining() would return 40
Expand All @@ -680,8 +697,8 @@ dispatching through a command bus, then you might need to log calls to the bucke
as events the rest of your application can listen for.

> **Note:** The `Evented` bucket is an extension of the `Leaky` bucket that only
wraps the parent class with events. All the same builder logic and behavior is the
same otherwise.
> wraps the parent class with events. All the same builder logic and behavior is the
> same otherwise.
You can switch from the basic `Leaky` bucket to the `Evented` bucket by binding
the interface to the concrete the `register()` method of your
Expand Down Expand Up @@ -740,7 +757,7 @@ See the `composer.json` for more details on their execution and reporting output

## Licensing

Copyright (c) 2018-2021 [Artisans Collaborative](https://artisanscollaborative.com)
Copyright (c) 2018-2022 [Artisan Made, Co.](http://artisanmade.io)

This package is released under the MIT license. Please see the LICENSE file
distributed with every copy of the code for commercial licensing terms.
8 changes: 5 additions & 3 deletions src/Buckets/Evented.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter\Buckets;

use ArtisanSdk\RateLimiter\Contracts\Bucket;
Expand All @@ -22,7 +24,7 @@ class Evented extends Leaky
protected $events;

/**
* {@inheritdoc}
* @inheritdoc
*/
public function __construct(Dispatcher $events, string $key = 'default', int $max = 60, $rate = 1)
{
Expand All @@ -32,7 +34,7 @@ public function __construct(Dispatcher $events, string $key = 'default', int $ma
}

/**
* {@inheritdoc}
* @inheritdoc
*/
public function leak($rate = null): Bucket
{
Expand All @@ -57,7 +59,7 @@ public function leak($rate = null): Bucket
}

/**
* {@inheritdoc}
* @inheritdoc
*/
public function fill(int $drips = 1): Bucket
{
Expand Down
18 changes: 10 additions & 8 deletions src/Buckets/Leaky.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter\Buckets;

use ArtisanSdk\RateLimiter\Contracts\Bucket;
Expand Down Expand Up @@ -115,26 +117,26 @@ public function rate($value = null)
*/
public function drips(): int
{
return max(0, ceil($this->drips));
return (int) max(0, ceil($this->drips));
}

/**
* Get the remaining drips before the bucket overflows.
*/
public function remaining(): int
{
return max(0, $this->max() - $this->drips());
return (int) max(0, $this->max() - $this->drips());
}

/**
* Get the duration in seconds before the bucket is fully drained.
*/
public function duration(): float
{
return (float) max(0,
return (float) (max(0,
microtime(true)
+ ($this->drips() / $this->rate())
- $this->timer()
- $this->timer())
);
}

Expand Down Expand Up @@ -166,7 +168,7 @@ public function leak($rate = null): Bucket
$timer = $this->timer();
$now = $this->reset()->timer();
$elapsed = $now - $timer;
$drops = floor($elapsed * $rate);
$drops = (int) floor($elapsed * $rate);

$this->drips = $this->bounded($drips - $drops);

Expand Down Expand Up @@ -235,9 +237,9 @@ public function toArray()
/**
* Convert the bucket into something JSON serializable.
*
* @return array
* @return mixed
*/
public function jsonSerialize()
public function jsonSerialize() : mixed
{
return $this->toArray();
}
Expand All @@ -259,6 +261,6 @@ public function toJson($options = 0)
*/
protected function bounded(int $drips): int
{
return max(0, min($this->max(), $drips));
return (int) max(0, min($this->max(), $drips));
}
}
4 changes: 3 additions & 1 deletion src/Contracts/Bucket.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter\Contracts;

use Illuminate\Contracts\Support\Arrayable;
Expand Down Expand Up @@ -57,7 +59,7 @@ public function isEmpty(): bool;
*
* @return \ArtisanSdk\RateLimiter\Contracts\Bucket
*/
public function reset(): Bucket;
public function reset(): self;

/**
* Configure the setting for the bucket.
Expand Down
2 changes: 2 additions & 0 deletions src/Contracts/Limiter.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter\Contracts;

interface Limiter
Expand Down
2 changes: 2 additions & 0 deletions src/Contracts/Resolver.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter\Contracts;

interface Resolver
Expand Down
8 changes: 5 additions & 3 deletions src/Events/Event.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter\Events;

use Illuminate\Contracts\Support\Arrayable;
Expand Down Expand Up @@ -55,7 +57,7 @@ public function __isset($key)
*
* @return \ArtisanSdk\RateLimiter\Events\Event
*/
public function fill(array $payload = []): Event
public function fill(array $payload = []): self
{
foreach ($payload as $key => $value) {
$this->payload[$key] = $value;
Expand All @@ -77,9 +79,9 @@ public function toArray()
/**
* Convert the event into something JSON serializable.
*
* @return array
* @return mixed
*/
public function jsonSerialize()
public function jsonSerialize() : mixed
{
return $this->toArray();
}
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Filled.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter\Events;

class Filled extends Event
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Filling.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter\Events;

class Filling extends Event
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Leaked.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter\Events;

class Leaked extends Event
Expand Down
2 changes: 2 additions & 0 deletions src/Events/Leaking.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter\Events;

class Leaking extends Event
Expand Down
2 changes: 2 additions & 0 deletions src/Exception.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace ArtisanSdk\RateLimiter;

use Symfony\Component\HttpFoundation\Response;
Expand Down
Loading

0 comments on commit 08abab7

Please sign in to comment.