1 | ;
|
2 |
|
3 | var assert = require('assert-plus');
|
4 | var once = require('once');
|
5 |
|
6 | module.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 | */
|
30 | function 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 | */
|
60 | Chain.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 | */
|
73 | Chain.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 | */
|
89 | Chain.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 | */
|
105 | Chain.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 | */
|
145 | function 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 | }
|