Socket
Socket
Sign inDemoInstall

rate-limiter-flexible

Package Overview
Dependencies
0
Maintainers
1
Versions
163
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.10.1 to 0.11.0

6

BLOCK_STRATEGY.md

@@ -8,3 +8,3 @@ ## Block Strategy

We don't want latency to become 3, 5 or more seconds.
RateLimiterRedis provides a block strategy to avoid too many requests to Redis during DDoS attack.
Any limiter like Redis or Mongo extended from RateLimiterStoreAbstract provides a block strategy to avoid too many requests to Store during DDoS attack.

@@ -15,4 +15,4 @@ It can be activated with setup `inmemoryBlockOnConsumed` and `inmemoryBlockDuration` options.

Note for distributed apps: DDoS requests still can go to Redis if not all NodeJS workers blocked appropriate keys.
Anyway, it allows to avoid over load of Redis
Note for distributed apps: DDoS requests still can go to Store if not all NodeJS workers blocked appropriate keys.
Anyway, it allows to avoid over load of Store

@@ -19,0 +19,0 @@ Block strategy algorithm developed with specificity rate limiter in mind:

@@ -17,2 +17,3 @@ const Record = require('./Record');

if (msBeforeExpires > 0) {
// Change value
this._storage[key].value = this._storage[key].value + value;

@@ -22,3 +23,2 @@

}
clearTimeout(this._storage[key].timeoutId);

@@ -33,2 +33,6 @@ return this.set(key, value, durationSec);

if (this._storage[key]) {
clearTimeout(this._storage[key].timeoutId);
}
this._storage[key] = new Record(value, new Date(Date.now() + durationMs));

