mirror of
https://github.com/scratchfoundation/node-redis-rate-limiter.git
synced 2024-12-02 20:17:03 -05:00
154 lines
3.9 KiB
JavaScript
154 lines
3.9 KiB
JavaScript
var _ = require('lodash');
|
|
var async = require('async');
|
|
var should = require('should');
|
|
var redis = require('redis');
|
|
var express = require('express');
|
|
var supertest = require('supertest');
|
|
var middleware = require('../lib/middleware');
|
|
|
|
describe('Middleware', function() {
|
|
|
|
this.slow(5000);
|
|
this.timeout(5000);
|
|
|
|
var client = null;
|
|
var limiter = null;
|
|
|
|
before(function(done) {
|
|
client = redis.createClient(6379, 'localhost', {enable_offline_queue: false});
|
|
client.on('ready', done);
|
|
});
|
|
|
|
describe('IP throttling', function() {
|
|
|
|
before(function() {
|
|
limiter = middleware({
|
|
redis: client,
|
|
key: 'ip',
|
|
rate: '10/second'
|
|
});
|
|
});
|
|
|
|
beforeEach(function(done) {
|
|
client.del('ratelimit:127.0.0.1', done);
|
|
});
|
|
|
|
it('passes through under the limit', function(done) {
|
|
var server = express();
|
|
server.use(limiter);
|
|
server.use(okResponse);
|
|
var reqs = requests(server, 9, '/test');
|
|
async.parallel(reqs, function(err, data) {
|
|
withStatus(data, 200).should.have.length(9);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('returns HTTP 429 over the limit', function(done) {
|
|
var server = express();
|
|
server.use(limiter);
|
|
server.use(okResponse);
|
|
var reqs = requests(server, 12, '/test');
|
|
async.parallel(reqs, function(err, data) {
|
|
withStatus(data, 200).should.have.length(10);
|
|
withStatus(data, 429).should.have.length(2);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('works across several rate-limit windows', function(done) {
|
|
var server = express();
|
|
server.use(limiter);
|
|
server.use(okResponse);
|
|
async.series([
|
|
parallelRequests(server, 9, '/test'),
|
|
wait(1100),
|
|
parallelRequests(server, 12, '/test'),
|
|
wait(1100),
|
|
parallelRequests(server, 9, '/test')
|
|
], function(err, data) {
|
|
withStatus(data[0], 200).should.have.length(9);
|
|
withStatus(data[2], 200).should.have.length(10);
|
|
withStatus(data[2], 429).should.have.length(2);
|
|
withStatus(data[4], 200).should.have.length(9);
|
|
done();
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
describe('Custom key throttling', function() {
|
|
|
|
before(function() {
|
|
limiter = middleware({
|
|
redis: client,
|
|
key: function(req) { return req.query.user; },
|
|
rate: '10/second'
|
|
});
|
|
});
|
|
|
|
beforeEach(function(done) {
|
|
async.series([
|
|
client.del.bind(client, 'ratelimit:a'),
|
|
client.del.bind(client, 'ratelimit:b'),
|
|
client.del.bind(client, 'ratelimit:c')
|
|
], done);
|
|
});
|
|
|
|
it('uses a different bucket for each custom key (user)', function(done) {
|
|
var server = express();
|
|
server.use(limiter);
|
|
server.use(okResponse);
|
|
var reqs = _.flatten([
|
|
requests(server, 5, '/test?user=a'),
|
|
requests(server, 12, '/test?user=b'),
|
|
requests(server, 10, '/test?user=c')
|
|
]);
|
|
async.parallel(reqs, function(err, data) {
|
|
withStatus(data, 200).should.have.length(25);
|
|
withStatus(data, 429).should.have.length(2);
|
|
withStatus(data, 429)[0].url.should.eql('/test?user=b');
|
|
withStatus(data, 429)[1].url.should.eql('/test?user=b');
|
|
done();
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
function requests(server, count, url) {
|
|
return _.times(count, function() {
|
|
return function(next) {
|
|
supertest(server).get(url).end(next);
|
|
};
|
|
});
|
|
}
|
|
|
|
function parallelRequests(server, count, url) {
|
|
return function(next) {
|
|
async.parallel(requests(server, count, url), next);
|
|
};
|
|
}
|
|
|
|
function wait(millis) {
|
|
return function(next) {
|
|
setTimeout(next, 1100);
|
|
};
|
|
}
|
|
|
|
function okResponse(req, res, next) {
|
|
res.writeHead(200);
|
|
res.end('ok');
|
|
}
|
|
|
|
function withStatus(data, code) {
|
|
var pretty = data.map(function(d) {
|
|
return {
|
|
url: d.req.path,
|
|
statusCode: d.res.statusCode,
|
|
body: d.res.body
|
|
}
|
|
});
|
|
return _.filter(pretty, {statusCode: code});
|
|
}
|