rate-limiter-flexible
Version:
Node.js atomic and non-atomic counters, rate limiting tools, protection from DoS and brute-force attacks at scale
134 lines (109 loc) • 3.28 kB
JavaScript
module.exports = class RateLimiterAbstract {
/**
*
* @param opts Object {
* points: <required>, // Number of points (must be a number, can be negative)
* duration: <required>, // Per seconds (must be a non-negative number, 0 = never expires)
* blockDuration: 0, // Block if consumed more than points in current duration for blockDuration seconds
* execEvenly: false, // Execute allowed actions evenly over duration
* execEvenlyMinDelayMs: duration * 1000 / points, // ms, works with execEvenly=true option
* keyPrefix: 'rlflx',
* }
*/
constructor(opts = {}) {
this.points = opts.points;
this.duration = opts.duration;
this.blockDuration = opts.blockDuration;
this.execEvenly = opts.execEvenly;
this.execEvenlyMinDelayMs = opts.execEvenlyMinDelayMs;
this.keyPrefix = opts.keyPrefix;
}
get points() {
return this._points;
}
set points(value) {
if (Number.isFinite(value)) {
this._points = value;
} else {
throw new Error('points must be set and must be a finite number');
}
}
get duration() {
return this._duration;
}
set duration(value) {
if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {
this._duration = value;
} else {
throw new Error('duration must be set and must be a finite, non-negative number');
}
}
get msDuration() {
return this.duration * 1000;
}
get blockDuration() {
return this._blockDuration;
}
set blockDuration(value) {
this._blockDuration = typeof value === 'undefined' ? 0 : value;
}
get msBlockDuration() {
return this.blockDuration * 1000;
}
get execEvenly() {
return this._execEvenly;
}
set execEvenly(value) {
this._execEvenly = typeof value === 'undefined' ? false : Boolean(value);
}
get execEvenlyMinDelayMs() {
return this._execEvenlyMinDelayMs;
}
set execEvenlyMinDelayMs(value) {
this._execEvenlyMinDelayMs = typeof value === 'undefined' ? Math.ceil(this.msDuration / this.points) : value;
}
get keyPrefix() {
return this._keyPrefix;
}
set keyPrefix(value) {
if (typeof value === 'undefined') {
value = 'rlflx';
}
if (typeof value !== 'string') {
throw new Error('keyPrefix must be string');
}
this._keyPrefix = value;
}
_getKeySecDuration(options = {}) {
return options && options.customDuration >= 0
? options.customDuration
: this.duration;
}
getKey(key) {
return this.keyPrefix.length > 0 ? `${this.keyPrefix}:${key}` : key;
}
parseKey(rlKey) {
return this.keyPrefix.length > 0 ? rlKey.substring(this.keyPrefix.length + 1) : rlKey;
}
consume() {
throw new Error("You have to implement the method 'consume'!");
}
penalty() {
throw new Error("You have to implement the method 'penalty'!");
}
reward() {
throw new Error("You have to implement the method 'reward'!");
}
get() {
throw new Error("You have to implement the method 'get'!");
}
set() {
throw new Error("You have to implement the method 'set'!");
}
block() {
throw new Error("You have to implement the method 'block'!");
}
delete() {
throw new Error("You have to implement the method 'delete'!");
}
};