1 | 'use strict';
|
2 |
|
3 | var assert = require('assert-plus');
|
4 | var once = require('once');
|
5 | var customErrorTypes = require('./errorTypes');
|
6 |
|
7 | module.exports = Chain;
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | function Chain(options) {
|
32 | assert.optionalObject(options, 'options');
|
33 | options = options || {};
|
34 | assert.optionalBool(options.onceNext, 'options.onceNext');
|
35 | assert.optionalBool(options.strictNext, 'options.strictNext');
|
36 |
|
37 | this.onceNext = !!options.onceNext;
|
38 | this.strictNext = !!options.strictNext;
|
39 |
|
40 |
|
41 | if (this.strictNext) {
|
42 | this.onceNext = true;
|
43 | }
|
44 |
|
45 | this._stack = [];
|
46 | this._once = this.strictNext === false ? once : once.strict;
|
47 | }
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 | Chain.prototype.getHandlers = function getHandlers() {
|
62 | return this._stack;
|
63 | };
|
64 |
|
65 |
|
66 |
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 | Chain.prototype.add = function add(handler) {
|
75 | assert.func(handler);
|
76 | var handlerId = handler._identifier || handler._name || handler.name;
|
77 | if (handler.length <= 2) {
|
78 |
|
79 | assert.equal(
|
80 | handler.constructor.name,
|
81 | 'AsyncFunction',
|
82 | `Handler [${handlerId}] is missing a third argument (the ` +
|
83 | '"next" callback) but is not an async function. Middleware ' +
|
84 | 'handlers can be either async/await or callback-based.' +
|
85 | 'Callback-based (non-async) handlers should accept three ' +
|
86 | 'arguments: (req, res, next). Async handler functions should ' +
|
87 | 'accept maximum of 2 arguments: (req, res).'
|
88 | );
|
89 | } else {
|
90 |
|
91 | assert.notEqual(
|
92 | handler.constructor.name,
|
93 | 'AsyncFunction',
|
94 | `Handler [${handlerId}] accepts a third argument (the 'next" ` +
|
95 | 'callback) but is also an async function. Middleware ' +
|
96 | 'handlers can be either async/await or callback-based. Async ' +
|
97 | 'handler functions should accept maximum of 2 arguments: ' +
|
98 | '(req, res). Non-async handlers should accept three ' +
|
99 | 'arguments: (req, res, next).'
|
100 | );
|
101 | }
|
102 |
|
103 |
|
104 | handler._name = handler._name || handler.name;
|
105 |
|
106 |
|
107 | this._stack.push(handler);
|
108 | };
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 | Chain.prototype.count = function count() {
|
119 | return this._stack.length;
|
120 | };
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 | Chain.prototype.run = function run(req, res, done) {
|
135 | var self = this;
|
136 | var index = 0;
|
137 |
|
138 | function next(err) {
|
139 |
|
140 | var handler = self._stack[index++];
|
141 |
|
142 |
|
143 | if (!handler || req.connectionState() === 'close') {
|
144 | process.nextTick(function nextTick() {
|
145 | return done(err, req, res);
|
146 | });
|
147 | return;
|
148 | }
|
149 |
|
150 |
|
151 | call(handler, err, req, res, self.onceNext ? self._once(next) : next);
|
152 | }
|
153 |
|
154 | next();
|
155 | return;
|
156 | };
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 | function call(handler, err, req, res, _next) {
|
175 | var arity = handler.length;
|
176 | var hasError = err === false || Boolean(err);
|
177 |
|
178 |
|
179 |
|
180 | req._currentHandler = handler._name;
|
181 | req.startHandlerTimer(handler._name);
|
182 |
|
183 | function next(nextErr) {
|
184 | req.endHandlerTimer(handler._name);
|
185 | _next(nextErr, req, res);
|
186 | }
|
187 |
|
188 | function resolve(value) {
|
189 | if (value && req.log) {
|
190 |
|
191 | req.log.warn(
|
192 | { value },
|
193 | 'Discarded returned value from async handler'
|
194 | );
|
195 | }
|
196 |
|
197 | return next();
|
198 | }
|
199 |
|
200 | function reject(error) {
|
201 | if (!(error instanceof Error)) {
|
202 | error = new customErrorTypes.AsyncError(
|
203 | {
|
204 | info: {
|
205 | cause: error,
|
206 | handler: handler._name,
|
207 | method: req.method,
|
208 | path: req.path ? req.path() : undefined
|
209 | }
|
210 | },
|
211 | 'Async middleware rejected without an error'
|
212 | );
|
213 | }
|
214 | return next(error);
|
215 | }
|
216 |
|
217 | if (hasError && arity === 4) {
|
218 |
|
219 | handler(err, req, res, next);
|
220 | return;
|
221 | } else if (!hasError && arity < 4) {
|
222 |
|
223 | process.nextTick(function nextTick() {
|
224 | const result = handler(req, res, next);
|
225 | if (result && typeof result.then === 'function') {
|
226 | result.then(resolve, reject);
|
227 | }
|
228 | });
|
229 | return;
|
230 | }
|
231 |
|
232 |
|
233 | next(err);
|
234 | }
|