routup 2.0.0 → 3.0.0-alpha.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 +88 -35
- package/dist/dispatcher/adapters/node/module.d.ts +2 -1
- package/dist/dispatcher/type.d.ts +8 -6
- package/dist/dispatcher/utils.d.ts +2 -1
- package/dist/error/create.d.ts +11 -0
- package/dist/error/index.d.ts +3 -0
- package/dist/error/is.d.ts +2 -0
- package/dist/error/module.d.ts +3 -0
- package/dist/handler/constants.d.ts +4 -0
- package/dist/handler/core/define.d.ts +3 -0
- package/dist/handler/core/index.d.ts +2 -0
- package/dist/handler/core/types.d.ts +10 -0
- package/dist/handler/error/define.d.ts +3 -0
- package/dist/handler/error/index.d.ts +2 -0
- package/dist/handler/error/types.d.ts +11 -0
- package/dist/handler/index.d.ts +6 -0
- package/dist/handler/is.d.ts +2 -0
- package/dist/handler/types-base.d.ts +6 -0
- package/dist/handler/types.d.ts +5 -0
- package/dist/index.cjs +732 -695
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.mjs +724 -693
- package/dist/index.mjs.map +1 -1
- package/dist/layer/constants.d.ts +1 -0
- package/dist/layer/module.d.ts +9 -7
- package/dist/layer/type.d.ts +6 -3
- package/dist/layer/utils.d.ts +1 -1
- package/dist/path/matcher.d.ts +4 -4
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/is.d.ts +2 -0
- package/dist/plugin/types.d.ts +32 -0
- package/dist/request/helpers/body.d.ts +1 -1
- package/dist/request/helpers/cache.d.ts +1 -1
- package/dist/request/helpers/cookie.d.ts +1 -1
- package/dist/request/helpers/env.d.ts +1 -1
- package/dist/request/helpers/header-accept-charset.d.ts +1 -1
- package/dist/request/helpers/header-accept-language.d.ts +1 -1
- package/dist/request/helpers/header-accept.d.ts +1 -1
- package/dist/request/helpers/header-content-type.d.ts +1 -1
- package/dist/request/helpers/header.d.ts +1 -1
- package/dist/request/helpers/hostname.d.ts +1 -1
- package/dist/request/helpers/ip.d.ts +1 -1
- package/dist/request/helpers/mount-path.d.ts +1 -1
- package/dist/request/helpers/negotiator.d.ts +1 -1
- package/dist/request/helpers/params.d.ts +1 -1
- package/dist/request/helpers/path.d.ts +1 -1
- package/dist/request/helpers/protocol.d.ts +1 -1
- package/dist/request/helpers/query.d.ts +1 -1
- package/dist/request/helpers/router.d.ts +3 -3
- package/dist/request/types.d.ts +4 -0
- package/dist/response/helpers/cache.d.ts +1 -1
- package/dist/response/helpers/gone.d.ts +1 -1
- package/dist/response/helpers/header-attachment.d.ts +1 -1
- package/dist/response/helpers/header-content-type.d.ts +1 -1
- package/dist/response/helpers/header.d.ts +1 -1
- package/dist/response/helpers/send-accepted.d.ts +1 -1
- package/dist/response/helpers/send-created.d.ts +1 -1
- package/dist/response/helpers/send-file.d.ts +1 -1
- package/dist/response/helpers/send-format.d.ts +1 -1
- package/dist/response/helpers/send-redirect.d.ts +1 -1
- package/dist/response/helpers/send-stream.d.ts +2 -1
- package/dist/response/helpers/send-web-blob.d.ts +2 -1
- package/dist/response/helpers/send-web-response.d.ts +2 -1
- package/dist/response/helpers/send.d.ts +1 -1
- package/dist/response/helpers/utils.d.ts +1 -1
- package/dist/response/index.d.ts +1 -0
- package/dist/response/module.d.ts +2 -1
- package/dist/response/types.d.ts +4 -0
- package/dist/router/constants.d.ts +1 -0
- package/dist/router/index.d.ts +1 -0
- package/dist/router/module.d.ts +26 -23
- package/dist/router/utils.d.ts +2 -0
- package/dist/router-options/module.d.ts +1 -1
- package/dist/router-options/type.d.ts +3 -12
- package/dist/types.d.ts +0 -9
- package/dist/utils/is-instance.d.ts +1 -1
- package/package.json +15 -14
- package/dist/error.d.ts +0 -1
- package/dist/route/index.d.ts +0 -3
- package/dist/route/module.d.ts +0 -28
- package/dist/route/type.d.ts +0 -6
- package/dist/route/utils.d.ts +0 -2
package/dist/index.mjs
CHANGED
|
@@ -1,127 +1,80 @@
|
|
|
1
|
+
import { HTTPError } from '@ebec/http';
|
|
2
|
+
import { merge, hasOwnProperty, distinctArray } from 'smob';
|
|
1
3
|
import { Buffer } from 'buffer';
|
|
2
4
|
import { subtle } from 'uncrypto';
|
|
3
|
-
import { merge, hasOwnProperty, distinctArray } from 'smob';
|
|
4
5
|
import { compile, all } from 'proxy-addr';
|
|
5
6
|
import { getType, get } from 'mime-explorer';
|
|
6
7
|
import Negotiator from 'negotiator';
|
|
7
|
-
import {
|
|
8
|
+
import { Readable, Writable } from 'readable-stream';
|
|
8
9
|
import { pathToRegexp } from 'path-to-regexp';
|
|
9
10
|
|
|
10
11
|
var MethodName;
|
|
11
12
|
(function(MethodName) {
|
|
12
|
-
MethodName["GET"] =
|
|
13
|
-
MethodName["POST"] =
|
|
14
|
-
MethodName["PUT"] =
|
|
15
|
-
MethodName["PATCH"] =
|
|
16
|
-
MethodName["DELETE"] =
|
|
17
|
-
MethodName["OPTIONS"] =
|
|
18
|
-
MethodName["HEAD"] =
|
|
13
|
+
MethodName["GET"] = "get";
|
|
14
|
+
MethodName["POST"] = "post";
|
|
15
|
+
MethodName["PUT"] = "put";
|
|
16
|
+
MethodName["PATCH"] = "patch";
|
|
17
|
+
MethodName["DELETE"] = "delete";
|
|
18
|
+
MethodName["OPTIONS"] = "options";
|
|
19
|
+
MethodName["HEAD"] = "head";
|
|
19
20
|
})(MethodName || (MethodName = {}));
|
|
20
21
|
var HeaderName;
|
|
21
22
|
(function(HeaderName) {
|
|
22
|
-
HeaderName["ACCEPT"] =
|
|
23
|
-
HeaderName["ACCEPT_CHARSET"] =
|
|
24
|
-
HeaderName["ACCEPT_ENCODING"] =
|
|
25
|
-
HeaderName["ACCEPT_LANGUAGE"] =
|
|
26
|
-
HeaderName["ACCEPT_RANGES"] =
|
|
27
|
-
HeaderName["ALLOW"] =
|
|
28
|
-
HeaderName["CACHE_CONTROL"] =
|
|
29
|
-
HeaderName["CONTENT_DISPOSITION"] =
|
|
30
|
-
HeaderName["CONTENT_ENCODING"] =
|
|
31
|
-
HeaderName["CONTENT_LENGTH"] =
|
|
32
|
-
HeaderName["CONTENT_RANGE"] =
|
|
33
|
-
HeaderName["CONTENT_TYPE"] =
|
|
34
|
-
HeaderName["COOKIE"] =
|
|
35
|
-
HeaderName["ETag"] =
|
|
36
|
-
HeaderName["HOST"] =
|
|
37
|
-
HeaderName["IF_MODIFIED_SINCE"] =
|
|
38
|
-
HeaderName["IF_NONE_MATCH"] =
|
|
39
|
-
HeaderName["LAST_MODIFIED"] =
|
|
40
|
-
HeaderName["LOCATION"] =
|
|
41
|
-
HeaderName["RANGE"] =
|
|
42
|
-
HeaderName["RATE_LIMIT_LIMIT"] =
|
|
43
|
-
HeaderName["RATE_LIMIT_REMAINING"] =
|
|
44
|
-
HeaderName["RATE_LIMIT_RESET"] =
|
|
45
|
-
HeaderName["RETRY_AFTER"] =
|
|
46
|
-
HeaderName["SET_COOKIE"] =
|
|
47
|
-
HeaderName["TRANSFER_ENCODING"] =
|
|
48
|
-
HeaderName["X_FORWARDED_HOST"] =
|
|
49
|
-
HeaderName["X_FORWARDED_FOR"] =
|
|
50
|
-
HeaderName["X_FORWARDED_PROTO"] =
|
|
23
|
+
HeaderName["ACCEPT"] = "accept";
|
|
24
|
+
HeaderName["ACCEPT_CHARSET"] = "accept-charset";
|
|
25
|
+
HeaderName["ACCEPT_ENCODING"] = "accept-encoding";
|
|
26
|
+
HeaderName["ACCEPT_LANGUAGE"] = "accept-language";
|
|
27
|
+
HeaderName["ACCEPT_RANGES"] = "accept-ranges";
|
|
28
|
+
HeaderName["ALLOW"] = "allow";
|
|
29
|
+
HeaderName["CACHE_CONTROL"] = "cache-control";
|
|
30
|
+
HeaderName["CONTENT_DISPOSITION"] = "content-disposition";
|
|
31
|
+
HeaderName["CONTENT_ENCODING"] = "content-encoding";
|
|
32
|
+
HeaderName["CONTENT_LENGTH"] = "content-length";
|
|
33
|
+
HeaderName["CONTENT_RANGE"] = "content-range";
|
|
34
|
+
HeaderName["CONTENT_TYPE"] = "content-type";
|
|
35
|
+
HeaderName["COOKIE"] = "cookie";
|
|
36
|
+
HeaderName["ETag"] = "etag";
|
|
37
|
+
HeaderName["HOST"] = "host";
|
|
38
|
+
HeaderName["IF_MODIFIED_SINCE"] = "if-modified-since";
|
|
39
|
+
HeaderName["IF_NONE_MATCH"] = "if-none-match";
|
|
40
|
+
HeaderName["LAST_MODIFIED"] = "last-modified";
|
|
41
|
+
HeaderName["LOCATION"] = "location";
|
|
42
|
+
HeaderName["RANGE"] = "range";
|
|
43
|
+
HeaderName["RATE_LIMIT_LIMIT"] = "ratelimit-limit";
|
|
44
|
+
HeaderName["RATE_LIMIT_REMAINING"] = "ratelimit-remaining";
|
|
45
|
+
HeaderName["RATE_LIMIT_RESET"] = "ratelimit-reset";
|
|
46
|
+
HeaderName["RETRY_AFTER"] = "retry-after";
|
|
47
|
+
HeaderName["SET_COOKIE"] = "set-cookie";
|
|
48
|
+
HeaderName["TRANSFER_ENCODING"] = "transfer-encoding";
|
|
49
|
+
HeaderName["X_FORWARDED_HOST"] = "x-forwarded-host";
|
|
50
|
+
HeaderName["X_FORWARDED_FOR"] = "x-forwarded-for";
|
|
51
|
+
HeaderName["X_FORWARDED_PROTO"] = "x-forwarded-proto";
|
|
51
52
|
})(HeaderName || (HeaderName = {}));
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
options = options || {};
|
|
55
|
-
const cacheControls = [
|
|
56
|
-
'public'
|
|
57
|
-
].concat(options.cacheControls || []);
|
|
58
|
-
if (options.maxAge !== undefined) {
|
|
59
|
-
cacheControls.push(`max-age=${+options.maxAge}`, `s-maxage=${+options.maxAge}`);
|
|
60
|
-
}
|
|
61
|
-
if (options.modifiedTime) {
|
|
62
|
-
const modifiedTime = typeof options.modifiedTime === 'string' ? new Date(options.modifiedTime) : options.modifiedTime;
|
|
63
|
-
res.setHeader('last-modified', modifiedTime.toUTCString());
|
|
64
|
-
}
|
|
65
|
-
res.setHeader('cache-control', cacheControls.join(', '));
|
|
54
|
+
class ErrorProxy extends HTTPError {
|
|
66
55
|
}
|
|
67
56
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (res.headersSent || res.writableEnded) {
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
if (GoneSymbol in res) {
|
|
74
|
-
return res[GoneSymbol];
|
|
75
|
-
}
|
|
76
|
-
return false;
|
|
57
|
+
function isError(input) {
|
|
58
|
+
return input instanceof ErrorProxy;
|
|
77
59
|
}
|
|
78
60
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
res.setHeader(name, [
|
|
91
|
-
...header,
|
|
92
|
-
value
|
|
93
|
-
]);
|
|
94
|
-
}
|
|
95
|
-
function appendResponseHeaderDirective(res, name, value) {
|
|
96
|
-
let header = res.getHeader(name);
|
|
97
|
-
if (!header) {
|
|
98
|
-
if (Array.isArray(value)) {
|
|
99
|
-
res.setHeader(name, value.join('; '));
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
res.setHeader(name, value);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
if (!Array.isArray(header)) {
|
|
106
|
-
if (typeof header === 'string') {
|
|
107
|
-
// split header by directive(s)
|
|
108
|
-
header = header.split('; ');
|
|
109
|
-
}
|
|
110
|
-
if (typeof header === 'number') {
|
|
111
|
-
header = [
|
|
112
|
-
header.toString()
|
|
113
|
-
];
|
|
114
|
-
}
|
|
61
|
+
/**
|
|
62
|
+
* Create an error proxy by
|
|
63
|
+
* - an existing error (accessible via cause property)
|
|
64
|
+
* - options
|
|
65
|
+
* - message
|
|
66
|
+
*
|
|
67
|
+
* @param input
|
|
68
|
+
*/ function createError(input) {
|
|
69
|
+
if (isError(input)) {
|
|
70
|
+
return input;
|
|
115
71
|
}
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
} else {
|
|
119
|
-
header.push(`${value}`);
|
|
72
|
+
if (typeof input === 'string') {
|
|
73
|
+
return new ErrorProxy(input);
|
|
120
74
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
res.setHeader(name, header.join('; '));
|
|
75
|
+
return new ErrorProxy({
|
|
76
|
+
cause: input
|
|
77
|
+
}, input);
|
|
125
78
|
}
|
|
126
79
|
|
|
127
80
|
/*
|
|
@@ -311,8 +264,11 @@ function buildTrustProxyFn(input) {
|
|
|
311
264
|
return compile(input || []);
|
|
312
265
|
}
|
|
313
266
|
|
|
314
|
-
function isInstance(input,
|
|
315
|
-
|
|
267
|
+
function isInstance(input, sym) {
|
|
268
|
+
if (!isObject(input)) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
return input['@instanceof'] === sym;
|
|
316
272
|
}
|
|
317
273
|
|
|
318
274
|
function getMimeType(type) {
|
|
@@ -393,7 +349,10 @@ function withLeadingSlash(input = '') {
|
|
|
393
349
|
return hasLeadingSlash(input) ? input : `/${input}`;
|
|
394
350
|
}
|
|
395
351
|
function cleanDoubleSlashes(input = '') {
|
|
396
|
-
|
|
352
|
+
if (input.indexOf('://') !== -1) {
|
|
353
|
+
return input.split('://').map((str)=>cleanDoubleSlashes(str)).join('://');
|
|
354
|
+
}
|
|
355
|
+
return input.replace(/\/+/g, '/');
|
|
397
356
|
}
|
|
398
357
|
|
|
399
358
|
function isWebBlob(input) {
|
|
@@ -403,67 +362,6 @@ function isWebResponse(input) {
|
|
|
403
362
|
return typeof Response !== 'undefined' && input instanceof Response;
|
|
404
363
|
}
|
|
405
364
|
|
|
406
|
-
function setResponseContentTypeByFileName(res, fileName) {
|
|
407
|
-
const ext = extname(fileName);
|
|
408
|
-
if (ext) {
|
|
409
|
-
let type = getMimeType(ext.substring(1));
|
|
410
|
-
if (type) {
|
|
411
|
-
const charset = getCharsetForMimeType(type);
|
|
412
|
-
if (charset) {
|
|
413
|
-
type += `; charset=${charset}`;
|
|
414
|
-
}
|
|
415
|
-
res.setHeader(HeaderName.CONTENT_TYPE, type);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
function setResponseHeaderAttachment(res, filename) {
|
|
421
|
-
if (typeof filename === 'string') {
|
|
422
|
-
setResponseContentTypeByFileName(res, filename);
|
|
423
|
-
}
|
|
424
|
-
res.setHeader(HeaderName.CONTENT_DISPOSITION, `attachment${filename ? `; filename="${filename}"` : ''}`);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
function setResponseHeaderContentType(res, input, ifNotExists) {
|
|
428
|
-
if (ifNotExists) {
|
|
429
|
-
const header = res.getHeader(HeaderName.CONTENT_TYPE);
|
|
430
|
-
if (header) {
|
|
431
|
-
return;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
const contentType = getMimeType(input);
|
|
435
|
-
if (contentType) {
|
|
436
|
-
res.setHeader(HeaderName.CONTENT_TYPE, contentType);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
const defaults = {
|
|
441
|
-
trustProxy: ()=>false,
|
|
442
|
-
subdomainOffset: 2,
|
|
443
|
-
etag: buildEtagFn(),
|
|
444
|
-
proxyIpMax: 0
|
|
445
|
-
};
|
|
446
|
-
const instances = {};
|
|
447
|
-
function setRouterOptions(id, input) {
|
|
448
|
-
instances[id] = input;
|
|
449
|
-
}
|
|
450
|
-
function findRouterOption(key, id) {
|
|
451
|
-
if (!id) {
|
|
452
|
-
return defaults[key];
|
|
453
|
-
}
|
|
454
|
-
const ids = Array.isArray(id) ? id : [
|
|
455
|
-
id
|
|
456
|
-
];
|
|
457
|
-
if (ids.length > 0) {
|
|
458
|
-
for(let i = ids.length; i >= 0; i--){
|
|
459
|
-
if (hasOwnProperty(instances, ids[i]) && typeof instances[ids[i]][key] !== 'undefined') {
|
|
460
|
-
return instances[ids[i]][key];
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
return defaults[key];
|
|
465
|
-
}
|
|
466
|
-
|
|
467
365
|
const BodySymbol = Symbol.for('ReqBody');
|
|
468
366
|
function useRequestBody(req, key) {
|
|
469
367
|
let body;
|
|
@@ -725,11 +623,35 @@ function matchRequestContentType(req, contentType) {
|
|
|
725
623
|
return header.split('; ').shift() === getMimeType(contentType);
|
|
726
624
|
}
|
|
727
625
|
|
|
626
|
+
const defaults = {
|
|
627
|
+
trustProxy: ()=>false,
|
|
628
|
+
subdomainOffset: 2,
|
|
629
|
+
etag: buildEtagFn(),
|
|
630
|
+
proxyIpMax: 0
|
|
631
|
+
};
|
|
632
|
+
const instances = {};
|
|
633
|
+
function setRouterOptions(id, input) {
|
|
634
|
+
instances[id] = input;
|
|
635
|
+
}
|
|
636
|
+
function findRouterOption(key, path) {
|
|
637
|
+
if (!path || path.length === 0) {
|
|
638
|
+
return defaults[key];
|
|
639
|
+
}
|
|
640
|
+
if (path.length > 0) {
|
|
641
|
+
for(let i = path.length; i >= 0; i--){
|
|
642
|
+
if (hasOwnProperty(instances, path[i]) && typeof instances[path[i]][key] !== 'undefined') {
|
|
643
|
+
return instances[path[i]][key];
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return defaults[key];
|
|
648
|
+
}
|
|
649
|
+
|
|
728
650
|
const routerSymbol = Symbol.for('ReqRouterID');
|
|
729
|
-
function
|
|
730
|
-
req[routerSymbol] =
|
|
651
|
+
function setRequestRouterPath(req, path) {
|
|
652
|
+
req[routerSymbol] = path;
|
|
731
653
|
}
|
|
732
|
-
function
|
|
654
|
+
function useRequestRouterPath(req) {
|
|
733
655
|
if (routerSymbol in req) {
|
|
734
656
|
return req[routerSymbol];
|
|
735
657
|
}
|
|
@@ -742,7 +664,7 @@ function getRequestHostName(req, options) {
|
|
|
742
664
|
if (typeof options.trustProxy !== 'undefined') {
|
|
743
665
|
trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
744
666
|
} else {
|
|
745
|
-
trustProxy = findRouterOption('trustProxy',
|
|
667
|
+
trustProxy = findRouterOption('trustProxy', useRequestRouterPath(req));
|
|
746
668
|
}
|
|
747
669
|
let hostname = req.headers[HeaderName.X_FORWARDED_HOST];
|
|
748
670
|
if (!hostname || !req.socket.remoteAddress || !trustProxy(req.socket.remoteAddress, 0)) {
|
|
@@ -768,7 +690,7 @@ function getRequestIP(req, options) {
|
|
|
768
690
|
if (typeof options.trustProxy !== 'undefined') {
|
|
769
691
|
trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
770
692
|
} else {
|
|
771
|
-
trustProxy = findRouterOption('trustProxy',
|
|
693
|
+
trustProxy = findRouterOption('trustProxy', useRequestRouterPath(req));
|
|
772
694
|
}
|
|
773
695
|
const addrs = all(req, trustProxy);
|
|
774
696
|
return addrs[addrs.length - 1];
|
|
@@ -829,7 +751,7 @@ function getRequestProtocol(req, options) {
|
|
|
829
751
|
if (typeof options.trustProxy !== 'undefined') {
|
|
830
752
|
trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
831
753
|
} else {
|
|
832
|
-
trustProxy = findRouterOption('trustProxy',
|
|
754
|
+
trustProxy = findRouterOption('trustProxy', useRequestRouterPath(req));
|
|
833
755
|
}
|
|
834
756
|
let protocol = options.default;
|
|
835
757
|
/* istanbul ignore next */ if (hasOwnProperty(req.socket, 'encrypted') && !!req.socket.encrypted) {
|
|
@@ -897,100 +819,280 @@ function extendRequestQuery(req, key, value) {
|
|
|
897
819
|
setRequestQuery(req, key, value);
|
|
898
820
|
}
|
|
899
821
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
break;
|
|
906
|
-
}
|
|
907
|
-
case 'boolean':
|
|
908
|
-
case 'number':
|
|
909
|
-
case 'object':
|
|
910
|
-
{
|
|
911
|
-
if (chunk === null) {
|
|
912
|
-
chunk = '';
|
|
913
|
-
} else if (Buffer.isBuffer(chunk)) {
|
|
914
|
-
setResponseHeaderContentType(res, 'bin', true);
|
|
915
|
-
} else {
|
|
916
|
-
chunk = JSON.stringify(chunk);
|
|
917
|
-
setResponseHeaderContentType(res, 'application/json', true);
|
|
918
|
-
}
|
|
919
|
-
break;
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
let encoding;
|
|
923
|
-
if (typeof chunk === 'string') {
|
|
924
|
-
res.setHeader(HeaderName.CONTENT_ENCODING, 'utf-8');
|
|
925
|
-
appendResponseHeaderDirective(res, HeaderName.CONTENT_TYPE, 'charset=utf-8');
|
|
926
|
-
encoding = 'utf-8';
|
|
927
|
-
}
|
|
928
|
-
// populate Content-Length
|
|
929
|
-
let len;
|
|
930
|
-
if (chunk !== undefined) {
|
|
931
|
-
if (Buffer.isBuffer(chunk)) {
|
|
932
|
-
// get length of Buffer
|
|
933
|
-
len = chunk.length;
|
|
934
|
-
} else if (chunk.length < 1000) {
|
|
935
|
-
// just calculate length when no ETag + small chunk
|
|
936
|
-
len = Buffer.byteLength(chunk, encoding);
|
|
822
|
+
function createRequest(context) {
|
|
823
|
+
let readable;
|
|
824
|
+
if (context.body) {
|
|
825
|
+
if (isWebStream(context.body)) {
|
|
826
|
+
readable = Readable.fromWeb(context.body);
|
|
937
827
|
} else {
|
|
938
|
-
|
|
939
|
-
chunk = Buffer.from(chunk, encoding);
|
|
940
|
-
encoding = undefined;
|
|
941
|
-
len = chunk.length;
|
|
828
|
+
readable = Readable.from(context.body);
|
|
942
829
|
}
|
|
943
|
-
|
|
830
|
+
} else {
|
|
831
|
+
readable = new Readable();
|
|
944
832
|
}
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
if (res.req.headers[HeaderName.IF_NONE_MATCH] === chunkHash) {
|
|
954
|
-
res.statusCode = 304;
|
|
833
|
+
const headers = context.headers || {};
|
|
834
|
+
const rawHeaders = [];
|
|
835
|
+
let keys = Object.keys(headers);
|
|
836
|
+
for(let i = 0; i < keys.length; i++){
|
|
837
|
+
const header = headers[keys[i]];
|
|
838
|
+
if (Array.isArray(header)) {
|
|
839
|
+
for(let j = 0; j < header.length; j++){
|
|
840
|
+
rawHeaders.push(keys[i], header[j]);
|
|
955
841
|
}
|
|
842
|
+
} else if (typeof header === 'string') {
|
|
843
|
+
rawHeaders.push(keys[i], header);
|
|
956
844
|
}
|
|
957
845
|
}
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
}
|
|
971
|
-
if (isResponseGone(res)) {
|
|
972
|
-
return Promise.resolve();
|
|
973
|
-
}
|
|
974
|
-
if (res.req.method === 'HEAD') {
|
|
975
|
-
// skip body for HEAD
|
|
976
|
-
res.end();
|
|
977
|
-
return Promise.resolve();
|
|
978
|
-
}
|
|
979
|
-
if (typeof encoding !== 'undefined') {
|
|
980
|
-
res.end(chunk, encoding);
|
|
981
|
-
return Promise.resolve();
|
|
846
|
+
const headersDistinct = {};
|
|
847
|
+
keys = Object.keys(headers);
|
|
848
|
+
for(let i = 0; i < keys.length; i++){
|
|
849
|
+
const header = headers[keys[i]];
|
|
850
|
+
if (Array.isArray(header)) {
|
|
851
|
+
headersDistinct[keys[i]] = header;
|
|
852
|
+
}
|
|
853
|
+
if (typeof header === 'string') {
|
|
854
|
+
headersDistinct[keys[i]] = [
|
|
855
|
+
header
|
|
856
|
+
];
|
|
857
|
+
}
|
|
982
858
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
859
|
+
Object.defineProperty(readable, 'connection', {
|
|
860
|
+
get () {
|
|
861
|
+
return {
|
|
862
|
+
remoteAddress: '127.0.0.1'
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
Object.defineProperty(readable, 'socket', {
|
|
867
|
+
get () {
|
|
868
|
+
return {
|
|
869
|
+
remoteAddress: '127.0.0.1'
|
|
870
|
+
};
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
Object.assign(readable, {
|
|
874
|
+
aborted: false,
|
|
875
|
+
complete: true,
|
|
876
|
+
headers,
|
|
877
|
+
headersDistinct,
|
|
878
|
+
httpVersion: '1.1',
|
|
879
|
+
httpVersionMajor: 1,
|
|
880
|
+
httpVersionMinor: 1,
|
|
881
|
+
method: context.method || 'GET',
|
|
882
|
+
rawHeaders,
|
|
883
|
+
rawTrailers: [],
|
|
884
|
+
trailers: {},
|
|
885
|
+
trailersDistinct: {},
|
|
886
|
+
url: context.url || '/',
|
|
887
|
+
setTimeout (_msecs, _callback) {
|
|
888
|
+
return this;
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
return readable;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
function setResponseCacheHeaders(res, options) {
|
|
895
|
+
options = options || {};
|
|
896
|
+
const cacheControls = [
|
|
897
|
+
'public'
|
|
898
|
+
].concat(options.cacheControls || []);
|
|
899
|
+
if (options.maxAge !== undefined) {
|
|
900
|
+
cacheControls.push(`max-age=${+options.maxAge}`, `s-maxage=${+options.maxAge}`);
|
|
901
|
+
}
|
|
902
|
+
if (options.modifiedTime) {
|
|
903
|
+
const modifiedTime = typeof options.modifiedTime === 'string' ? new Date(options.modifiedTime) : options.modifiedTime;
|
|
904
|
+
res.setHeader('last-modified', modifiedTime.toUTCString());
|
|
905
|
+
}
|
|
906
|
+
res.setHeader('cache-control', cacheControls.join(', '));
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
const GoneSymbol = Symbol.for('ResGone');
|
|
910
|
+
function isResponseGone(res) {
|
|
911
|
+
if (res.headersSent || res.writableEnded) {
|
|
912
|
+
return true;
|
|
913
|
+
}
|
|
914
|
+
if (GoneSymbol in res) {
|
|
915
|
+
return res[GoneSymbol];
|
|
916
|
+
}
|
|
917
|
+
return false;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
function appendResponseHeader(res, name, value) {
|
|
921
|
+
let header = res.getHeader(name);
|
|
922
|
+
if (!header) {
|
|
923
|
+
res.setHeader(name, value);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (!Array.isArray(header)) {
|
|
927
|
+
header = [
|
|
928
|
+
header.toString()
|
|
929
|
+
];
|
|
930
|
+
}
|
|
931
|
+
res.setHeader(name, [
|
|
932
|
+
...header,
|
|
933
|
+
value
|
|
934
|
+
]);
|
|
935
|
+
}
|
|
936
|
+
function appendResponseHeaderDirective(res, name, value) {
|
|
937
|
+
let header = res.getHeader(name);
|
|
938
|
+
if (!header) {
|
|
939
|
+
if (Array.isArray(value)) {
|
|
940
|
+
res.setHeader(name, value.join('; '));
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
res.setHeader(name, value);
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
if (!Array.isArray(header)) {
|
|
947
|
+
if (typeof header === 'string') {
|
|
948
|
+
// split header by directive(s)
|
|
949
|
+
header = header.split('; ');
|
|
950
|
+
}
|
|
951
|
+
if (typeof header === 'number') {
|
|
952
|
+
header = [
|
|
953
|
+
header.toString()
|
|
954
|
+
];
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
if (Array.isArray(value)) {
|
|
958
|
+
header.push(...value);
|
|
959
|
+
} else {
|
|
960
|
+
header.push(`${value}`);
|
|
961
|
+
}
|
|
962
|
+
header = [
|
|
963
|
+
...new Set(header)
|
|
964
|
+
];
|
|
965
|
+
res.setHeader(name, header.join('; '));
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function setResponseContentTypeByFileName(res, fileName) {
|
|
969
|
+
const ext = extname(fileName);
|
|
970
|
+
if (ext) {
|
|
971
|
+
let type = getMimeType(ext.substring(1));
|
|
972
|
+
if (type) {
|
|
973
|
+
const charset = getCharsetForMimeType(type);
|
|
974
|
+
if (charset) {
|
|
975
|
+
type += `; charset=${charset}`;
|
|
976
|
+
}
|
|
977
|
+
res.setHeader(HeaderName.CONTENT_TYPE, type);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function setResponseHeaderAttachment(res, filename) {
|
|
983
|
+
if (typeof filename === 'string') {
|
|
984
|
+
setResponseContentTypeByFileName(res, filename);
|
|
985
|
+
}
|
|
986
|
+
res.setHeader(HeaderName.CONTENT_DISPOSITION, `attachment${filename ? `; filename="${filename}"` : ''}`);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
function setResponseHeaderContentType(res, input, ifNotExists) {
|
|
990
|
+
if (ifNotExists) {
|
|
991
|
+
const header = res.getHeader(HeaderName.CONTENT_TYPE);
|
|
992
|
+
if (header) {
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
const contentType = getMimeType(input);
|
|
997
|
+
if (contentType) {
|
|
998
|
+
res.setHeader(HeaderName.CONTENT_TYPE, contentType);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
async function send(res, chunk) {
|
|
1003
|
+
switch(typeof chunk){
|
|
1004
|
+
case 'string':
|
|
1005
|
+
{
|
|
1006
|
+
setResponseHeaderContentType(res, 'html', true);
|
|
1007
|
+
break;
|
|
1008
|
+
}
|
|
1009
|
+
case 'boolean':
|
|
1010
|
+
case 'number':
|
|
1011
|
+
case 'object':
|
|
1012
|
+
{
|
|
1013
|
+
if (Buffer.isBuffer(chunk)) {
|
|
1014
|
+
setResponseHeaderContentType(res, 'bin', true);
|
|
1015
|
+
} else if (chunk !== null) {
|
|
1016
|
+
chunk = JSON.stringify(chunk);
|
|
1017
|
+
setResponseHeaderContentType(res, 'application/json', true);
|
|
1018
|
+
}
|
|
1019
|
+
break;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
let encoding;
|
|
1023
|
+
if (typeof chunk === 'string') {
|
|
1024
|
+
res.setHeader(HeaderName.CONTENT_ENCODING, 'utf-8');
|
|
1025
|
+
appendResponseHeaderDirective(res, HeaderName.CONTENT_TYPE, 'charset=utf-8');
|
|
1026
|
+
encoding = 'utf-8';
|
|
1027
|
+
}
|
|
1028
|
+
// populate Content-Length
|
|
1029
|
+
let len;
|
|
1030
|
+
if (chunk !== undefined && chunk !== null) {
|
|
1031
|
+
if (Buffer.isBuffer(chunk)) {
|
|
1032
|
+
// get length of Buffer
|
|
1033
|
+
len = chunk.length;
|
|
1034
|
+
} else if (chunk.length < 1000) {
|
|
1035
|
+
// just calculate length when no ETag + small chunk
|
|
1036
|
+
len = Buffer.byteLength(chunk, encoding);
|
|
1037
|
+
} else {
|
|
1038
|
+
// convert chunk to Buffer and calculate
|
|
1039
|
+
chunk = Buffer.from(chunk, encoding);
|
|
1040
|
+
encoding = undefined;
|
|
1041
|
+
len = chunk.length;
|
|
1042
|
+
}
|
|
1043
|
+
res.setHeader(HeaderName.CONTENT_LENGTH, `${len}`);
|
|
1044
|
+
}
|
|
1045
|
+
if (typeof len !== 'undefined') {
|
|
1046
|
+
const etagFn = findRouterOption('etag', useRequestRouterPath(res.req));
|
|
1047
|
+
const chunkHash = await etagFn(chunk, encoding, len);
|
|
1048
|
+
if (isResponseGone(res)) {
|
|
1049
|
+
return Promise.resolve();
|
|
1050
|
+
}
|
|
1051
|
+
if (typeof chunkHash === 'string') {
|
|
1052
|
+
res.setHeader(HeaderName.ETag, chunkHash);
|
|
1053
|
+
if (res.req.headers[HeaderName.IF_NONE_MATCH] === chunkHash) {
|
|
1054
|
+
res.statusCode = 304;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
// strip irrelevant headers
|
|
1059
|
+
if (res.statusCode === 204 || res.statusCode === 304) {
|
|
1060
|
+
res.removeHeader(HeaderName.CONTENT_TYPE);
|
|
1061
|
+
res.removeHeader(HeaderName.CONTENT_LENGTH);
|
|
1062
|
+
res.removeHeader(HeaderName.TRANSFER_ENCODING);
|
|
1063
|
+
}
|
|
1064
|
+
// alter headers for 205
|
|
1065
|
+
if (res.statusCode === 205) {
|
|
1066
|
+
res.setHeader(HeaderName.CONTENT_LENGTH, 0);
|
|
1067
|
+
res.removeHeader(HeaderName.TRANSFER_ENCODING);
|
|
1068
|
+
}
|
|
1069
|
+
if (isResponseGone(res)) {
|
|
1070
|
+
return Promise.resolve();
|
|
1071
|
+
}
|
|
1072
|
+
if (res.req.method === 'HEAD') {
|
|
1073
|
+
// skip body for HEAD
|
|
1074
|
+
res.end();
|
|
1075
|
+
return Promise.resolve();
|
|
1076
|
+
}
|
|
1077
|
+
if (typeof chunk === 'undefined' || chunk === null) {
|
|
1078
|
+
res.end();
|
|
1079
|
+
return Promise.resolve();
|
|
1080
|
+
}
|
|
1081
|
+
if (typeof encoding !== 'undefined') {
|
|
1082
|
+
res.end(chunk, encoding);
|
|
1083
|
+
return Promise.resolve();
|
|
1084
|
+
}
|
|
1085
|
+
res.end(chunk);
|
|
1086
|
+
return Promise.resolve();
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
function sendAccepted(res, chunk) {
|
|
1090
|
+
res.statusCode = 202;
|
|
1091
|
+
res.statusMessage = 'Accepted';
|
|
1092
|
+
return send(res, chunk);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
function sendCreated(res, chunk) {
|
|
994
1096
|
res.statusCode = 201;
|
|
995
1097
|
res.statusMessage = 'Created';
|
|
996
1098
|
return send(res, chunk);
|
|
@@ -1301,99 +1403,90 @@ function createResponse(request) {
|
|
|
1301
1403
|
return writable;
|
|
1302
1404
|
}
|
|
1303
1405
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
if (dispatched) {
|
|
1311
|
-
return;
|
|
1312
|
-
}
|
|
1313
|
-
if (!isResponseGone(res)) {
|
|
1314
|
-
res.statusCode = 404;
|
|
1315
|
-
res.end();
|
|
1316
|
-
}
|
|
1317
|
-
} catch (e) {
|
|
1318
|
-
if (!isResponseGone(res)) {
|
|
1319
|
-
res.statusCode = 500;
|
|
1320
|
-
res.end();
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
function createNodeDispatcher(router) {
|
|
1325
|
-
return (req, res)=>{
|
|
1326
|
-
// eslint-disable-next-line no-void
|
|
1327
|
-
void dispatchNodeRequest(router, req, res);
|
|
1406
|
+
function buildDispatcherMeta(input) {
|
|
1407
|
+
return {
|
|
1408
|
+
mountPath: input.mountPath || '/',
|
|
1409
|
+
params: input.params || {},
|
|
1410
|
+
path: input.path || '/',
|
|
1411
|
+
routerPath: []
|
|
1328
1412
|
};
|
|
1329
1413
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1414
|
+
function cloneDispatcherMeta(input) {
|
|
1415
|
+
return {
|
|
1416
|
+
path: input.path,
|
|
1417
|
+
mountPath: input.mountPath,
|
|
1418
|
+
error: input.error,
|
|
1419
|
+
routerPath: [
|
|
1420
|
+
...input.routerPath
|
|
1421
|
+
],
|
|
1422
|
+
params: cloneDispatcherMetaParams(input.params)
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
function cloneDispatcherMetaParams(input) {
|
|
1426
|
+
if (typeof input === 'undefined') {
|
|
1427
|
+
return {};
|
|
1341
1428
|
}
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1344
|
-
|
|
1429
|
+
const keys = Object.keys(input);
|
|
1430
|
+
if (keys.length === 0) {
|
|
1431
|
+
return {};
|
|
1432
|
+
}
|
|
1433
|
+
const output = {};
|
|
1345
1434
|
for(let i = 0; i < keys.length; i++){
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1435
|
+
output[keys[i]] = input[keys[i]];
|
|
1436
|
+
}
|
|
1437
|
+
return output;
|
|
1438
|
+
}
|
|
1439
|
+
function mergeDispatcherMetaParams(t1, t2) {
|
|
1440
|
+
if (!t1 && !t2) {
|
|
1441
|
+
return {};
|
|
1442
|
+
}
|
|
1443
|
+
if (!t1 || !t2) {
|
|
1444
|
+
return t1 || t2;
|
|
1445
|
+
}
|
|
1446
|
+
const keys = Object.keys(t2);
|
|
1447
|
+
if (keys.length === 0) {
|
|
1448
|
+
return t1;
|
|
1354
1449
|
}
|
|
1355
|
-
const headersDistinct = {};
|
|
1356
|
-
keys = Object.keys(headers);
|
|
1357
1450
|
for(let i = 0; i < keys.length; i++){
|
|
1358
|
-
|
|
1359
|
-
if (Array.isArray(header)) {
|
|
1360
|
-
headersDistinct[keys[i]] = header;
|
|
1361
|
-
}
|
|
1362
|
-
if (typeof header === 'string') {
|
|
1363
|
-
headersDistinct[keys[i]] = [
|
|
1364
|
-
header
|
|
1365
|
-
];
|
|
1366
|
-
}
|
|
1451
|
+
t1[keys[i]] = t2[keys[i]];
|
|
1367
1452
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1453
|
+
return t1;
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
async function dispatchNodeRequest(router, req, res) {
|
|
1457
|
+
try {
|
|
1458
|
+
const dispatched = await router.dispatch({
|
|
1459
|
+
req,
|
|
1460
|
+
res
|
|
1461
|
+
}, buildDispatcherMeta({
|
|
1462
|
+
path: useRequestPath(req)
|
|
1463
|
+
}));
|
|
1464
|
+
if (dispatched) {
|
|
1465
|
+
return;
|
|
1371
1466
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
return {};
|
|
1467
|
+
if (!isResponseGone(res)) {
|
|
1468
|
+
res.statusCode = 404;
|
|
1469
|
+
res.end();
|
|
1376
1470
|
}
|
|
1377
|
-
})
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
rawTrailers: [],
|
|
1389
|
-
trailers: {},
|
|
1390
|
-
trailersDistinct: {},
|
|
1391
|
-
url: context.url || '/',
|
|
1392
|
-
setTimeout (_msecs, _callback) {
|
|
1393
|
-
return this;
|
|
1471
|
+
} catch (e) {
|
|
1472
|
+
if (!isResponseGone(res)) {
|
|
1473
|
+
if (isError(e)) {
|
|
1474
|
+
res.statusCode = e.statusCode;
|
|
1475
|
+
if (e.statusMessage) {
|
|
1476
|
+
res.statusMessage = e.statusMessage;
|
|
1477
|
+
}
|
|
1478
|
+
} else {
|
|
1479
|
+
res.statusCode = 500;
|
|
1480
|
+
}
|
|
1481
|
+
res.end();
|
|
1394
1482
|
}
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
function createNodeDispatcher(router) {
|
|
1486
|
+
return (req, res)=>{
|
|
1487
|
+
// eslint-disable-next-line no-void
|
|
1488
|
+
void dispatchNodeRequest(router, req, res);
|
|
1489
|
+
};
|
|
1397
1490
|
}
|
|
1398
1491
|
|
|
1399
1492
|
async function dispatchRawRequest(router, request, options = {}) {
|
|
@@ -1418,33 +1511,38 @@ async function dispatchRawRequest(router, request, options = {}) {
|
|
|
1418
1511
|
}
|
|
1419
1512
|
return output;
|
|
1420
1513
|
};
|
|
1514
|
+
const createRawResponse = (input = {})=>({
|
|
1515
|
+
status: input.status || res.statusCode,
|
|
1516
|
+
statusMessage: input.statusMessage || res.statusMessage,
|
|
1517
|
+
headers: getHeaders(),
|
|
1518
|
+
body: res.body
|
|
1519
|
+
});
|
|
1421
1520
|
try {
|
|
1422
1521
|
const dispatched = await router.dispatch({
|
|
1423
1522
|
req,
|
|
1424
1523
|
res
|
|
1425
|
-
}
|
|
1524
|
+
}, buildDispatcherMeta({
|
|
1525
|
+
path: useRequestPath(req)
|
|
1526
|
+
}));
|
|
1426
1527
|
if (dispatched) {
|
|
1427
|
-
return
|
|
1428
|
-
status: res.statusCode,
|
|
1429
|
-
statusMessage: res.statusMessage,
|
|
1430
|
-
headers: getHeaders(),
|
|
1431
|
-
body: res.body
|
|
1432
|
-
};
|
|
1528
|
+
return createRawResponse();
|
|
1433
1529
|
}
|
|
1434
|
-
return {
|
|
1435
|
-
status: 404
|
|
1436
|
-
|
|
1437
|
-
body: res.body
|
|
1438
|
-
};
|
|
1530
|
+
return createRawResponse({
|
|
1531
|
+
status: 404
|
|
1532
|
+
});
|
|
1439
1533
|
} catch (e) {
|
|
1440
1534
|
if (options.throwOnError) {
|
|
1441
1535
|
throw e;
|
|
1442
1536
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1537
|
+
if (isError(e)) {
|
|
1538
|
+
return createRawResponse({
|
|
1539
|
+
status: e.statusCode,
|
|
1540
|
+
statusMessage: e.statusMessage
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
return createRawResponse({
|
|
1544
|
+
status: 500
|
|
1545
|
+
});
|
|
1448
1546
|
}
|
|
1449
1547
|
}
|
|
1450
1548
|
function createRawDispatcher(router) {
|
|
@@ -1479,54 +1577,40 @@ function createWebDispatcher(router) {
|
|
|
1479
1577
|
return async (request)=>dispatchWebRequest(router, request);
|
|
1480
1578
|
}
|
|
1481
1579
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1580
|
+
var HandlerType;
|
|
1581
|
+
(function(HandlerType) {
|
|
1582
|
+
HandlerType["CORE"] = "core";
|
|
1583
|
+
HandlerType["ERROR"] = "error";
|
|
1584
|
+
})(HandlerType || (HandlerType = {}));
|
|
1585
|
+
|
|
1586
|
+
function coreHandler(input) {
|
|
1587
|
+
if (typeof input === 'function') {
|
|
1588
|
+
return {
|
|
1589
|
+
type: HandlerType.CORE,
|
|
1590
|
+
fn: input
|
|
1591
|
+
};
|
|
1485
1592
|
}
|
|
1486
1593
|
return {
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
error: input.error,
|
|
1490
|
-
routerIds: [
|
|
1491
|
-
...input.routerIds || []
|
|
1492
|
-
],
|
|
1493
|
-
params: cloneDispatcherMetaParams(input.params)
|
|
1594
|
+
type: HandlerType.CORE,
|
|
1595
|
+
...input
|
|
1494
1596
|
};
|
|
1495
1597
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
output[keys[i]] = input[keys[i]];
|
|
1504
|
-
}
|
|
1505
|
-
return output;
|
|
1506
|
-
}
|
|
1507
|
-
function mergeDispatcherMetaParams(t1, t2) {
|
|
1508
|
-
if (!t1 && !t2) {
|
|
1509
|
-
return {};
|
|
1510
|
-
}
|
|
1511
|
-
if (!t1 || !t2) {
|
|
1512
|
-
return t1 || t2;
|
|
1513
|
-
}
|
|
1514
|
-
const keys = Object.keys(t2);
|
|
1515
|
-
for(let i = 0; i < keys.length; i++){
|
|
1516
|
-
t1[keys[i]] = t2[keys[i]];
|
|
1598
|
+
|
|
1599
|
+
function errorHandler(input) {
|
|
1600
|
+
if (typeof input === 'function') {
|
|
1601
|
+
return {
|
|
1602
|
+
type: HandlerType.ERROR,
|
|
1603
|
+
fn: input
|
|
1604
|
+
};
|
|
1517
1605
|
}
|
|
1518
|
-
return
|
|
1606
|
+
return {
|
|
1607
|
+
type: HandlerType.ERROR,
|
|
1608
|
+
...input
|
|
1609
|
+
};
|
|
1519
1610
|
}
|
|
1520
1611
|
|
|
1521
|
-
function
|
|
1522
|
-
|
|
1523
|
-
return input;
|
|
1524
|
-
}
|
|
1525
|
-
const error = new Error();
|
|
1526
|
-
if (typeof input.message === 'string') {
|
|
1527
|
-
error.message = input.message;
|
|
1528
|
-
}
|
|
1529
|
-
return error;
|
|
1612
|
+
function isHandler(input) {
|
|
1613
|
+
return isObject(input) && typeof input.fn === 'function' && typeof input.type === 'string';
|
|
1530
1614
|
}
|
|
1531
1615
|
|
|
1532
1616
|
function decodeParam(val) {
|
|
@@ -1537,56 +1621,46 @@ function decodeParam(val) {
|
|
|
1537
1621
|
}
|
|
1538
1622
|
class PathMatcher {
|
|
1539
1623
|
test(path) {
|
|
1540
|
-
const fastSlash = this.path === '/' && this.regexpOptions.end === false;
|
|
1541
|
-
if (fastSlash) {
|
|
1542
|
-
return true;
|
|
1543
|
-
}
|
|
1544
1624
|
return this.regexp.test(path);
|
|
1545
1625
|
}
|
|
1546
1626
|
exec(path) {
|
|
1547
|
-
|
|
1548
|
-
const fastSlash = this.path === '/' && this.regexpOptions.end === false;
|
|
1549
|
-
if (fastSlash) {
|
|
1627
|
+
if (this.path === '/' && this.regexpOptions.end === false) {
|
|
1550
1628
|
return {
|
|
1551
1629
|
path: '/',
|
|
1552
1630
|
params: {}
|
|
1553
1631
|
};
|
|
1554
1632
|
}
|
|
1555
|
-
|
|
1556
|
-
if (!match) {
|
|
1557
|
-
return undefined;
|
|
1558
|
-
}
|
|
1559
|
-
if (this.path instanceof RegExp) {
|
|
1633
|
+
if (this.path === '*') {
|
|
1560
1634
|
return {
|
|
1561
1635
|
path,
|
|
1562
1636
|
params: {
|
|
1563
|
-
0: decodeParam(
|
|
1637
|
+
0: decodeParam(path)
|
|
1564
1638
|
}
|
|
1565
1639
|
};
|
|
1566
1640
|
}
|
|
1567
|
-
const
|
|
1641
|
+
const match = this.regexp.exec(path);
|
|
1642
|
+
if (!match) {
|
|
1643
|
+
return undefined;
|
|
1644
|
+
}
|
|
1645
|
+
const params = {};
|
|
1568
1646
|
for(let i = 1; i < match.length; i++){
|
|
1569
1647
|
const key = this.regexpKeys[i - 1];
|
|
1570
1648
|
const prop = key.name;
|
|
1571
1649
|
const val = decodeParam(match[i]);
|
|
1572
1650
|
if (typeof val !== 'undefined') {
|
|
1573
|
-
|
|
1651
|
+
params[prop] = val;
|
|
1574
1652
|
}
|
|
1575
1653
|
}
|
|
1576
1654
|
return {
|
|
1577
1655
|
path: match[0],
|
|
1578
|
-
params
|
|
1656
|
+
params
|
|
1579
1657
|
};
|
|
1580
1658
|
}
|
|
1581
1659
|
constructor(path, options){
|
|
1582
1660
|
this.regexpKeys = [];
|
|
1583
1661
|
this.path = path;
|
|
1584
1662
|
this.regexpOptions = options || {};
|
|
1585
|
-
|
|
1586
|
-
this.regexp = path;
|
|
1587
|
-
} else {
|
|
1588
|
-
this.regexp = pathToRegexp(path, this.regexpKeys, options);
|
|
1589
|
-
}
|
|
1663
|
+
this.regexp = pathToRegexp(path, this.regexpKeys, options);
|
|
1590
1664
|
}
|
|
1591
1665
|
}
|
|
1592
1666
|
|
|
@@ -1594,27 +1668,33 @@ function isPath(input) {
|
|
|
1594
1668
|
return typeof input === 'string' || input instanceof RegExp;
|
|
1595
1669
|
}
|
|
1596
1670
|
|
|
1671
|
+
const LayerSymbol = Symbol.for('Layer');
|
|
1672
|
+
|
|
1597
1673
|
class Layer {
|
|
1598
1674
|
// --------------------------------------------------
|
|
1599
|
-
|
|
1600
|
-
return this.
|
|
1675
|
+
get type() {
|
|
1676
|
+
return this.handler.type;
|
|
1677
|
+
}
|
|
1678
|
+
get path() {
|
|
1679
|
+
return this.handler.path;
|
|
1680
|
+
}
|
|
1681
|
+
get method() {
|
|
1682
|
+
return this.handler.method ? this.handler.method.toLowerCase() : undefined;
|
|
1601
1683
|
}
|
|
1602
1684
|
// --------------------------------------------------
|
|
1603
1685
|
dispatch(event, meta) {
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1686
|
+
if (this.pathMatcher) {
|
|
1687
|
+
const pathMatch = this.pathMatcher.exec(meta.path);
|
|
1688
|
+
if (pathMatch) {
|
|
1689
|
+
meta.params = mergeDispatcherMetaParams(meta.params, pathMatch.params);
|
|
1690
|
+
}
|
|
1609
1691
|
}
|
|
1610
|
-
|
|
1692
|
+
setRequestParams(event.req, meta.params);
|
|
1693
|
+
setRequestMountPath(event.req, meta.mountPath);
|
|
1694
|
+
setRequestRouterPath(event.req, meta.routerPath);
|
|
1611
1695
|
return new Promise((resolve, reject)=>{
|
|
1612
|
-
let timeoutInstance;
|
|
1613
1696
|
let handled = false;
|
|
1614
1697
|
const unsubscribe = ()=>{
|
|
1615
|
-
if (timeoutInstance) {
|
|
1616
|
-
clearTimeout(timeoutInstance);
|
|
1617
|
-
}
|
|
1618
1698
|
event.res.off('close', onFinished);
|
|
1619
1699
|
event.res.off('error', onFinished);
|
|
1620
1700
|
};
|
|
@@ -1634,30 +1714,23 @@ class Layer {
|
|
|
1634
1714
|
const onNext = (err)=>shutdown(false, err);
|
|
1635
1715
|
event.res.once('close', onFinished);
|
|
1636
1716
|
event.res.once('error', onFinished);
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
}
|
|
1717
|
+
const handle = (data)=>{
|
|
1718
|
+
if (typeof data === 'undefined' || handled) {
|
|
1719
|
+
return Promise.resolve();
|
|
1720
|
+
}
|
|
1721
|
+
handled = true;
|
|
1722
|
+
unsubscribe();
|
|
1723
|
+
return this.sendOutput(event.res, data).then(()=>resolve(true)).catch((e)=>reject(createError(e)));
|
|
1724
|
+
};
|
|
1646
1725
|
try {
|
|
1647
1726
|
let output;
|
|
1648
|
-
if (
|
|
1649
|
-
|
|
1727
|
+
if (this.handler.type === HandlerType.ERROR) {
|
|
1728
|
+
if (meta.error) {
|
|
1729
|
+
output = this.handler.fn(meta.error, event.req, event.res, onNext);
|
|
1730
|
+
}
|
|
1650
1731
|
} else {
|
|
1651
|
-
output = this.fn(event.req, event.res, onNext);
|
|
1732
|
+
output = this.handler.fn(event.req, event.res, onNext);
|
|
1652
1733
|
}
|
|
1653
|
-
const handle = (data)=>{
|
|
1654
|
-
if (typeof data === 'undefined' || handled) {
|
|
1655
|
-
return Promise.resolve();
|
|
1656
|
-
}
|
|
1657
|
-
handled = true;
|
|
1658
|
-
unsubscribe();
|
|
1659
|
-
return this.sendOutput(event.res, data).then(()=>resolve(true)).catch((e)=>reject(createError(e)));
|
|
1660
|
-
};
|
|
1661
1734
|
if (isPromise(output)) {
|
|
1662
1735
|
output.then((r)=>handle(r)).catch((e)=>reject(createError(e)));
|
|
1663
1736
|
return;
|
|
@@ -1670,7 +1743,7 @@ class Layer {
|
|
|
1670
1743
|
}
|
|
1671
1744
|
sendOutput(res, input) {
|
|
1672
1745
|
if (input instanceof Error) {
|
|
1673
|
-
return Promise.reject(input);
|
|
1746
|
+
return Promise.reject(createError(input));
|
|
1674
1747
|
}
|
|
1675
1748
|
if (isStream(input)) {
|
|
1676
1749
|
return sendStream(res, input);
|
|
@@ -1685,143 +1758,45 @@ class Layer {
|
|
|
1685
1758
|
}
|
|
1686
1759
|
// --------------------------------------------------
|
|
1687
1760
|
matchPath(path) {
|
|
1761
|
+
if (!this.pathMatcher) {
|
|
1762
|
+
return true;
|
|
1763
|
+
}
|
|
1688
1764
|
return this.pathMatcher.test(path);
|
|
1689
1765
|
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1766
|
+
matchMethod(method) {
|
|
1767
|
+
if (!this.method) {
|
|
1768
|
+
return true;
|
|
1769
|
+
}
|
|
1770
|
+
const name = method.toLowerCase();
|
|
1771
|
+
if (name === this.method) {
|
|
1772
|
+
return true;
|
|
1773
|
+
}
|
|
1774
|
+
return name === MethodName.HEAD && this.method === MethodName.GET;
|
|
1692
1775
|
}
|
|
1693
1776
|
// --------------------------------------------------
|
|
1694
|
-
constructor(
|
|
1695
|
-
this['@instanceof'] =
|
|
1696
|
-
this.
|
|
1697
|
-
|
|
1777
|
+
constructor(handler){
|
|
1778
|
+
this['@instanceof'] = LayerSymbol;
|
|
1779
|
+
this.handler = handler;
|
|
1780
|
+
if (handler.path) {
|
|
1781
|
+
this.pathMatcher = new PathMatcher(handler.path, {
|
|
1782
|
+
end: !!handler.method
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1698
1785
|
}
|
|
1699
1786
|
}
|
|
1700
1787
|
|
|
1701
1788
|
function isLayerInstance(input) {
|
|
1702
|
-
|
|
1703
|
-
return true;
|
|
1704
|
-
}
|
|
1705
|
-
return isInstance(input, 'Layer');
|
|
1789
|
+
return isInstance(input, LayerSymbol);
|
|
1706
1790
|
}
|
|
1707
1791
|
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
matchPath(path) {
|
|
1711
|
-
return this.pathMatcher.test(path);
|
|
1712
|
-
}
|
|
1713
|
-
matchMethod(method) {
|
|
1714
|
-
let name = method.toLowerCase();
|
|
1715
|
-
if (name === MethodName.HEAD && !hasOwnProperty(this.layers, name)) {
|
|
1716
|
-
name = MethodName.GET;
|
|
1717
|
-
}
|
|
1718
|
-
return Object.prototype.hasOwnProperty.call(this.layers, name);
|
|
1719
|
-
}
|
|
1720
|
-
// --------------------------------------------------
|
|
1721
|
-
getMethods() {
|
|
1722
|
-
const keys = Object.keys(this.layers);
|
|
1723
|
-
if (hasOwnProperty(this.layers, MethodName.GET) && !hasOwnProperty(this.layers, MethodName.HEAD)) {
|
|
1724
|
-
keys.push(MethodName.HEAD);
|
|
1725
|
-
}
|
|
1726
|
-
return keys;
|
|
1727
|
-
}
|
|
1728
|
-
// --------------------------------------------------
|
|
1729
|
-
async dispatch(event, meta) {
|
|
1730
|
-
/* istanbul ignore next */ if (!event.req.method) {
|
|
1731
|
-
return false;
|
|
1732
|
-
}
|
|
1733
|
-
let name = event.req.method.toLowerCase();
|
|
1734
|
-
if (name === MethodName.HEAD && !hasOwnProperty(this.layers, name)) {
|
|
1735
|
-
name = MethodName.GET;
|
|
1736
|
-
}
|
|
1737
|
-
const layers = this.layers[name];
|
|
1738
|
-
/* istanbul ignore next */ if (typeof layers === 'undefined' || layers.length === 0 || typeof meta.path === 'undefined') {
|
|
1739
|
-
return false;
|
|
1740
|
-
}
|
|
1741
|
-
const layerMeta = cloneDispatcherMeta(meta);
|
|
1742
|
-
const output = this.pathMatcher.exec(meta.path);
|
|
1743
|
-
if (output) {
|
|
1744
|
-
layerMeta.params = mergeDispatcherMetaParams(layerMeta.params, output.params);
|
|
1745
|
-
}
|
|
1746
|
-
let err;
|
|
1747
|
-
for(let i = 0; i < layers.length; i++){
|
|
1748
|
-
const layer = layers[i];
|
|
1749
|
-
if (err && !layer.isError()) {
|
|
1750
|
-
continue;
|
|
1751
|
-
}
|
|
1752
|
-
try {
|
|
1753
|
-
const dispatched = await layer.dispatch(event, {
|
|
1754
|
-
...layerMeta
|
|
1755
|
-
});
|
|
1756
|
-
if (dispatched) {
|
|
1757
|
-
return true;
|
|
1758
|
-
}
|
|
1759
|
-
} catch (e) {
|
|
1760
|
-
if (e instanceof Error) {
|
|
1761
|
-
err = e;
|
|
1762
|
-
}
|
|
1763
|
-
}
|
|
1764
|
-
}
|
|
1765
|
-
if (err) {
|
|
1766
|
-
throw err;
|
|
1767
|
-
}
|
|
1792
|
+
function isPluginInstallContext(input) {
|
|
1793
|
+
if (!isObject(input) || !isObject(input.options)) {
|
|
1768
1794
|
return false;
|
|
1769
1795
|
}
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
this.layers[method] = [];
|
|
1773
|
-
for(let i = 0; i < handlers.length; i++){
|
|
1774
|
-
const layer = new Layer({
|
|
1775
|
-
path: this.path,
|
|
1776
|
-
pathMatcher: this.pathMatcherOptions
|
|
1777
|
-
}, handlers[i]);
|
|
1778
|
-
this.layers[method].push(layer);
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
get(...handlers) {
|
|
1782
|
-
return this.register(MethodName.GET, ...handlers);
|
|
1783
|
-
}
|
|
1784
|
-
post(...handlers) {
|
|
1785
|
-
return this.register(MethodName.POST, ...handlers);
|
|
1786
|
-
}
|
|
1787
|
-
put(...handlers) {
|
|
1788
|
-
return this.register(MethodName.PUT, ...handlers);
|
|
1789
|
-
}
|
|
1790
|
-
patch(...handlers) {
|
|
1791
|
-
return this.register(MethodName.PATCH, ...handlers);
|
|
1792
|
-
}
|
|
1793
|
-
delete(...handlers) {
|
|
1794
|
-
return this.register(MethodName.DELETE, ...handlers);
|
|
1795
|
-
}
|
|
1796
|
-
head(...handlers) {
|
|
1797
|
-
return this.register(MethodName.HEAD, ...handlers);
|
|
1798
|
-
}
|
|
1799
|
-
options(...handlers) {
|
|
1800
|
-
return this.register(MethodName.OPTIONS, ...handlers);
|
|
1801
|
-
}
|
|
1802
|
-
// --------------------------------------------------
|
|
1803
|
-
isStrictPath() {
|
|
1804
|
-
return typeof this.path !== 'string' || this.path !== '/' && this.path.length !== 0;
|
|
1805
|
-
}
|
|
1806
|
-
// --------------------------------------------------
|
|
1807
|
-
constructor(options){
|
|
1808
|
-
this['@instanceof'] = Symbol.for('Route');
|
|
1809
|
-
this.layers = {};
|
|
1810
|
-
this.path = options.path;
|
|
1811
|
-
this.pathMatcherOptions = {
|
|
1812
|
-
end: true,
|
|
1813
|
-
strict: this.isStrictPath(),
|
|
1814
|
-
...options.pathMatcher
|
|
1815
|
-
};
|
|
1816
|
-
this.pathMatcher = new PathMatcher(this.path, this.pathMatcherOptions);
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
function isRouteInstance(input) {
|
|
1821
|
-
if (input instanceof Route) {
|
|
1822
|
-
return true;
|
|
1796
|
+
if (typeof input.name !== 'undefined' && typeof input.name !== 'string') {
|
|
1797
|
+
return false;
|
|
1823
1798
|
}
|
|
1824
|
-
return
|
|
1799
|
+
return typeof input.path === 'undefined' || isPath(input.path);
|
|
1825
1800
|
}
|
|
1826
1801
|
|
|
1827
1802
|
function transformRouterOptions(input) {
|
|
@@ -1834,17 +1809,16 @@ function transformRouterOptions(input) {
|
|
|
1834
1809
|
return input;
|
|
1835
1810
|
}
|
|
1836
1811
|
|
|
1812
|
+
const RouterSymbol = Symbol.for('Router');
|
|
1813
|
+
|
|
1837
1814
|
let nextId = 0;
|
|
1838
1815
|
function generateRouterID() {
|
|
1839
1816
|
return ++nextId;
|
|
1840
1817
|
}
|
|
1841
|
-
|
|
1842
1818
|
function isRouterInstance(input) {
|
|
1843
|
-
|
|
1844
|
-
return true;
|
|
1845
|
-
}
|
|
1846
|
-
return isInstance(input, 'Router');
|
|
1819
|
+
return isInstance(input, RouterSymbol);
|
|
1847
1820
|
}
|
|
1821
|
+
|
|
1848
1822
|
class Router {
|
|
1849
1823
|
// --------------------------------------------------
|
|
1850
1824
|
setPath(value) {
|
|
@@ -1858,9 +1832,7 @@ class Router {
|
|
|
1858
1832
|
path = value;
|
|
1859
1833
|
}
|
|
1860
1834
|
this.pathMatcher = new PathMatcher(path, {
|
|
1861
|
-
end: false
|
|
1862
|
-
sensitive: false,
|
|
1863
|
-
...this.pathMatcherOptions ? this.pathMatcherOptions : {}
|
|
1835
|
+
end: false
|
|
1864
1836
|
});
|
|
1865
1837
|
}
|
|
1866
1838
|
// --------------------------------------------------
|
|
@@ -1871,78 +1843,60 @@ class Router {
|
|
|
1871
1843
|
return true;
|
|
1872
1844
|
}
|
|
1873
1845
|
// --------------------------------------------------
|
|
1874
|
-
async dispatch(event, meta
|
|
1846
|
+
async dispatch(event, meta) {
|
|
1875
1847
|
const allowedMethods = [];
|
|
1876
|
-
let path = meta.path || useRequestPath(event.req);
|
|
1877
1848
|
if (this.pathMatcher) {
|
|
1878
|
-
const output = this.pathMatcher.exec(path);
|
|
1849
|
+
const output = this.pathMatcher.exec(meta.path);
|
|
1879
1850
|
if (typeof output !== 'undefined') {
|
|
1880
|
-
meta.mountPath = cleanDoubleSlashes(`${meta.mountPath
|
|
1881
|
-
if (path === output.path) {
|
|
1882
|
-
path = '/';
|
|
1851
|
+
meta.mountPath = cleanDoubleSlashes(`${meta.mountPath}/${output.path}`);
|
|
1852
|
+
if (meta.path === output.path) {
|
|
1853
|
+
meta.path = '/';
|
|
1883
1854
|
} else {
|
|
1884
|
-
path = withLeadingSlash(path.substring(output.path.length));
|
|
1855
|
+
meta.path = withLeadingSlash(meta.path.substring(output.path.length));
|
|
1885
1856
|
}
|
|
1886
|
-
meta.params =
|
|
1857
|
+
meta.params = {
|
|
1858
|
+
...meta.params,
|
|
1859
|
+
...output.params
|
|
1860
|
+
};
|
|
1887
1861
|
}
|
|
1888
1862
|
}
|
|
1889
|
-
meta.
|
|
1890
|
-
if (meta.routerIds) {
|
|
1891
|
-
meta.routerIds.push(this.id);
|
|
1892
|
-
} else {
|
|
1893
|
-
meta.routerIds = [
|
|
1894
|
-
this.id
|
|
1895
|
-
];
|
|
1896
|
-
}
|
|
1897
|
-
if (!meta.mountPath) {
|
|
1898
|
-
meta.mountPath = '/';
|
|
1899
|
-
}
|
|
1863
|
+
meta.routerPath.push(this.id);
|
|
1900
1864
|
let err;
|
|
1901
|
-
let
|
|
1865
|
+
let item;
|
|
1866
|
+
let itemMeta;
|
|
1902
1867
|
let match = false;
|
|
1903
1868
|
for(let i = 0; i < this.stack.length; i++){
|
|
1904
|
-
|
|
1905
|
-
if (
|
|
1906
|
-
if (
|
|
1869
|
+
item = this.stack[i];
|
|
1870
|
+
if (isLayerInstance(item)) {
|
|
1871
|
+
if (item.type !== HandlerType.ERROR && err) {
|
|
1907
1872
|
continue;
|
|
1908
1873
|
}
|
|
1909
|
-
match =
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
if (event.req.method && !layer.matchMethod(event.req.method)) {
|
|
1917
|
-
match = false;
|
|
1918
|
-
if (event.req.method.toLowerCase() === MethodName.OPTIONS) {
|
|
1919
|
-
allowedMethods.push(...layer.getMethods());
|
|
1874
|
+
match = item.matchPath(meta.path);
|
|
1875
|
+
if (match && event.req.method) {
|
|
1876
|
+
if (!item.matchMethod(event.req.method)) {
|
|
1877
|
+
match = false;
|
|
1878
|
+
}
|
|
1879
|
+
if (item.method) {
|
|
1880
|
+
allowedMethods.push(item.method);
|
|
1920
1881
|
}
|
|
1921
1882
|
}
|
|
1883
|
+
} else if (isRouterInstance(item)) {
|
|
1884
|
+
match = item.matchPath(meta.path);
|
|
1922
1885
|
}
|
|
1923
1886
|
if (!match) {
|
|
1924
1887
|
continue;
|
|
1925
1888
|
}
|
|
1926
|
-
|
|
1927
|
-
if (
|
|
1928
|
-
|
|
1929
|
-
if (output) {
|
|
1930
|
-
layerMeta.params = mergeDispatcherMetaParams(layerMeta.params, output.params);
|
|
1931
|
-
layerMeta.mountPath = cleanDoubleSlashes(`${layerMeta.mountPath || ''}/${output.path}`);
|
|
1932
|
-
}
|
|
1933
|
-
if (err) {
|
|
1934
|
-
layerMeta.error = err;
|
|
1935
|
-
}
|
|
1936
|
-
} else if (err) {
|
|
1937
|
-
continue;
|
|
1889
|
+
itemMeta = cloneDispatcherMeta(meta);
|
|
1890
|
+
if (err) {
|
|
1891
|
+
itemMeta.error = err;
|
|
1938
1892
|
}
|
|
1939
1893
|
try {
|
|
1940
|
-
const dispatched = await
|
|
1894
|
+
const dispatched = await item.dispatch(event, itemMeta);
|
|
1941
1895
|
if (dispatched) {
|
|
1942
1896
|
return true;
|
|
1943
1897
|
}
|
|
1944
1898
|
} catch (e) {
|
|
1945
|
-
if (e
|
|
1899
|
+
if (isError(e)) {
|
|
1946
1900
|
err = e;
|
|
1947
1901
|
}
|
|
1948
1902
|
}
|
|
@@ -1951,7 +1905,11 @@ class Router {
|
|
|
1951
1905
|
throw err;
|
|
1952
1906
|
}
|
|
1953
1907
|
if (event.req.method && event.req.method.toLowerCase() === MethodName.OPTIONS) {
|
|
1954
|
-
|
|
1908
|
+
if (allowedMethods.indexOf(MethodName.GET) !== -1) {
|
|
1909
|
+
allowedMethods.push(MethodName.HEAD);
|
|
1910
|
+
}
|
|
1911
|
+
distinctArray(allowedMethods);
|
|
1912
|
+
const options = allowedMethods.map((key)=>key.toUpperCase()).join(',');
|
|
1955
1913
|
if (!isResponseGone(event.res)) {
|
|
1956
1914
|
event.res.setHeader(HeaderName.ALLOW, options);
|
|
1957
1915
|
await send(event.res, options);
|
|
@@ -1960,71 +1918,128 @@ class Router {
|
|
|
1960
1918
|
}
|
|
1961
1919
|
return false;
|
|
1962
1920
|
}
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1921
|
+
delete(path, handler) {
|
|
1922
|
+
if (isPath(path)) {
|
|
1923
|
+
this.use({
|
|
1924
|
+
...handler,
|
|
1925
|
+
method: MethodName.DELETE,
|
|
1926
|
+
path
|
|
1927
|
+
});
|
|
1928
|
+
return this;
|
|
1967
1929
|
}
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
}
|
|
1972
|
-
const route = new Route({
|
|
1973
|
-
path,
|
|
1974
|
-
pathMatcher: {
|
|
1975
|
-
...this.pathMatcherOptions ? {
|
|
1976
|
-
sensitive: this.pathMatcherOptions.sensitive
|
|
1977
|
-
} : {}
|
|
1978
|
-
}
|
|
1930
|
+
this.use({
|
|
1931
|
+
...path,
|
|
1932
|
+
method: MethodName.DELETE
|
|
1979
1933
|
});
|
|
1980
|
-
this.stack.push(route);
|
|
1981
|
-
return route;
|
|
1982
|
-
}
|
|
1983
|
-
delete(path, ...handlers) {
|
|
1984
|
-
const route = this.route(path);
|
|
1985
|
-
route.delete(...handlers);
|
|
1986
1934
|
return this;
|
|
1987
1935
|
}
|
|
1988
|
-
get(path,
|
|
1989
|
-
|
|
1990
|
-
|
|
1936
|
+
get(path, handler) {
|
|
1937
|
+
if (isPath(path)) {
|
|
1938
|
+
this.use({
|
|
1939
|
+
...handler,
|
|
1940
|
+
method: MethodName.GET,
|
|
1941
|
+
path
|
|
1942
|
+
});
|
|
1943
|
+
return this;
|
|
1944
|
+
}
|
|
1945
|
+
this.use({
|
|
1946
|
+
...path,
|
|
1947
|
+
method: MethodName.GET
|
|
1948
|
+
});
|
|
1991
1949
|
return this;
|
|
1992
1950
|
}
|
|
1993
|
-
post(path,
|
|
1994
|
-
|
|
1995
|
-
|
|
1951
|
+
post(path, handler) {
|
|
1952
|
+
if (isPath(path)) {
|
|
1953
|
+
this.use({
|
|
1954
|
+
...handler,
|
|
1955
|
+
method: MethodName.POST,
|
|
1956
|
+
path
|
|
1957
|
+
});
|
|
1958
|
+
return this;
|
|
1959
|
+
}
|
|
1960
|
+
this.use({
|
|
1961
|
+
...path,
|
|
1962
|
+
method: MethodName.POST
|
|
1963
|
+
});
|
|
1996
1964
|
return this;
|
|
1997
1965
|
}
|
|
1998
|
-
put(path,
|
|
1999
|
-
|
|
2000
|
-
|
|
1966
|
+
put(path, handler) {
|
|
1967
|
+
if (isPath(path)) {
|
|
1968
|
+
this.use({
|
|
1969
|
+
...handler,
|
|
1970
|
+
method: MethodName.PUT,
|
|
1971
|
+
path
|
|
1972
|
+
});
|
|
1973
|
+
return this;
|
|
1974
|
+
}
|
|
1975
|
+
this.use({
|
|
1976
|
+
...path,
|
|
1977
|
+
method: MethodName.PUT
|
|
1978
|
+
});
|
|
2001
1979
|
return this;
|
|
2002
1980
|
}
|
|
2003
|
-
patch(path,
|
|
2004
|
-
|
|
2005
|
-
|
|
1981
|
+
patch(path, handler) {
|
|
1982
|
+
if (isPath(path)) {
|
|
1983
|
+
this.use({
|
|
1984
|
+
...handler,
|
|
1985
|
+
method: MethodName.PATCH,
|
|
1986
|
+
path
|
|
1987
|
+
});
|
|
1988
|
+
return this;
|
|
1989
|
+
}
|
|
1990
|
+
this.use({
|
|
1991
|
+
...path,
|
|
1992
|
+
method: MethodName.PATCH
|
|
1993
|
+
});
|
|
2006
1994
|
return this;
|
|
2007
1995
|
}
|
|
2008
|
-
head(path,
|
|
2009
|
-
|
|
2010
|
-
|
|
1996
|
+
head(path, handler) {
|
|
1997
|
+
if (isPath(path)) {
|
|
1998
|
+
this.use({
|
|
1999
|
+
...handler,
|
|
2000
|
+
method: MethodName.HEAD,
|
|
2001
|
+
path
|
|
2002
|
+
});
|
|
2003
|
+
return this;
|
|
2004
|
+
}
|
|
2005
|
+
this.use({
|
|
2006
|
+
...path,
|
|
2007
|
+
method: MethodName.HEAD
|
|
2008
|
+
});
|
|
2011
2009
|
return this;
|
|
2012
2010
|
}
|
|
2013
|
-
options(path,
|
|
2014
|
-
|
|
2015
|
-
|
|
2011
|
+
options(path, handler) {
|
|
2012
|
+
if (isPath(path)) {
|
|
2013
|
+
this.use({
|
|
2014
|
+
...handler,
|
|
2015
|
+
method: MethodName.OPTIONS,
|
|
2016
|
+
path
|
|
2017
|
+
});
|
|
2018
|
+
return this;
|
|
2019
|
+
}
|
|
2020
|
+
this.use({
|
|
2021
|
+
...path,
|
|
2022
|
+
method: MethodName.OPTIONS
|
|
2023
|
+
});
|
|
2016
2024
|
return this;
|
|
2017
2025
|
}
|
|
2018
2026
|
use(...input) {
|
|
2019
2027
|
/* istanbul ignore next */ if (input.length === 0) {
|
|
2020
2028
|
return this;
|
|
2021
2029
|
}
|
|
2030
|
+
const modifyPath = (input)=>{
|
|
2031
|
+
if (typeof input === 'string') {
|
|
2032
|
+
return withLeadingSlash(input);
|
|
2033
|
+
}
|
|
2034
|
+
return input;
|
|
2035
|
+
};
|
|
2022
2036
|
let path;
|
|
2023
|
-
if (isPath(input[0])) {
|
|
2024
|
-
path = input.shift();
|
|
2025
|
-
}
|
|
2026
2037
|
for(let i = 0; i < input.length; i++){
|
|
2027
2038
|
const item = input[i];
|
|
2039
|
+
if (isPath(item)) {
|
|
2040
|
+
path = modifyPath(item);
|
|
2041
|
+
continue;
|
|
2042
|
+
}
|
|
2028
2043
|
if (isRouterInstance(item)) {
|
|
2029
2044
|
if (path) {
|
|
2030
2045
|
item.setPath(path);
|
|
@@ -2032,35 +2047,51 @@ class Router {
|
|
|
2032
2047
|
this.stack.push(item);
|
|
2033
2048
|
continue;
|
|
2034
2049
|
}
|
|
2035
|
-
if (
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
pathMatcher: {
|
|
2039
|
-
strict: false,
|
|
2040
|
-
end: false,
|
|
2041
|
-
...this.pathMatcherOptions ? {
|
|
2042
|
-
sensitive: this.pathMatcherOptions.sensitive
|
|
2043
|
-
} : {}
|
|
2044
|
-
}
|
|
2045
|
-
}, item));
|
|
2050
|
+
if (isHandler(item)) {
|
|
2051
|
+
item.path = path || modifyPath(item.path);
|
|
2052
|
+
this.stack.push(new Layer(item));
|
|
2046
2053
|
}
|
|
2047
2054
|
}
|
|
2048
2055
|
return this;
|
|
2049
2056
|
}
|
|
2050
2057
|
// --------------------------------------------------
|
|
2058
|
+
install(plugin, context) {
|
|
2059
|
+
if (isPluginInstallContext(context)) {
|
|
2060
|
+
const name = context.name || plugin.name;
|
|
2061
|
+
if (context.path) {
|
|
2062
|
+
const router = new Router({
|
|
2063
|
+
name
|
|
2064
|
+
});
|
|
2065
|
+
plugin.install(router, context.options);
|
|
2066
|
+
this.use(context.path, router);
|
|
2067
|
+
return this;
|
|
2068
|
+
}
|
|
2069
|
+
plugin.install(this, context.options);
|
|
2070
|
+
return this;
|
|
2071
|
+
}
|
|
2072
|
+
plugin.install(this, context);
|
|
2073
|
+
return this;
|
|
2074
|
+
}
|
|
2075
|
+
uninstall(name) {
|
|
2076
|
+
const index = this.stack.findIndex((el)=>isRouterInstance(el) && el.name === name);
|
|
2077
|
+
if (index !== -1) {
|
|
2078
|
+
this.stack.splice(index, 1);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
// --------------------------------------------------
|
|
2051
2082
|
constructor(options = {}){
|
|
2052
|
-
this['@instanceof'] =
|
|
2083
|
+
this['@instanceof'] = RouterSymbol;
|
|
2053
2084
|
/**
|
|
2054
2085
|
* Array of mounted layers, routes & routers.
|
|
2055
2086
|
*
|
|
2056
2087
|
* @protected
|
|
2057
2088
|
*/ this.stack = [];
|
|
2058
2089
|
this.id = generateRouterID();
|
|
2059
|
-
this.
|
|
2090
|
+
this.name = options.name;
|
|
2060
2091
|
this.setPath(options.path);
|
|
2061
2092
|
setRouterOptions(this.id, transformRouterOptions(options));
|
|
2062
2093
|
}
|
|
2063
2094
|
}
|
|
2064
2095
|
|
|
2065
|
-
export { HeaderName, Layer, MethodName, PathMatcher,
|
|
2096
|
+
export { ErrorProxy, HandlerType, HeaderName, Layer, MethodName, PathMatcher, Router, appendResponseHeader, appendResponseHeaderDirective, buildDispatcherMeta, cloneDispatcherMeta, cloneDispatcherMetaParams, coreHandler, createError, createNodeDispatcher, createRawDispatcher, createRequest, createResponse, createWebDispatcher, dispatchNodeRequest, dispatchRawRequest, dispatchWebRequest, errorHandler, extendRequestBody, extendRequestCookies, extendRequestQuery, getRequestAcceptableCharset, getRequestAcceptableCharsets, getRequestAcceptableContentType, getRequestAcceptableContentTypes, getRequestAcceptableEncoding, getRequestAcceptableEncodings, getRequestAcceptableLanguage, getRequestAcceptableLanguages, getRequestHeader, getRequestHostName, getRequestIP, getRequestProtocol, hasRequestBody, hasRequestCookies, hasRequestQuery, isError, isHandler, isLayerInstance, isPath, isPluginInstallContext, isRequestCacheable, isResponseGone, isRouterInstance, matchRequestContentType, mergeDispatcherMetaParams, send, sendAccepted, sendCreated, sendFile, sendFormat, sendRedirect, sendStream, sendWebBlob, sendWebResponse, setRequestBody, setRequestCookies, setRequestEnv, setRequestHeader, setRequestMountPath, setRequestParam, setRequestParams, setRequestQuery, setRequestRouterPath, setResponseCacheHeaders, setResponseContentTypeByFileName, setResponseHeaderAttachment, setResponseHeaderContentType, unsetRequestEnv, useRequestBody, useRequestCookie, useRequestCookies, useRequestEnv, useRequestMountPath, useRequestNegotiator, useRequestParam, useRequestParams, useRequestPath, useRequestQuery, useRequestRouterPath };
|
|
2066
2097
|
//# sourceMappingURL=index.mjs.map
|