Socket
Socket
Sign inDemoInstall

rate-limiter-flexible

Package Overview
Dependencies
Maintainers
1
Versions
163
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rate-limiter-flexible - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0

BLOCK_STRATEGY.md

48

lib/RateLimiterRedis.js
const RateLimiterInterface = require('./RateLimiterInterface');
const RateLimiterRes = require('./RateLimiterRes');
const RateLimiterMemory = require('./RateLimiterMemory');
const BlockedKeys = require('./component/BlockedKeys');

@@ -34,2 +35,8 @@ const handleRedisError = function(funcName, resolve, reject, rlKey, pointsToConsume) {

if (consumed > this.points) {
// Block key for this.blockDuration seconds
if (this.blockOnPointsConsumed > 0 && consumed >= this.blockOnPointsConsumed) {
this._blockedKeys.add(rlKey, this.blockDuration);
res.msBeforeNext = this.msBlockDuration;
}
reject(res);

@@ -61,3 +68,6 @@ } else {

this.redis = opts.redis;
this.blockOnPointsConsumed = opts.blockOnPointsConsumed;
this.blockDuration = opts.blockDuration;
this.inMemoryLimiter = opts.inMemoryLimiter;
this._blockedKeys = new BlockedKeys();
}

@@ -76,2 +86,28 @@

get blockOnPointsConsumed() {
return this._blockOnPointsConsumed;
}
set blockOnPointsConsumed(value) {
this._blockOnPointsConsumed = value ? parseInt(value) : 0;
if (this.blockOnPointsConsumed > 0 && this.points >= this.blockOnPointsConsumed) {
throw new Error('blockOnPointsConsumed option must be more than points option');
}
}
get blockDuration() {
return this._blockDuration;
}
get msBlockDuration() {
return this._blockDuration * 1000;
}
set blockDuration(value) {
this._blockDuration = value ? parseInt(value) : 0;
if (this.blockDuration > 0 && this.blockOnPointsConsumed === 0) {
throw new Error('blockOnPointsConsumed option must be set up');
}
}
get inMemoryLimiter() {

@@ -97,2 +133,10 @@ return this._rateLimiterMemory;

const rlKey = RateLimiterInterface.getKey(key);
if (this.blockOnPointsConsumed > 0) {
const msBeforeBlockExpires = this._blockedKeys.msBeforeExpire(rlKey);
if (msBeforeBlockExpires > 0) {
return reject(new RateLimiterRes(0, msBeforeBlockExpires));
}
}
this.redis.multi()

@@ -137,2 +181,6 @@ .set(rlKey, 0, 'EX', this.duration, 'NX')

}
reset(key) {
}
}

@@ -139,0 +187,0 @@

86

lib/RateLimiterRedis.test.js

@@ -7,6 +7,6 @@ const expect = require('chai').expect;

this.timeout(5000);
let redisMockClient;
const redisMockClient = redisMock.createClient();
beforeEach(() => {
redisMockClient = redisMock.createClient();
beforeEach((done) => {
redisMockClient.flushall(done);
});

@@ -117,2 +117,82 @@

});
it('block key when block options set up', (done) => {
const testKey = 'block';
const rateLimiter = new RateLimiterRedis({
redis: redisMockClient,
points: 1,
duration: 5,
blockOnPointsConsumed: 2,
blockDuration: 10
});
rateLimiter.consume(testKey)
.then(() => {
rateLimiter.consume(testKey)
.then(() => {
})
.catch((rejRes) => {
// msBeforeNext more than 5000, so key was blocked
expect(rejRes.msBeforeNext > 5000 && rejRes.remainingPoints === 0).to.equal(true);
done();
});
})
.catch((rejRes) => {
done(rejRes);
});
});
it('expire blocked key', (done) => {
const testKey = 'block';
const rateLimiter = new RateLimiterRedis({
redis: redisMockClient,
points: 1,
duration: 1,
blockOnPointsConsumed: 2,
blockDuration: 2,
});
// It blocks on the first consume as consumed points more than available
rateLimiter.consume(testKey, 2)
.then(() => {
})
.catch((rejRes) => {
setTimeout(() => {
rateLimiter.consume(testKey)
.then((res) => {
// Block expired
expect(res.msBeforeNext <= 1000 && res.remainingPoints === 0).to.equal(true);
done();
})
.catch((rejRes) => {
done(rejRes);
});
}, 2001);
});
});
it('throws error when blockOnPointsConsumed is not set, but blockDuration is set', (done) => {
try {
const rateLimiter = new RateLimiterRedis({
redis: redisMockClient,
blockDuration: 2,
});
} catch (err) {
expect(err instanceof Error).to.equal(true);
done();
}
});
it('throws error when blockOnPointsConsumed less or equal to points', (done) => {
try {
const rateLimiter = new RateLimiterRedis({
redis: redisMockClient,
points: 2,
blockOnPointsConsumed: 2,
});
} catch (err) {
expect(err instanceof Error).to.equal(true);
done();
}
});
});