@@ -35,0 +39,0 @@ this._storage[key].timeoutId = setTimeout(() => {

@@ -7,2 +7,3 @@ module.exports = class RateLimiterAbstract {

* duration: 1, // Per seconds
* blockDuration: 0, // Block if consumed more than points in current duration for blockDuration seconds
* execEvenly: false, // Execute allowed actions evenly over duration

@@ -15,2 +16,3 @@ * keyPrefix: 'rlflx',

this.duration = opts.duration;
this.blockDuration = opts.blockDuration;
this.execEvenly = opts.execEvenly;

@@ -36,2 +38,18 @@ this.keyPrefix = opts.keyPrefix;

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() {

@@ -38,0 +56,0 @@ return this._execEvenly;

const RateLimiterAbstract = require('./RateLimiterAbstract');
const MemoryStorage = require('./component/MemoryStorage/MemoryStorage');
const RateLimiterRes = require('./RateLimiterRes');

@@ -20,8 +19,14 @@ class RateLimiterMemory extends RateLimiterAbstract {

const rlKey = this.getKey(key);
const res = this._memoryStorage.incrby(rlKey, pointsToConsume, this.duration);
res.remainingPoints = this.points - res.consumedPoints;
let res = this._memoryStorage.incrby(rlKey, pointsToConsume, this.duration);
res.remainingPoints = Math.max(this.points - res.consumedPoints, 0);
if (res.consumedPoints > this.points) {
reject(new RateLimiterRes(0, res.msBeforeNext));
// Block only first time when consumed more than points
if (this.blockDuration > 0 && res.consumedPoints <= (this.points + pointsToConsume)) {
// Block key
res = this._memoryStorage.set(rlKey, res.consumedPoints, this.blockDuration);
}
reject(res);
} else if (this.execEvenly && res.msBeforeNext > 0 && !res.isFirstInDuration) {
// Execute evenly
const delay = Math.ceil(res.msBeforeNext / ((this.points - res.consumedPoints) + 2));

@@ -28,0 +33,0 @@

@@ -18,21 +18,2 @@ const RateLimiterStoreAbstract = require('./RateLimiterStoreAbstract');

const afterConsume = function (resolve, reject, rlKey, points, result) {
const res = getRateLimiterRes.call(this, points, result);
if (res.consumedPoints > this.points) {
// Block key for this.inmemoryBlockDuration seconds
if (this.inmemoryBlockOnConsumed > 0 && res.consumedPoints >= this.inmemoryBlockOnConsumed) {
this._blockedKeys.add(rlKey, this.inmemoryBlockDuration);
res.msBeforeNext = this.msBlockDuration;
}
reject(res);
} else if (this.execEvenly && res.msBeforeNext > 0 && !res.isFirstInDuration) {
const delay = Math.ceil(res.msBeforeNext / (res.remainingPoints + 2));
setTimeout(resolve, delay, res);
} else {
resolve(res);
}
};
const update = function (key, points) {

@@ -46,3 +27,3 @@ return this._collection.findOneAndUpdate(

$inc: { points },
$setOnInsert: { expire: new Date(Date.now() + (this.duration * 1000)) },
$setOnInsert: { expire: new Date(Date.now() + this.msDuration) },
},

@@ -56,2 +37,42 @@ {

const upsertExpire = function (key, points, msDuration) {
return this._collection.findOneAndUpdate(
{
key,
},
{
expire: new Date(Date.now() + msDuration),
$setOnInsert: { points },
},
{
upsert: true,
returnNewDocument: true,
} // eslint-disable-line comma-dangle
);
};
const afterConsume = function (resolve, reject, rlKey, changedPoints, result) {
const res = getRateLimiterRes.call(this, changedPoints, result);
if (res.consumedPoints > this.points) {
if (this.inmemoryBlockOnConsumed > 0 && res.consumedPoints >= this.inmemoryBlockOnConsumed) {
// Block key for this.inmemoryBlockDuration seconds
this._inmemoryBlockedKeys.add(rlKey, this.inmemoryBlockDuration);
res.msBeforeNext = this.msInmemoryBlockDuration;
// Block only first time when consumed more than points
} else if (this.blockDuration > 0 && res.consumedPoints <= (this.points + changedPoints)) {
upsertExpire.call(this, rlKey, res.consumedPoints, this.msBlockDuration);
res.msBeforeNext = this.msBlockDuration;
}
reject(res);
} else if (this.execEvenly && res.msBeforeNext > 0 && !res.isFirstInDuration) {
const delay = Math.ceil(res.msBeforeNext / (res.remainingPoints + 2));
setTimeout(resolve, delay, res);
} else {
resolve(res);
}
};
class RateLimiterMongo extends RateLimiterStoreAbstract {

@@ -104,5 +125,5 @@ /**

const blockMsBeforeExpire = this.getBlockMsBeforeExpire(rlKey);
if (blockMsBeforeExpire > 0) {
return reject(new RateLimiterRes(0, blockMsBeforeExpire));
const inmemoryBlockMsBeforeExpire = this.getInmemoryBlockMsBeforeExpire(rlKey);
if (inmemoryBlockMsBeforeExpire > 0) {
return reject(new RateLimiterRes(0, inmemoryBlockMsBeforeExpire));
}

@@ -109,0 +130,0 @@

const RateLimiterStoreAbstract = require('./RateLimiterStoreAbstract');
const RateLimiterRes = require('./RateLimiterRes');
const afterConsume = function (resolve, reject, rlKey, results) {
const afterConsume = function (resolve, reject, rlKey, changedPoints, results) {
let [resSet, consumed, resTtlMs] = results;

@@ -26,5 +26,9 @@ // Support ioredis results format

if (res.consumedPoints > this.points) {
// Block key for this.inmemoryBlockDuration seconds
if (this.inmemoryBlockOnConsumed > 0 && res.consumedPoints >= this.inmemoryBlockOnConsumed) {
this._blockedKeys.add(rlKey, this.inmemoryBlockDuration);
// Block key in memory for this.inmemoryBlockDuration seconds
this._inmemoryBlockedKeys.add(rlKey, this.inmemoryBlockDuration);
res.msBeforeNext = this.msInmemoryBlockDuration;
} else if (this.blockDuration > 0 && res.consumedPoints <= (this.points + changedPoints)) {
// Block key
this.redis.set(rlKey, res.consumedPoints, 'EX', this.blockDuration, () => {});
res.msBeforeNext = this.msBlockDuration;

@@ -79,5 +83,5 @@ }

const blockMsBeforeExpire = this.getBlockMsBeforeExpire(rlKey);
if (blockMsBeforeExpire > 0) {
return reject(new RateLimiterRes(0, blockMsBeforeExpire));
const inmemoryBlockMsBeforeExpire = this.getInmemoryBlockMsBeforeExpire(rlKey);
if (inmemoryBlockMsBeforeExpire > 0) {
return reject(new RateLimiterRes(0, inmemoryBlockMsBeforeExpire));
}

@@ -93,3 +97,3 @@

} else {
afterConsume.call(this, resolve, reject, rlKey, results);
afterConsume.call(this, resolve, reject, rlKey, pointsToConsume, results);
}

@@ -96,0 +100,0 @@ });

@@ -21,8 +21,8 @@ const RateLimiterAbstract = require('./RateLimiterAbstract');

this.insuranceLimiter = opts.insuranceLimiter;
this._blockedKeys = new BlockedKeys();
this._inmemoryBlockedKeys = new BlockedKeys();
}
getBlockMsBeforeExpire(rlKey) {
getInmemoryBlockMsBeforeExpire(rlKey) {
if (this.inmemoryBlockOnConsumed > 0) {
return this._blockedKeys.msBeforeExpire(rlKey);
return this._inmemoryBlockedKeys.msBeforeExpire(rlKey);
}

@@ -62,3 +62,3 @@

get msBlockDuration() {
get msInmemoryBlockDuration() {
return this._inmemoryBlockDuration * 1000;

@@ -65,0 +65,0 @@ }

{
"name": "rate-limiter-flexible",
"version": "0.10.1",
"version": "0.11.0",
"description": "Flexible API rate limiter backed by Redis for distributed node.js applications",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -20,3 +20,3 @@ [![Build Status](https://travis-ci.org/animir/node-rate-limiter-flexible.png)](https://travis-ci.org/animir/node-rate-limiter-flexible)

Advantages:
* block strategy against really powerful DDoS attacks (like 100k requests per sec) [Read about it and benchmarking here](https://github.com/animir/node-rate-limiter-flexible/blob/master/BLOCK_STRATEGY.md)
* in-memory block strategy against really powerful DDoS attacks (like 100k requests per sec) [Read about it and benchmarking here](https://github.com/animir/node-rate-limiter-flexible/blob/master/BLOCK_STRATEGY.md)
* backed on native Promises

@@ -106,8 +106,12 @@ * works in Cluster without additional software [See RateLimiterCluster benchmark and detailed description here](https://github.com/animir/node-rate-limiter-flexible/blob/master/CLUSTER.md)

const opts = {
// Basic options
redis: redisClient,
keyPrefix: 'rlflx', // useful for multiple limiters
points: 5, // Number of points
duration: 5, // Per second(s)
execEvenly: false,
// Custom
execEvenly: false, // Do not delay actions evenly
blockDuration: 0, // Do not block if consumed more than points
keyPrefix: 'rlflx', // must be unique for limiters with different purpose
// Redis and Mongo specific

@@ -337,3 +341,3 @@ inmemoryBlockOnConsumed: 10, // If 10 points consumed in current duration

* `duration` `Default: 1` Number of seconds before points are reset
* `duration` `Default: 1` Number of seconds before consumed points are reset

@@ -346,2 +350,5 @@ * `execEvenly` `Default: false` Delay action to be executed evenly over duration

* `blockDuration` `Default: 0` If positive number and consumed more than points in current duration,
block for `blockDuration` seconds.
#### Options specific to Redis and Mongo

@@ -348,0 +355,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc