all files / esecurity/lib/middleware/ rate.js

100% Statements 35/35
96.43% Branches 27/28
100% Functions 2/2
100% Lines 35/35
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65          12×     11×     11×   10× 10×   10×   10×   10×   10×   10×     10×     10×               10×        
 
var utils = require('../utils');
 
module.exports = function RateConstructor(opts) {
 
    opts = opts || {};
    opts.rate = parseInt(opts.rate) || 100;
    opts.window = parseInt(opts.window) || 900;
    opts.enableHeaders = opts.enableHeaders || false;
    
    return function rate(req, res, next) {
        
        if (!req.session) {
            console.log('esecurity.rate: Session support is needed');
            return next();
        }
        
        var keyZone = req.route.path + '::' + req.route.method;
        
        // self-awareness
        if (req._esecurity_rate && req._esecurity_rate[keyZone])
            return next();
 
        Eif (!req._esecurity_rate)
            req._esecurity_rate = {};
        
        req._esecurity_rate[keyZone] = true;
        
        if (!req.session._esecurity_rate)
            req.session._esecurity_rate = {};
        
        var rateLimitReached = true, currentTime = parseInt(Date.now() / 1E3);
        
        if (!req.session._esecurity_rate[keyZone])  
            req.session._esecurity_rate[keyZone] = { t: currentTime, v: 0 };
 
        if (currentTime - req.session._esecurity_rate[keyZone].t > opts.window) {
            req.session._esecurity_rate[keyZone].t = currentTime;
            req.session._esecurity_rate[keyZone].v = 0;
            rateLimitReached = false;
        }
        
        if (req.session._esecurity_rate[keyZone].v < opts.rate) {
            ++req.session._esecurity_rate[keyZone].v;
            rateLimitReached = false;
        }
        
        if (opts.enableHeaders) {
            // the rate limit ceiling for that given request
            res.set('X-Rate-Limit-Limit', Math.max(0, opts.rate));
            
            // the number of requests left for the N minute window
            res.set('X-Rate-Limit-Remaining', Math.max(0, opts.rate - req.session._esecurity_rate[keyZone].v));
            
            // the remaining window before the rate limit resets in UTC epoch seconds
            res.set('X-Rate-Limit-Reset', Math.max(0, req.session._esecurity_rate[keyZone].t + opts.window));
        }
        
        if (rateLimitReached)
            return next(utils.error(429, '{"errors": [{"code": 88, "message": "Rate limit exceeded"}]}'));
        
        return next();
    };
};