UNPKG

4.16 kBJavaScriptView Raw
1'use strict';
2
3var assert = require('assert-plus');
4var once = require('once');
5
6module.exports = Chain;
7
8/**
9 * Create a new middleware chain
10 *
11 * @public
12 * @class Chain
13 * @param {Object} [options] - options
14 * @param {Boolean} [options.onceNext=false] - Prevents calling next multiple
15 * times
16 * @param {Boolean} [options.strictNext=false] - Throws error when next() is
17 * called more than once, enables onceNext option
18 * @example
19 * var chain = new Chain();
20 * chain.add(function (req, res, next) { next(); })
21 * // chain.add(function (req, res, next) { next(new Error('Foo')); })
22 * // chain.add(function (req, res, next) { next(false); })
23 *
24 * http.createServer((req, res) => {
25 * chain.run(req, res, function done(err) {
26 * res.end(err ? err.message : 'hello world');
27 * });
28 * })
29 */
30function Chain(options) {
31 assert.optionalObject(options, 'options');
32 options = options || {};
33 assert.optionalBool(options.onceNext, 'options.onceNext');
34 assert.optionalBool(options.strictNext, 'options.strictNext');
35
36 this.onceNext = !!options.onceNext;
37 this.strictNext = !!options.strictNext;
38
39 // strictNext next enforces onceNext
40 if (this.strictNext) {
41 this.onceNext = true;
42 }
43
44 this._stack = [];
45 this._once = this.strictNext === false ? once : once.strict;
46}
47
48/**
49 * Public methods.
50 * @private
51 */
52
53/**
54 * Get handlers of a chain instance
55 *
56 * @memberof Chain
57 * @instance
58 * @returns {Function[]} handlers
59 */
60Chain.prototype.getHandlers = function getHandlers() {
61 return this._stack;
62};
63
64/**
65 * Utilize the given middleware `handler`
66 *
67 * @public
68 * @memberof Chain
69 * @instance
70 * @param {Function} handler - handler
71 * @returns {undefined} no return value
72 */
73Chain.prototype.add = function add(handler) {
74 // _name is assigned in the server and router
75 handler._name = handler._name || handler.name;
76
77 // add the middleware
78 this._stack.push(handler);
79};
80
81/**
82 * Returns the number of handlers
83 *
84 * @public
85 * @memberof Chain
86 * @instance
87 * @returns {Number} number of handlers in the stack
88 */
89Chain.prototype.count = function count() {
90 return this._stack.length;
91};
92
93/**
94 * Handle server requests, punting them down
95 * the middleware stack.
96 *
97 * @public
98 * @memberof Chain
99 * @instance
100 * @param {Request} req - request
101 * @param {Response} res - response
102 * @param {Function} done - final handler
103 * @returns {undefined} no return value
104 */
105Chain.prototype.run = function run(req, res, done) {
106 var self = this;
107 var index = 0;
108
109 function next(err) {
110 // next callback
111 var handler = self._stack[index++];
112
113 // all done or request closed
114 if (!handler || req.closed()) {
115 process.nextTick(function nextTick() {
116 return done(err, req, res);
117 });
118 return;
119 }
120
121 // call the handler
122 call(handler, err, req, res, self.onceNext ? self._once(next) : next);
123 }
124
125 next();
126 return;
127};
128
129/**
130 * Helper functions
131 * @private
132 */
133
134/**
135 * Invoke a handler.
136 *
137 * @private
138 * @param {Function} handler - handler function
139 * @param {Error|false|*} err - error, abort when true value or false
140 * @param {Request} req - request
141 * @param {Response} res - response
142 * @param {Function} _next - next handler
143 * @returns {undefined} no return value
144 */
145function call(handler, err, req, res, _next) {
146 var arity = handler.length;
147 var error = err;
148 var hasError = err === false || Boolean(err);
149
150 // Meassure handler timings
151 // _name is assigned in the server and router
152 req._currentHandler = handler._name;
153 req.startHandlerTimer(handler._name);
154
155 function next(nextErr) {
156 req.endHandlerTimer(handler._name);
157 _next(nextErr, req, res);
158 }
159
160 if (hasError && arity === 4) {
161 // error-handling middleware
162 handler(err, req, res, next);
163 return;
164 } else if (!hasError && arity < 4) {
165 // request-handling middleware
166 process.nextTick(function nextTick() {
167 handler(req, res, next);
168 });
169 return;
170 }
171
172 // continue
173 next(error, req, res);
174 return;
175}