kopia lustrzana https://github.com/c9/core
54 wiersze
1.8 KiB
JavaScript
54 wiersze
1.8 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'
|
|
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 handle = resolveValue(req.params, 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();
|
|
};
|
|
}
|