UNPKG

express-concurrency-limit

Version:
176 lines (147 loc) 3.26 kB
/** * @file concurrency limit (limit request concurrency process) * @author liulangyu(liulangyu90316@gmail.com) * @date 2016-06-15 */ var events = require('events'); var util = require('util'); /** * validate conf * * @inner * @param {Array} rules limit rules * @return {boolean} */ function valiConf(rules) { return Array.isArray(rules) && rules.length && rules[0].hasOwnProperty('path') && rules[0].hasOwnProperty('limit'); } /** * concurrency limit * * @constructor * @extends EventEmitter */ function ConLimit() { events.EventEmitter.call(this); } util.inherits(ConLimit, events.EventEmitter); var proto = ConLimit.prototype; /** * add concurrency limit rules * * @public * @param {Array} rules limit rules * @return {ConLimit} */ proto.addRules = function (rules) { if (!valiConf(rules)) { throw new Error('rules conf wrong~'); } var self = this; rules = rules.slice(0); this.queue = []; rules.forEach(function (v) { v.pending = []; v.current = 0; self.queue.push(v); }); return this; }; /** * get rule object index by request path * * @private * @param {string} path request path * @return {number} index */ proto.getRuleIndexByPath = function (path) { var index = -1; this.queue.forEach(function (item, i) { if (new RegExp('^' + item.path, 'ig').test(path)) { index = i; return false; } }); return index; }; /** * check if this request match rules * * @private * @param {string} path request path * @return {boolean} */ proto.isInRules = function (path) { return ~this.getRuleIndexByPath(path); }; /** * get rule object by request path * * @private * @param {string} path request path * @return {Object} */ proto.getRuleByPath = function (path) { return this.queue[this.getRuleIndexByPath(path)]; }; /** * start resuest handle * * @public * @return {Function} */ proto.start = function () { var self = this; /** * return express middleware * * @param {Request} req request * @param {Response} res response * @param {*} next next handler */ return function (req, res, next) { var path = req.path; if (!self.isInRules(path)) { next(); return; } // add listeners for each response res.on('finish', function () { self.nextPending(path); }); res.on('close', function () { self.nextPending(path); }); var rule = self.getRuleByPath(path); if (rule.current >= rule.limit) { rule.pending.push({ req: req, res: res, next: next }); } else { next(); rule.current++; } }; }; /** * exec next pending in queue * * @private * @param {string} path request path */ proto.nextPending = function (path) { var rule = this.getRuleByPath(path); rule.current && rule.current--; var pending = rule.pending; if (pending.length) { pending.shift().next(); rule.current++; } }; module.exports = new ConLimit();