c9-core/node_modules/c9/ratelimit.js

61 wiersze
2.0 KiB
JavaScript

var error = require("http-error");
var MAX_EXPIRE_INTERVAL = 5000;
/**
* In memory rate limiter as connect middleware
*/
module.exports = ratelimit;
function ratelimit(key, duration, max) {
var requests = Object.create(null); // in case there handles like 'constructor'
var rootKey = "params";
if (/^req\./.test(key)) {
rootKey = null;
key = key.replace(/^req\./, "");
}
setInterval(function() {
Object.keys(requests).forEach(expireRequests);
}, Math.min(duration * 0.75, MAX_EXPIRE_INTERVAL));
function expireRequests(handle) {
var requestsForHandle = requests[handle];
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 (totalToSplice = 0; totalToSplice < requestsForHandle.length; totalToSplice++) {
if (requestsForHandle[totalToSplice] >= expireTime) break;
}
requests[handle].splice(0, totalToSplice);
if (requests[handle].length == 0) {
delete requests[handle];
}
return true;
}
// Returns a deep value from an object. E.g. resolveValue({user: {id: 5}}, "user.id") === 5
function resolveValue(obj, path) {
return path.split('.').reduce(function(prev, curr) {
return prev ? prev[curr] : undefined;
}, obj);
}
return function(req, res, next) {
var root = rootKey ? req[rootKey] : req;
var handle = resolveValue(root, key);
requests[handle] = requests[handle] || [];
if (requests[handle].length >= max) {
var err = new error.TooManyRequests("Rate limit exceeded");
err.retryIn = Math.min(duration, 5000);
return next(err);
}
requests[handle].push(Date.now());
return next();
};
}