2015-02-10 19:41:24 +00:00
|
|
|
var error = require("http-error");
|
2016-05-12 17:19:31 +00:00
|
|
|
var _ = require("lodash");
|
2015-02-10 19:41:24 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* In memory rate limiter as connect middleware
|
|
|
|
*/
|
|
|
|
module.exports = ratelimit;
|
|
|
|
|
|
|
|
function ratelimit(key, duration, max) {
|
2016-05-12 17:19:31 +00:00
|
|
|
var requests = {};
|
|
|
|
setInterval(function() {
|
|
|
|
_.forIn(requests, function (requestsForHandle, key) {
|
|
|
|
var totalToSplice = 0;
|
|
|
|
var expireTime = Date.now() - duration;
|
|
|
|
/* Requests are already sorted by date as they are appended, so we just loop
|
|
|
|
until we find one that shouldn't have expired and splice them from the list */
|
|
|
|
for (var i = 0; i < requestsForHandle.length; i++) {
|
|
|
|
if (requestsForHandle[i] >= expireTime) break;
|
|
|
|
totalToSplice++;
|
|
|
|
}
|
|
|
|
requests[key].splice(0, totalToSplice);
|
|
|
|
if (requests[key].length == 0) {
|
|
|
|
delete requests[key];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}, 10);
|
2015-02-10 19:41:24 +00:00
|
|
|
return function(req, res, next) {
|
|
|
|
var handle = req.params[key];
|
|
|
|
|
2016-05-12 17:19:31 +00:00
|
|
|
requests[handle] = requests[handle] || [];
|
|
|
|
if (requests[handle].length >= max) {
|
2015-02-10 19:41:24 +00:00
|
|
|
var err = new error.TooManyRequests("Rate limit exceeded");
|
2016-04-28 10:10:39 +00:00
|
|
|
err.retryIn = Math.min(duration, 5000);
|
|
|
|
return next(err);
|
2015-02-10 19:41:24 +00:00
|
|
|
}
|
2016-04-28 10:10:39 +00:00
|
|
|
|
2016-05-12 17:19:31 +00:00
|
|
|
requests[handle].push(Date.now());
|
2015-02-10 19:41:24 +00:00
|
|
|
return next();
|
|
|
|
};
|
|
|
|
}
|