UNPKG

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
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'!"); } };