@pirxpilot/router 1.1.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -1
- package/index.js +241 -290
- package/lib/layer.js +38 -154
- package/lib/matcher.js +147 -0
- package/lib/route.js +58 -88
- package/package.json +7 -10
- package/HISTORY.md +0 -217
- package/SECURITY.md +0 -24
package/index.js
CHANGED
|
@@ -1,38 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* MIT Licensed
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
'use strict'
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Module dependencies.
|
|
12
|
-
* @private
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const { METHODS } = require('node:http')
|
|
16
|
-
const Layer = require('./lib/layer')
|
|
17
|
-
const parseUrl = require('parseurl')
|
|
18
|
-
const Route = require('./lib/route')
|
|
1
|
+
import { METHODS } from 'node:http';
|
|
2
|
+
import parseUrl from 'parseurl';
|
|
3
|
+
import Layer from './lib/layer.js';
|
|
4
|
+
import Route from './lib/route.js';
|
|
19
5
|
|
|
20
|
-
|
|
21
|
-
* Module variables.
|
|
22
|
-
* @private
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Expose `Router`.
|
|
27
|
-
*/
|
|
6
|
+
export { Route };
|
|
28
7
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Expose `Route`.
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
module.exports.Route = Route
|
|
8
|
+
Router.Route = Route;
|
|
36
9
|
|
|
37
10
|
/**
|
|
38
11
|
* Initialize a new `Router` with the given `options`.
|
|
@@ -42,35 +15,32 @@ module.exports.Route = Route
|
|
|
42
15
|
* @public
|
|
43
16
|
*/
|
|
44
17
|
|
|
45
|
-
function Router
|
|
18
|
+
export default function Router(options = {}) {
|
|
46
19
|
if (!(this instanceof Router)) {
|
|
47
|
-
return new Router(options)
|
|
20
|
+
return new Router(options);
|
|
48
21
|
}
|
|
49
22
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
function router (req, res, next) {
|
|
53
|
-
router.handle(req, res, next)
|
|
23
|
+
function router(req, res, next) {
|
|
24
|
+
router.handle(req, res, next);
|
|
54
25
|
}
|
|
55
26
|
|
|
56
27
|
// inherit from the correct prototype
|
|
57
|
-
Object.setPrototypeOf(router, this)
|
|
28
|
+
Object.setPrototypeOf(router, this);
|
|
58
29
|
|
|
59
|
-
router.caseSensitive =
|
|
60
|
-
router.mergeParams =
|
|
61
|
-
router.params = {}
|
|
62
|
-
router.strict =
|
|
63
|
-
router.stack = []
|
|
30
|
+
router.caseSensitive = options.caseSensitive;
|
|
31
|
+
router.mergeParams = options.mergeParams;
|
|
32
|
+
router.params = {};
|
|
33
|
+
router.strict = options.strict;
|
|
34
|
+
router.stack = [];
|
|
64
35
|
|
|
65
|
-
return router
|
|
36
|
+
return router;
|
|
66
37
|
}
|
|
67
38
|
|
|
68
39
|
/**
|
|
69
40
|
* Router prototype inherits from a Function.
|
|
70
41
|
*/
|
|
71
42
|
|
|
72
|
-
|
|
73
|
-
Router.prototype = function () {}
|
|
43
|
+
Router.prototype = () => {};
|
|
74
44
|
|
|
75
45
|
/**
|
|
76
46
|
* Map the given param placeholder `name`(s) to the given callback.
|
|
@@ -105,233 +75,225 @@ Router.prototype = function () {}
|
|
|
105
75
|
* @public
|
|
106
76
|
*/
|
|
107
77
|
|
|
108
|
-
Router.prototype.param = function param
|
|
78
|
+
Router.prototype.param = function param(name, fn) {
|
|
109
79
|
if (!name) {
|
|
110
|
-
throw new TypeError('argument name is required')
|
|
80
|
+
throw new TypeError('argument name is required');
|
|
111
81
|
}
|
|
112
82
|
|
|
113
83
|
if (typeof name !== 'string') {
|
|
114
|
-
throw new TypeError('argument name must be a string')
|
|
84
|
+
throw new TypeError('argument name must be a string');
|
|
115
85
|
}
|
|
116
86
|
|
|
117
87
|
if (!fn) {
|
|
118
|
-
throw new TypeError('argument fn is required')
|
|
88
|
+
throw new TypeError('argument fn is required');
|
|
119
89
|
}
|
|
120
90
|
|
|
121
91
|
if (typeof fn !== 'function') {
|
|
122
|
-
throw new TypeError('argument fn must be a function')
|
|
92
|
+
throw new TypeError('argument fn must be a function');
|
|
123
93
|
}
|
|
124
94
|
|
|
125
|
-
let params = this.params[name]
|
|
95
|
+
let params = this.params[name];
|
|
126
96
|
|
|
127
97
|
if (!params) {
|
|
128
|
-
params = this.params[name] = []
|
|
98
|
+
params = this.params[name] = [];
|
|
129
99
|
}
|
|
130
100
|
|
|
131
|
-
params.push(fn)
|
|
101
|
+
params.push(fn);
|
|
132
102
|
|
|
133
|
-
return this
|
|
134
|
-
}
|
|
103
|
+
return this;
|
|
104
|
+
};
|
|
135
105
|
|
|
136
106
|
/**
|
|
137
107
|
* Dispatch a req, res into the router.
|
|
138
|
-
*
|
|
139
|
-
* @private
|
|
140
108
|
*/
|
|
141
109
|
|
|
142
|
-
Router.prototype.handle = function handle
|
|
110
|
+
Router.prototype.handle = function handle(req, res, callback) {
|
|
143
111
|
if (!callback) {
|
|
144
|
-
throw new TypeError('argument callback is required')
|
|
112
|
+
throw new TypeError('argument callback is required');
|
|
145
113
|
}
|
|
146
114
|
|
|
147
|
-
let idx = 0
|
|
148
|
-
let methods
|
|
149
|
-
const protohost = getProtohost(req.url) || ''
|
|
150
|
-
let removed = ''
|
|
151
|
-
const self = this
|
|
152
|
-
let slashAdded = false
|
|
153
|
-
let sync = 0
|
|
154
|
-
const paramcalled = {}
|
|
115
|
+
let idx = 0;
|
|
116
|
+
let methods;
|
|
117
|
+
const protohost = getProtohost(req.url) || '';
|
|
118
|
+
let removed = '';
|
|
119
|
+
const self = this;
|
|
120
|
+
let slashAdded = false;
|
|
121
|
+
let sync = 0;
|
|
122
|
+
const paramcalled = {};
|
|
155
123
|
|
|
156
124
|
// middleware and routes
|
|
157
|
-
const stack = this.stack
|
|
125
|
+
const stack = this.stack;
|
|
158
126
|
|
|
159
127
|
// manage inter-router variables
|
|
160
|
-
const parentParams = req.params
|
|
161
|
-
const parentUrl = req.baseUrl || ''
|
|
162
|
-
let done = restore(callback, req, 'baseUrl', 'next', 'params')
|
|
128
|
+
const parentParams = req.params;
|
|
129
|
+
const parentUrl = req.baseUrl || '';
|
|
130
|
+
let done = restore(callback, req, 'baseUrl', 'next', 'params');
|
|
163
131
|
|
|
164
132
|
// setup next layer
|
|
165
|
-
req.next = next
|
|
133
|
+
req.next = next;
|
|
166
134
|
|
|
167
135
|
// for options requests, respond with a default if nothing else responds
|
|
168
136
|
if (req.method === 'OPTIONS') {
|
|
169
|
-
methods = []
|
|
170
|
-
done = wrap(done, generateOptionsResponder(res, methods))
|
|
137
|
+
methods = [];
|
|
138
|
+
done = wrap(done, generateOptionsResponder(res, methods));
|
|
171
139
|
}
|
|
172
140
|
|
|
173
141
|
// setup basic req values
|
|
174
|
-
req.baseUrl = parentUrl
|
|
175
|
-
req.originalUrl = req.originalUrl || req.url
|
|
142
|
+
req.baseUrl = parentUrl;
|
|
143
|
+
req.originalUrl = req.originalUrl || req.url;
|
|
176
144
|
|
|
177
|
-
next()
|
|
145
|
+
next();
|
|
178
146
|
|
|
179
|
-
function next
|
|
180
|
-
let layerError = err === 'route'
|
|
181
|
-
? null
|
|
182
|
-
: err
|
|
147
|
+
function next(err) {
|
|
148
|
+
let layerError = err === 'route' ? null : err;
|
|
183
149
|
|
|
184
150
|
// remove added slash
|
|
185
151
|
if (slashAdded) {
|
|
186
|
-
req.url = req.url.slice(1)
|
|
187
|
-
slashAdded = false
|
|
152
|
+
req.url = req.url.slice(1);
|
|
153
|
+
slashAdded = false;
|
|
188
154
|
}
|
|
189
155
|
|
|
190
156
|
// restore altered req.url
|
|
191
157
|
if (removed.length !== 0) {
|
|
192
|
-
req.baseUrl = parentUrl
|
|
193
|
-
req.url = protohost + removed + req.url.slice(protohost.length)
|
|
194
|
-
removed = ''
|
|
158
|
+
req.baseUrl = parentUrl;
|
|
159
|
+
req.url = protohost + removed + req.url.slice(protohost.length);
|
|
160
|
+
removed = '';
|
|
195
161
|
}
|
|
196
162
|
|
|
197
163
|
// signal to exit router
|
|
198
164
|
if (layerError === 'router') {
|
|
199
|
-
setImmediate(done, null)
|
|
200
|
-
return
|
|
165
|
+
setImmediate(done, null);
|
|
166
|
+
return;
|
|
201
167
|
}
|
|
202
168
|
|
|
203
169
|
// no more matching layers
|
|
204
170
|
if (idx >= stack.length) {
|
|
205
|
-
setImmediate(done, layerError)
|
|
206
|
-
return
|
|
171
|
+
setImmediate(done, layerError);
|
|
172
|
+
return;
|
|
207
173
|
}
|
|
208
174
|
|
|
209
175
|
// max sync stack
|
|
210
176
|
if (++sync > 100) {
|
|
211
|
-
return setImmediate(next, err)
|
|
177
|
+
return setImmediate(next, err);
|
|
212
178
|
}
|
|
213
179
|
|
|
214
180
|
// get pathname of request
|
|
215
|
-
const path = getPathname(req)
|
|
181
|
+
const path = getPathname(req);
|
|
216
182
|
|
|
217
183
|
if (path == null) {
|
|
218
|
-
return done(layerError)
|
|
184
|
+
return done(layerError);
|
|
219
185
|
}
|
|
220
186
|
|
|
221
187
|
// find next matching layer
|
|
222
|
-
let layer
|
|
223
|
-
let match
|
|
224
|
-
let route
|
|
188
|
+
let layer;
|
|
189
|
+
let match;
|
|
190
|
+
let route;
|
|
225
191
|
|
|
226
192
|
while (match !== true && idx < stack.length) {
|
|
227
|
-
layer = stack[idx++]
|
|
228
|
-
match = matchLayer(layer, path)
|
|
229
|
-
route = layer.route
|
|
193
|
+
layer = stack[idx++];
|
|
194
|
+
match = matchLayer(layer, path);
|
|
195
|
+
route = layer.route;
|
|
230
196
|
|
|
231
197
|
if (typeof match !== 'boolean') {
|
|
232
198
|
// hold on to layerError
|
|
233
|
-
layerError = layerError || match
|
|
199
|
+
layerError = layerError || match;
|
|
234
200
|
}
|
|
235
201
|
|
|
236
202
|
if (match !== true) {
|
|
237
|
-
continue
|
|
203
|
+
continue;
|
|
238
204
|
}
|
|
239
205
|
|
|
240
206
|
if (!route) {
|
|
241
207
|
// process non-route handlers normally
|
|
242
|
-
continue
|
|
208
|
+
continue;
|
|
243
209
|
}
|
|
244
210
|
|
|
245
211
|
if (layerError) {
|
|
246
212
|
// routes do not match with a pending error
|
|
247
|
-
match = false
|
|
248
|
-
continue
|
|
213
|
+
match = false;
|
|
214
|
+
continue;
|
|
249
215
|
}
|
|
250
216
|
|
|
251
|
-
const method = req.method
|
|
252
|
-
const hasMethod = route._handlesMethod(method)
|
|
217
|
+
const method = req.method;
|
|
218
|
+
const hasMethod = route._handlesMethod(method);
|
|
253
219
|
|
|
254
220
|
// build up automatic options response
|
|
255
221
|
if (!hasMethod && method === 'OPTIONS' && methods) {
|
|
256
|
-
methods.push.apply(methods, route._methods())
|
|
222
|
+
methods.push.apply(methods, route._methods());
|
|
257
223
|
}
|
|
258
224
|
|
|
259
225
|
// don't even bother matching route
|
|
260
226
|
if (!hasMethod && method !== 'HEAD') {
|
|
261
|
-
match = false
|
|
262
|
-
continue
|
|
227
|
+
match = false;
|
|
263
228
|
}
|
|
264
229
|
}
|
|
265
230
|
|
|
266
231
|
// no match
|
|
267
232
|
if (match !== true) {
|
|
268
|
-
return done(layerError)
|
|
233
|
+
return done(layerError);
|
|
269
234
|
}
|
|
270
235
|
|
|
271
236
|
// store route for dispatch on change
|
|
272
237
|
if (route) {
|
|
273
|
-
req.route = route
|
|
238
|
+
req.route = route;
|
|
274
239
|
}
|
|
275
240
|
|
|
276
241
|
// Capture one-time layer values
|
|
277
|
-
req.params = self.mergeParams
|
|
278
|
-
|
|
279
|
-
: layer.params
|
|
280
|
-
const layerPath = layer.path
|
|
242
|
+
req.params = self.mergeParams ? mergeParams(layer.params, parentParams) : layer.params;
|
|
243
|
+
const layerPath = layer.path;
|
|
281
244
|
|
|
282
245
|
// this should be done for the layer
|
|
283
|
-
processParams(self.params, layer, paramcalled, req, res,
|
|
246
|
+
processParams(self.params, layer, paramcalled, req, res, err => {
|
|
284
247
|
if (err) {
|
|
285
|
-
next(layerError || err)
|
|
248
|
+
next(layerError || err);
|
|
286
249
|
} else if (route) {
|
|
287
|
-
layer.handleRequest(req, res, next)
|
|
250
|
+
layer.handleRequest(req, res, next);
|
|
288
251
|
} else {
|
|
289
|
-
trimPrefix(layer, layerError, layerPath, path)
|
|
252
|
+
trimPrefix(layer, layerError, layerPath, path);
|
|
290
253
|
}
|
|
291
254
|
|
|
292
|
-
sync = 0
|
|
293
|
-
})
|
|
255
|
+
sync = 0;
|
|
256
|
+
});
|
|
294
257
|
}
|
|
295
258
|
|
|
296
|
-
function trimPrefix
|
|
259
|
+
function trimPrefix(layer, layerError, layerPath, path) {
|
|
297
260
|
if (layerPath.length !== 0) {
|
|
298
261
|
// Validate path is a prefix match
|
|
299
262
|
if (layerPath !== path.substring(0, layerPath.length)) {
|
|
300
|
-
next(layerError)
|
|
301
|
-
return
|
|
263
|
+
next(layerError);
|
|
264
|
+
return;
|
|
302
265
|
}
|
|
303
266
|
|
|
304
267
|
// Validate path breaks on a path separator
|
|
305
|
-
const c = path[layerPath.length]
|
|
268
|
+
const c = path[layerPath.length];
|
|
306
269
|
if (c && c !== '/') {
|
|
307
|
-
next(layerError)
|
|
308
|
-
return
|
|
270
|
+
next(layerError);
|
|
271
|
+
return;
|
|
309
272
|
}
|
|
310
273
|
|
|
311
274
|
// Trim off the part of the url that matches the route
|
|
312
275
|
// middleware (.use stuff) needs to have the path stripped
|
|
313
|
-
removed = layerPath
|
|
314
|
-
req.url = protohost + req.url.slice(protohost.length + removed.length)
|
|
276
|
+
removed = layerPath;
|
|
277
|
+
req.url = protohost + req.url.slice(protohost.length + removed.length);
|
|
315
278
|
|
|
316
279
|
// Ensure leading slash
|
|
317
280
|
if (!protohost && req.url[0] !== '/') {
|
|
318
|
-
req.url =
|
|
319
|
-
slashAdded = true
|
|
281
|
+
req.url = `/${req.url}`;
|
|
282
|
+
slashAdded = true;
|
|
320
283
|
}
|
|
321
284
|
|
|
322
285
|
// Setup base URL (no trailing slash)
|
|
323
|
-
req.baseUrl =
|
|
324
|
-
? removed.substring(0, removed.length - 1)
|
|
325
|
-
: removed)
|
|
286
|
+
req.baseUrl =
|
|
287
|
+
parentUrl + (removed[removed.length - 1] === '/' ? removed.substring(0, removed.length - 1) : removed);
|
|
326
288
|
}
|
|
327
289
|
|
|
328
290
|
if (layerError) {
|
|
329
|
-
layer.handleError(layerError, req, res, next)
|
|
291
|
+
layer.handleError(layerError, req, res, next);
|
|
330
292
|
} else {
|
|
331
|
-
layer.handleRequest(req, res, next)
|
|
293
|
+
layer.handleRequest(req, res, next);
|
|
332
294
|
}
|
|
333
295
|
}
|
|
334
|
-
}
|
|
296
|
+
};
|
|
335
297
|
|
|
336
298
|
/**
|
|
337
299
|
* Use the given middleware function, with optional path, defaulting to "/".
|
|
@@ -348,51 +310,57 @@ Router.prototype.handle = function handle (req, res, callback) {
|
|
|
348
310
|
* @public
|
|
349
311
|
*/
|
|
350
312
|
|
|
351
|
-
Router.prototype.use = function use
|
|
352
|
-
let path = '/'
|
|
313
|
+
Router.prototype.use = function use(handler, ...args) {
|
|
314
|
+
let path = '/';
|
|
353
315
|
|
|
354
316
|
// default path to '/'
|
|
355
317
|
// disambiguate router.use([handler])
|
|
356
318
|
if (typeof handler !== 'function') {
|
|
357
|
-
let arg = handler
|
|
319
|
+
let arg = handler;
|
|
358
320
|
|
|
359
321
|
while (Array.isArray(arg) && arg.length !== 0) {
|
|
360
|
-
arg = arg[0]
|
|
322
|
+
arg = arg[0];
|
|
361
323
|
}
|
|
362
324
|
|
|
363
325
|
// first arg is the path
|
|
364
326
|
if (typeof arg !== 'function') {
|
|
365
|
-
path = handler
|
|
366
|
-
handler = undefined
|
|
327
|
+
path = handler;
|
|
328
|
+
handler = undefined;
|
|
367
329
|
}
|
|
368
330
|
}
|
|
369
331
|
if (handler !== undefined) {
|
|
370
|
-
args.unshift(handler)
|
|
332
|
+
args.unshift(handler);
|
|
371
333
|
}
|
|
372
|
-
const callbacks = args.flat(
|
|
334
|
+
const callbacks = args.flat(Number.POSITIVE_INFINITY);
|
|
373
335
|
|
|
374
336
|
if (callbacks.length === 0) {
|
|
375
|
-
throw new TypeError('argument handler is required')
|
|
337
|
+
throw new TypeError('argument handler is required');
|
|
376
338
|
}
|
|
377
339
|
|
|
378
|
-
this.stack.push(
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
// add the middleware
|
|
384
|
-
const layer = new Layer(path, {
|
|
385
|
-
sensitive: this.caseSensitive,
|
|
386
|
-
strict: false,
|
|
387
|
-
end: false
|
|
388
|
-
}, fn)
|
|
340
|
+
this.stack.push(
|
|
341
|
+
...callbacks.map(fn => {
|
|
342
|
+
if (typeof fn !== 'function') {
|
|
343
|
+
throw new TypeError('argument handler must be a function');
|
|
344
|
+
}
|
|
389
345
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
346
|
+
// add the middleware
|
|
347
|
+
const layer = new Layer(
|
|
348
|
+
path,
|
|
349
|
+
{
|
|
350
|
+
sensitive: this.caseSensitive,
|
|
351
|
+
strict: false,
|
|
352
|
+
end: false
|
|
353
|
+
},
|
|
354
|
+
fn
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
layer.route = undefined;
|
|
358
|
+
return layer;
|
|
359
|
+
})
|
|
360
|
+
);
|
|
393
361
|
|
|
394
|
-
return this
|
|
395
|
-
}
|
|
362
|
+
return this;
|
|
363
|
+
};
|
|
396
364
|
|
|
397
365
|
/**
|
|
398
366
|
* Create a new Route for the given path.
|
|
@@ -407,66 +375,68 @@ Router.prototype.use = function use (handler, ...args) {
|
|
|
407
375
|
* @public
|
|
408
376
|
*/
|
|
409
377
|
|
|
410
|
-
Router.prototype.route = function route
|
|
411
|
-
const route = new Route(path)
|
|
378
|
+
Router.prototype.route = function route(path) {
|
|
379
|
+
const route = new Route(path);
|
|
412
380
|
|
|
413
|
-
const layer = new Layer(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
381
|
+
const layer = new Layer(
|
|
382
|
+
path,
|
|
383
|
+
{
|
|
384
|
+
sensitive: this.caseSensitive,
|
|
385
|
+
strict: this.strict,
|
|
386
|
+
end: true
|
|
387
|
+
},
|
|
388
|
+
handle
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
function handle(req, res, next) {
|
|
392
|
+
route.dispatch(req, res, next);
|
|
421
393
|
}
|
|
422
394
|
|
|
423
|
-
layer.route = route
|
|
395
|
+
layer.route = route;
|
|
424
396
|
|
|
425
|
-
this.stack.push(layer)
|
|
426
|
-
return route
|
|
427
|
-
}
|
|
397
|
+
this.stack.push(layer);
|
|
398
|
+
return route;
|
|
399
|
+
};
|
|
428
400
|
|
|
429
401
|
// create Router#VERB functions
|
|
430
402
|
METHODS.concat('all')
|
|
431
403
|
.map(m => m.toLowerCase())
|
|
432
404
|
.forEach(method => {
|
|
433
405
|
Router.prototype[method] = function (path, ...args) {
|
|
434
|
-
const route = this.route(path)
|
|
435
|
-
route[method].apply(route, args)
|
|
436
|
-
return this
|
|
437
|
-
}
|
|
438
|
-
})
|
|
406
|
+
const route = this.route(path);
|
|
407
|
+
route[method].apply(route, args);
|
|
408
|
+
return this;
|
|
409
|
+
};
|
|
410
|
+
});
|
|
439
411
|
|
|
440
412
|
/**
|
|
441
413
|
* Generate a callback that will make an OPTIONS response.
|
|
442
414
|
*
|
|
443
415
|
* @param {OutgoingMessage} res
|
|
444
416
|
* @param {array} methods
|
|
445
|
-
* @private
|
|
446
417
|
*/
|
|
447
418
|
|
|
448
|
-
function generateOptionsResponder
|
|
449
|
-
return function onDone
|
|
419
|
+
function generateOptionsResponder(res, methods) {
|
|
420
|
+
return function onDone(fn, err) {
|
|
450
421
|
if (err || methods.length === 0) {
|
|
451
|
-
return fn(err)
|
|
422
|
+
return fn(err);
|
|
452
423
|
}
|
|
453
424
|
|
|
454
|
-
trySendOptionsResponse(res, methods, fn)
|
|
455
|
-
}
|
|
425
|
+
trySendOptionsResponse(res, methods, fn);
|
|
426
|
+
};
|
|
456
427
|
}
|
|
457
428
|
|
|
458
429
|
/**
|
|
459
430
|
* Get pathname of request.
|
|
460
431
|
*
|
|
461
432
|
* @param {IncomingMessage} req
|
|
462
|
-
* @private
|
|
463
433
|
*/
|
|
464
434
|
|
|
465
|
-
function getPathname
|
|
435
|
+
function getPathname(req) {
|
|
466
436
|
try {
|
|
467
|
-
return parseUrl(req).pathname
|
|
468
|
-
} catch (
|
|
469
|
-
return undefined
|
|
437
|
+
return parseUrl(req).pathname;
|
|
438
|
+
} catch (_err) {
|
|
439
|
+
return undefined;
|
|
470
440
|
}
|
|
471
441
|
}
|
|
472
442
|
|
|
@@ -474,23 +444,18 @@ function getPathname (req) {
|
|
|
474
444
|
* Get get protocol + host for a URL.
|
|
475
445
|
*
|
|
476
446
|
* @param {string} url
|
|
477
|
-
* @private
|
|
478
447
|
*/
|
|
479
448
|
|
|
480
|
-
function getProtohost
|
|
449
|
+
function getProtohost(url) {
|
|
481
450
|
if (typeof url !== 'string' || url.length === 0 || url[0] === '/') {
|
|
482
|
-
return undefined
|
|
451
|
+
return undefined;
|
|
483
452
|
}
|
|
484
453
|
|
|
485
|
-
const searchIndex = url.indexOf('?')
|
|
486
|
-
const pathLength = searchIndex !== -1
|
|
487
|
-
|
|
488
|
-
: url.length
|
|
489
|
-
const fqdnIndex = url.substring(0, pathLength).indexOf('://')
|
|
454
|
+
const searchIndex = url.indexOf('?');
|
|
455
|
+
const pathLength = searchIndex !== -1 ? searchIndex : url.length;
|
|
456
|
+
const fqdnIndex = url.substring(0, pathLength).indexOf('://');
|
|
490
457
|
|
|
491
|
-
return fqdnIndex !== -1
|
|
492
|
-
? url.substring(0, url.indexOf('/', 3 + fqdnIndex))
|
|
493
|
-
: undefined
|
|
458
|
+
return fqdnIndex !== -1 ? url.substring(0, url.indexOf('/', 3 + fqdnIndex)) : undefined;
|
|
494
459
|
}
|
|
495
460
|
|
|
496
461
|
/**
|
|
@@ -498,219 +463,205 @@ function getProtohost (url) {
|
|
|
498
463
|
*
|
|
499
464
|
* @param {Layer} layer
|
|
500
465
|
* @param {string} path
|
|
501
|
-
* @private
|
|
502
466
|
*/
|
|
503
467
|
|
|
504
|
-
function matchLayer
|
|
468
|
+
function matchLayer(layer, path) {
|
|
505
469
|
try {
|
|
506
|
-
return layer.match(path)
|
|
470
|
+
return layer.match(path);
|
|
507
471
|
} catch (err) {
|
|
508
|
-
return err
|
|
472
|
+
return err;
|
|
509
473
|
}
|
|
510
474
|
}
|
|
511
475
|
|
|
512
476
|
/**
|
|
513
477
|
* Merge params with parent params
|
|
514
|
-
*
|
|
515
|
-
* @private
|
|
516
478
|
*/
|
|
517
479
|
|
|
518
|
-
function mergeParams
|
|
480
|
+
function mergeParams(params, parent) {
|
|
519
481
|
if (typeof parent !== 'object' || !parent) {
|
|
520
|
-
return params
|
|
482
|
+
return params;
|
|
521
483
|
}
|
|
522
484
|
|
|
523
485
|
// make copy of parent for base
|
|
524
|
-
const obj = { ...parent }
|
|
486
|
+
const obj = { ...parent };
|
|
525
487
|
|
|
526
488
|
// simple non-numeric merging
|
|
527
489
|
if (!(0 in params) || !(0 in parent)) {
|
|
528
|
-
return Object.assign(obj, params)
|
|
490
|
+
return Object.assign(obj, params);
|
|
529
491
|
}
|
|
530
492
|
|
|
531
|
-
let i = 0
|
|
532
|
-
let o = 0
|
|
493
|
+
let i = 0;
|
|
494
|
+
let o = 0;
|
|
533
495
|
|
|
534
496
|
// determine numeric gap in params
|
|
535
497
|
while (i in params) {
|
|
536
|
-
i
|
|
498
|
+
i++;
|
|
537
499
|
}
|
|
538
500
|
|
|
539
501
|
// determine numeric gap in parent
|
|
540
502
|
while (o in parent) {
|
|
541
|
-
o
|
|
503
|
+
o++;
|
|
542
504
|
}
|
|
543
505
|
|
|
544
506
|
// offset numeric indices in params before merge
|
|
545
507
|
for (i--; i >= 0; i--) {
|
|
546
|
-
params[i + o] = params[i]
|
|
508
|
+
params[i + o] = params[i];
|
|
547
509
|
|
|
548
510
|
// create holes for the merge when necessary
|
|
549
511
|
if (i < o) {
|
|
550
|
-
delete params[i]
|
|
512
|
+
delete params[i];
|
|
551
513
|
}
|
|
552
514
|
}
|
|
553
515
|
|
|
554
|
-
return Object.assign(obj, params)
|
|
516
|
+
return Object.assign(obj, params);
|
|
555
517
|
}
|
|
556
518
|
|
|
557
519
|
/**
|
|
558
520
|
* Process any parameters for the layer.
|
|
559
|
-
*
|
|
560
|
-
* @private
|
|
561
521
|
*/
|
|
562
522
|
|
|
563
|
-
function processParams
|
|
523
|
+
function processParams(params, layer, called, req, res, done) {
|
|
564
524
|
// captured parameters from the layer, keys and values
|
|
565
|
-
const keys = layer.keys
|
|
525
|
+
const keys = layer.keys;
|
|
566
526
|
|
|
567
527
|
// fast track
|
|
568
528
|
if (!keys || keys.length === 0) {
|
|
569
|
-
return done()
|
|
529
|
+
return done();
|
|
570
530
|
}
|
|
571
531
|
|
|
572
|
-
let i = 0
|
|
573
|
-
let paramIndex = 0
|
|
574
|
-
let key
|
|
575
|
-
let paramVal
|
|
576
|
-
let paramCallbacks
|
|
577
|
-
let paramCalled
|
|
532
|
+
let i = 0;
|
|
533
|
+
let paramIndex = 0;
|
|
534
|
+
let key;
|
|
535
|
+
let paramVal;
|
|
536
|
+
let paramCallbacks;
|
|
537
|
+
let paramCalled;
|
|
578
538
|
|
|
579
539
|
// process params in order
|
|
580
540
|
// param callbacks can be async
|
|
581
|
-
function param
|
|
541
|
+
function param(err) {
|
|
582
542
|
if (err) {
|
|
583
|
-
return done(err)
|
|
543
|
+
return done(err);
|
|
584
544
|
}
|
|
585
545
|
|
|
586
546
|
if (i >= keys.length) {
|
|
587
|
-
return done()
|
|
547
|
+
return done();
|
|
588
548
|
}
|
|
589
549
|
|
|
590
|
-
paramIndex = 0
|
|
591
|
-
key = keys[i++]
|
|
592
|
-
paramVal = req.params[key]
|
|
593
|
-
paramCallbacks = params[key]
|
|
594
|
-
paramCalled = called[key]
|
|
550
|
+
paramIndex = 0;
|
|
551
|
+
key = keys[i++];
|
|
552
|
+
paramVal = req.params[key];
|
|
553
|
+
paramCallbacks = params[key];
|
|
554
|
+
paramCalled = called[key];
|
|
595
555
|
|
|
596
556
|
if (paramVal === undefined || !paramCallbacks) {
|
|
597
|
-
return param()
|
|
557
|
+
return param();
|
|
598
558
|
}
|
|
599
559
|
|
|
600
560
|
// param previously called with same value or error occurred
|
|
601
|
-
if (paramCalled && (paramCalled.match === paramVal ||
|
|
602
|
-
(paramCalled.error && paramCalled.error !== 'route'))) {
|
|
561
|
+
if (paramCalled && (paramCalled.match === paramVal || (paramCalled.error && paramCalled.error !== 'route'))) {
|
|
603
562
|
// restore value
|
|
604
|
-
req.params[key] = paramCalled.value
|
|
563
|
+
req.params[key] = paramCalled.value;
|
|
605
564
|
|
|
606
565
|
// next param
|
|
607
|
-
return param(paramCalled.error)
|
|
566
|
+
return param(paramCalled.error);
|
|
608
567
|
}
|
|
609
568
|
|
|
610
569
|
called[key] = paramCalled = {
|
|
611
570
|
error: null,
|
|
612
571
|
match: paramVal,
|
|
613
572
|
value: paramVal
|
|
614
|
-
}
|
|
573
|
+
};
|
|
615
574
|
|
|
616
|
-
paramCallback()
|
|
575
|
+
paramCallback();
|
|
617
576
|
}
|
|
618
577
|
|
|
619
578
|
// single param callbacks
|
|
620
|
-
function paramCallback
|
|
621
|
-
const fn = paramCallbacks[paramIndex++]
|
|
579
|
+
function paramCallback(err) {
|
|
580
|
+
const fn = paramCallbacks[paramIndex++];
|
|
622
581
|
|
|
623
582
|
// store updated value
|
|
624
|
-
paramCalled.value = req.params[key]
|
|
583
|
+
paramCalled.value = req.params[key];
|
|
625
584
|
|
|
626
585
|
if (err) {
|
|
627
586
|
// store error
|
|
628
|
-
paramCalled.error = err
|
|
629
|
-
param(err)
|
|
630
|
-
return
|
|
587
|
+
paramCalled.error = err;
|
|
588
|
+
param(err);
|
|
589
|
+
return;
|
|
631
590
|
}
|
|
632
591
|
|
|
633
|
-
if (!fn) return param()
|
|
592
|
+
if (!fn) return param();
|
|
634
593
|
|
|
635
594
|
try {
|
|
636
|
-
const ret = fn(req, res, paramCallback, paramVal, key)
|
|
595
|
+
const ret = fn(req, res, paramCallback, paramVal, key);
|
|
637
596
|
if (ret instanceof Promise) {
|
|
638
|
-
ret.catch((error = new Error('Rejected promise')) => paramCallback(error))
|
|
597
|
+
ret.catch((error = new Error('Rejected promise')) => paramCallback(error));
|
|
639
598
|
}
|
|
640
599
|
} catch (e) {
|
|
641
|
-
paramCallback(e)
|
|
600
|
+
paramCallback(e);
|
|
642
601
|
}
|
|
643
602
|
}
|
|
644
603
|
|
|
645
|
-
param()
|
|
604
|
+
param();
|
|
646
605
|
}
|
|
647
606
|
|
|
648
607
|
/**
|
|
649
608
|
* Restore obj props after function
|
|
650
|
-
*
|
|
651
|
-
* @private
|
|
652
609
|
*/
|
|
653
610
|
|
|
654
|
-
function restore
|
|
655
|
-
const vals = props.map(prop => obj[prop])
|
|
611
|
+
function restore(fn, obj, ...props) {
|
|
612
|
+
const vals = props.map(prop => obj[prop]);
|
|
656
613
|
|
|
657
|
-
return function () {
|
|
614
|
+
return function (...args) {
|
|
658
615
|
// restore vals
|
|
659
616
|
for (let i = 0; i < props.length; i++) {
|
|
660
|
-
obj[props[i]] = vals[i]
|
|
617
|
+
obj[props[i]] = vals[i];
|
|
661
618
|
}
|
|
662
619
|
|
|
663
|
-
return fn.apply(this,
|
|
664
|
-
}
|
|
620
|
+
return fn.apply(this, args);
|
|
621
|
+
};
|
|
665
622
|
}
|
|
666
623
|
|
|
667
624
|
/**
|
|
668
625
|
* Send an OPTIONS response.
|
|
669
|
-
*
|
|
670
|
-
* @private
|
|
671
626
|
*/
|
|
672
627
|
|
|
673
|
-
function sendOptionsResponse
|
|
674
|
-
const options = Object.create(null)
|
|
628
|
+
function sendOptionsResponse(res, methods) {
|
|
629
|
+
const options = Object.create(null);
|
|
675
630
|
|
|
676
631
|
// build unique method map
|
|
677
632
|
for (let i = 0; i < methods.length; i++) {
|
|
678
|
-
options[methods[i]] = true
|
|
633
|
+
options[methods[i]] = true;
|
|
679
634
|
}
|
|
680
635
|
|
|
681
636
|
// construct the allow list
|
|
682
|
-
const allow = Object.keys(options).sort().join(', ')
|
|
637
|
+
const allow = Object.keys(options).sort().join(', ');
|
|
683
638
|
|
|
684
639
|
// send response
|
|
685
|
-
res.setHeader('Allow', allow)
|
|
686
|
-
res.setHeader('Content-Length', Buffer.byteLength(allow))
|
|
687
|
-
res.setHeader('Content-Type', 'text/plain')
|
|
688
|
-
res.setHeader('X-Content-Type-Options', 'nosniff')
|
|
689
|
-
res.end(allow)
|
|
640
|
+
res.setHeader('Allow', allow);
|
|
641
|
+
res.setHeader('Content-Length', Buffer.byteLength(allow));
|
|
642
|
+
res.setHeader('Content-Type', 'text/plain');
|
|
643
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
644
|
+
res.end(allow);
|
|
690
645
|
}
|
|
691
646
|
|
|
692
647
|
/**
|
|
693
648
|
* Try to send an OPTIONS response.
|
|
694
|
-
*
|
|
695
|
-
* @private
|
|
696
649
|
*/
|
|
697
650
|
|
|
698
|
-
function trySendOptionsResponse
|
|
651
|
+
function trySendOptionsResponse(res, methods, next) {
|
|
699
652
|
try {
|
|
700
|
-
sendOptionsResponse(res, methods)
|
|
653
|
+
sendOptionsResponse(res, methods);
|
|
701
654
|
} catch (err) {
|
|
702
|
-
next(err)
|
|
655
|
+
next(err);
|
|
703
656
|
}
|
|
704
657
|
}
|
|
705
658
|
|
|
706
659
|
/**
|
|
707
660
|
* Wrap a function
|
|
708
|
-
*
|
|
709
|
-
* @private
|
|
710
661
|
*/
|
|
711
662
|
|
|
712
|
-
function wrap
|
|
713
|
-
return function proxy
|
|
714
|
-
fn.call(this, old, ...args)
|
|
715
|
-
}
|
|
663
|
+
function wrap(old, fn) {
|
|
664
|
+
return function proxy(...args) {
|
|
665
|
+
fn.call(this, old, ...args);
|
|
666
|
+
};
|
|
716
667
|
}
|