2

package.json
{
"name": "rate-limiter-flexible",
"version": "0.3.0",
"version": "0.4.0",
"description": "Flexible API rate limiter backed by Redis for distributed node.js applications",

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

[![Build Status](https://travis-ci.org/animir/node-rate-limiter-flexible.png)](https://travis-ci.org/animir/node-rate-limiter-flexible)
[![Coverage Status](https://coveralls.io/repos/animir/node-rate-limiter-flexible/badge.svg?branch=master)](https://coveralls.io/r/animir/node-rate-limiter-flexible?branch=master)
[![node version][node-image]][node-url]
[node-image]: https://img.shields.io/badge/node.js-%3E=_6.0-green.svg?style=flat-square
[node-url]: http://nodejs.org/download/
## node-rate-limiter-flexible
Flexible rate limiter with Redis as broker allows to control requests rate in cluster or distributed environment.
Flexible rate limiter and DDoS protector with Redis as broker allows to control requests rate in cluster or distributed environment.
It uses fixed window to limit requests.
Advantages:
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)
* backed on native Promises

@@ -52,2 +57,3 @@ * actions can be done evenly over duration window to cut off picks

Note: Performance will be much better on real servers, as for this benchmark everything was launched on one machine

@@ -80,2 +86,4 @@ ## Installation

execEvenly: false,
blockOnPointsConsumed: 10, // If 10 points consumed in current duration
blockDuration: 30, // block for 30 seconds in current process memory
inMemoryLimiter: new RateLimiterMemory( // It will be used only on Redis error as insurance

@@ -135,3 +143,5 @@ {

* `points` `Default: 4` Maximum number of points can be consumed over duration
* `duration` `Default: 1` Number of seconds before points are reset
* `duration` `Default: 1` Number of seconds before points are reset
* `execEvenly` `Default: false` Delay action to be executed evenly over duration

@@ -142,6 +152,16 @@ First action in duration is executed without delay.

Note: it isn't recommended to use it for long duration, as it may delay action for too long
* `inMemoryLimiter` `Default: undefined` RateLimiterMemory object to store limits in process memory, when Redis comes up with any error.
* `blockOnPointsConsumed` `Default: 0` Against DDoS attacks. Blocked key isn't checked by requesting Redis.
Blocking works in **current process memory**.
Redis is quite fast, however, it may be significantly slowed down on dozens of thousands requests.
* `blockDuration` `Default: 0` Block key for `blockDuration` seconds,
if `blockOnPointsConsumed` or more points are consumed
* `inMemoryLimiter` `Default: undefined` RateLimiterMemory object to store limits in process memory,
when Redis comes up with any error.
Be careful when use it in cluster or in distributed app.
It may result to floating number of allowed actions.
If an action with a same `key` is launched on one worker several times in sequence, limiter will reach out of points soon.
If an action with a same `key` is launched on one worker several times in sequence,
limiter will reach out of points soon.
Omit it if you want strictly use Redis and deal with errors from it

@@ -169,2 +189,3 @@

* rejected when there is no points to be consumed, where reject reason `rejRes` is `RateLimiterRes` object
* rejected when key is blocked (if block strategy is set up), where reject reason `rejRes` is `RateLimiterRes` object

@@ -171,0 +192,0 @@ Arguments:

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc