-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #500 from the-control-group/rate_limiting
Rate limiting strategy
- Loading branch information
Showing
14 changed files
with
153 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import { performance } from "perf_hooks"; | ||
import { TooManyRequests } from "../errors"; | ||
|
||
/** | ||
* Applies a simple in-memory rate limiting scheme. This system is designed to prevent a single | ||
* malfunctioning client from bringing down the service. This system is only designed to prevent | ||
* unintentional abuse by malfunctioning clients. | ||
*/ | ||
export interface RateLimiter { | ||
/** | ||
* Applies the configured rate limit to the given key. If the key is being excessively used, throws a TooManyRequests exception. | ||
* @param key A string representing the key to use for rate limiting. This is generally the id of an authorization, | ||
* client, or credential. | ||
*/ | ||
limit(key: string): void; | ||
} | ||
|
||
export class LocalMemoryRateLimiter implements RateLimiter { | ||
private readonly map: { [key: string]: number[] } = { | ||
__proto__: null as any, | ||
}; | ||
|
||
constructor( | ||
private readonly limitPerWindow = 100, | ||
private readonly window = 60 * 1_000, | ||
private readonly timeSource: () => number = performance.now | ||
) {} | ||
|
||
limit(key: string): void { | ||
const currentTime = this.timeSource(); | ||
|
||
for (const existingKey of Object.keys(this.map)) { | ||
for (let i = 0; i < this.map[existingKey].length; ++i) { | ||
if (currentTime - this.map[existingKey][i] > this.window) { | ||
this.map[existingKey].splice(i, 1); | ||
--i; | ||
} | ||
} | ||
|
||
if (this.map[existingKey].length == 0) { | ||
delete this.map[existingKey]; | ||
} | ||
} | ||
|
||
if ( | ||
typeof this.map[key] !== "undefined" && | ||
this.map[key].length >= this.limitPerWindow | ||
) { | ||
throw new TooManyRequests(); | ||
} | ||
|
||
if (typeof this.map[key] === "undefined") { | ||
this.map[key] = []; | ||
} | ||
|
||
this.map[key].push(currentTime); | ||
} | ||
} | ||
|
||
export class NoOpRateLimiter implements RateLimiter { | ||
limit(): void { | ||
// This rate limiter does nothing. It only exists so that clients can call some rate limiter. | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import test from "ava"; | ||
import { LocalMemoryRateLimiter } from "./ratelimiter"; | ||
|
||
test("Rate limiter over rate", async (t) => { | ||
let curTime = 0; | ||
|
||
const limiter = new LocalMemoryRateLimiter(3, 60 * 1_000, () => { | ||
curTime += 14_000; | ||
return curTime; | ||
}); | ||
|
||
limiter.limit("A"); | ||
limiter.limit("B"); | ||
limiter.limit("C"); | ||
limiter.limit("A"); | ||
limiter.limit("B"); | ||
limiter.limit("C"); | ||
limiter.limit("A"); | ||
limiter.limit("B"); | ||
limiter.limit("C"); | ||
|
||
limiter.limit("A"); | ||
limiter.limit("A"); | ||
limiter.limit("A"); | ||
|
||
try { | ||
limiter.limit("A"); | ||
t.fail("4th call in the same minute should cause 429"); | ||
} catch (ex) { | ||
t.pass(); | ||
} | ||
}); |
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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
This file contains 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