routup 1.0.2 → 2.0.0
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 +92 -8
- package/dist/dispatcher/adapters/index.d.ts +3 -0
- package/dist/dispatcher/adapters/node/index.d.ts +1 -0
- package/dist/dispatcher/adapters/node/module.d.ts +6 -0
- package/dist/dispatcher/adapters/raw/module.d.ts +4 -0
- package/dist/dispatcher/adapters/raw/type.d.ts +18 -0
- package/dist/dispatcher/adapters/web/index.d.ts +2 -0
- package/dist/dispatcher/adapters/web/module.d.ts +5 -0
- package/dist/dispatcher/adapters/web/type.d.ts +3 -0
- package/dist/dispatcher/index.d.ts +3 -0
- package/dist/dispatcher/type.d.ts +30 -0
- package/dist/dispatcher/utils.d.ts +4 -0
- package/dist/error.d.ts +1 -0
- package/dist/index.cjs +1075 -538
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.mjs +1054 -509
- package/dist/index.mjs.map +1 -1
- package/dist/layer/module.d.ts +5 -4
- package/dist/layer/type.d.ts +1 -2
- package/dist/layer/utils.d.ts +1 -1
- package/dist/path/index.d.ts +1 -0
- package/dist/path/matcher.d.ts +1 -2
- package/dist/path/type.d.ts +1 -0
- package/dist/path/utils.d.ts +2 -0
- package/dist/{helpers/request → request/helpers}/body.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/cache.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/cookie.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/env.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/header-accept-charset.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/header-accept-language.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/header-accept.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/header-content-type.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/header.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/hostname.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/index.d.ts +1 -0
- package/dist/{helpers/request → request/helpers}/ip.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/mount-path.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/negotiator.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/params.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/path.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/protocol.d.ts +1 -1
- package/dist/{helpers/request → request/helpers}/query.d.ts +1 -1
- package/dist/request/helpers/router.d.ts +3 -0
- package/dist/request/index.d.ts +3 -0
- package/dist/request/module.d.ts +4 -0
- package/dist/request/types.d.ts +9 -0
- package/dist/{helpers/response → response/helpers}/cache.d.ts +1 -1
- package/dist/response/helpers/gone.d.ts +2 -0
- package/dist/{helpers/response → response/helpers}/header-attachment.d.ts +1 -1
- package/dist/{helpers/response → response/helpers}/header-content-type.d.ts +1 -1
- package/dist/{helpers/response → response/helpers}/header.d.ts +1 -1
- package/dist/{helpers/response → response/helpers}/index.d.ts +3 -0
- package/dist/{helpers/response → response/helpers}/send-accepted.d.ts +2 -2
- package/dist/{helpers/response → response/helpers}/send-created.d.ts +2 -2
- package/dist/response/helpers/send-file.d.ts +17 -0
- package/dist/{helpers/response → response/helpers}/send-format.d.ts +1 -1
- package/dist/response/helpers/send-redirect.d.ts +2 -0
- package/dist/response/helpers/send-stream.d.ts +2 -0
- package/dist/response/helpers/send-web-blob.d.ts +2 -0
- package/dist/response/helpers/send-web-response.d.ts +2 -0
- package/dist/response/helpers/send.d.ts +2 -0
- package/dist/response/helpers/utils.d.ts +2 -0
- package/dist/response/index.d.ts +2 -0
- package/dist/response/module.d.ts +2 -0
- package/dist/route/module.d.ts +6 -5
- package/dist/route/type.d.ts +1 -1
- package/dist/route/utils.d.ts +1 -1
- package/dist/router/index.d.ts +0 -1
- package/dist/router/module.d.ts +13 -32
- package/dist/router/utils.d.ts +1 -0
- package/dist/router-options/index.d.ts +2 -0
- package/dist/router-options/module.d.ts +4 -0
- package/dist/router-options/transform.d.ts +2 -0
- package/dist/router-options/type.d.ts +50 -0
- package/dist/types.d.ts +19 -0
- package/dist/utils/cookie.d.ts +1 -0
- package/dist/utils/etag/module.d.ts +4 -3
- package/dist/utils/etag/type.d.ts +1 -1
- package/dist/utils/header.d.ts +3 -0
- package/dist/utils/index.d.ts +4 -1
- package/dist/utils/path.d.ts +5 -2
- package/dist/utils/stream.d.ts +8 -0
- package/dist/utils/web.d.ts +3 -0
- package/package.json +17 -16
- package/dist/config/module.d.ts +0 -8
- package/dist/config/type.d.ts +0 -34
- package/dist/handler/index.d.ts +0 -1
- package/dist/handler/utils.d.ts +0 -2
- package/dist/helpers/index.d.ts +0 -2
- package/dist/helpers/response/send-file.d.ts +0 -9
- package/dist/helpers/response/send-redirect.d.ts +0 -2
- package/dist/helpers/response/send-stream.d.ts +0 -4
- package/dist/helpers/response/send.d.ts +0 -2
- package/dist/helpers/response/utils.d.ts +0 -3
- package/dist/router/type.d.ts +0 -24
- package/dist/type.d.ts +0 -24
- package/dist/utils/request.d.ts +0 -2
- /package/dist/{config → dispatcher/adapters/raw}/index.d.ts +0 -0
- /package/dist/{helpers/request → request/helpers}/header-accept-encoding.d.ts +0 -0
package/dist/index.mjs
CHANGED
|
@@ -1,17 +1,231 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import zod from 'zod';
|
|
4
|
-
import crypto from 'node:crypto';
|
|
5
|
-
import { Stats, stat, createReadStream } from 'node:fs';
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import { subtle } from 'uncrypto';
|
|
6
3
|
import { merge, hasOwnProperty, distinctArray } from 'smob';
|
|
7
|
-
import
|
|
4
|
+
import { compile, all } from 'proxy-addr';
|
|
8
5
|
import { getType, get } from 'mime-explorer';
|
|
9
|
-
import { GatewayTimeoutErrorOptions, BadRequestError } from '@ebec/http';
|
|
10
6
|
import Negotiator from 'negotiator';
|
|
11
|
-
import {
|
|
12
|
-
import path from 'node:path';
|
|
7
|
+
import { Writable, Readable } from 'readable-stream';
|
|
13
8
|
import { pathToRegexp } from 'path-to-regexp';
|
|
14
|
-
|
|
9
|
+
|
|
10
|
+
var MethodName;
|
|
11
|
+
(function(MethodName) {
|
|
12
|
+
MethodName["GET"] = 'get';
|
|
13
|
+
MethodName["POST"] = 'post';
|
|
14
|
+
MethodName["PUT"] = 'put';
|
|
15
|
+
MethodName["PATCH"] = 'patch';
|
|
16
|
+
MethodName["DELETE"] = 'delete';
|
|
17
|
+
MethodName["OPTIONS"] = 'options';
|
|
18
|
+
MethodName["HEAD"] = 'head';
|
|
19
|
+
})(MethodName || (MethodName = {}));
|
|
20
|
+
var HeaderName;
|
|
21
|
+
(function(HeaderName) {
|
|
22
|
+
HeaderName["ACCEPT"] = 'accept';
|
|
23
|
+
HeaderName["ACCEPT_CHARSET"] = 'accept-charset';
|
|
24
|
+
HeaderName["ACCEPT_ENCODING"] = 'accept-encoding';
|
|
25
|
+
HeaderName["ACCEPT_LANGUAGE"] = 'accept-language';
|
|
26
|
+
HeaderName["ACCEPT_RANGES"] = 'accept-ranges';
|
|
27
|
+
HeaderName["ALLOW"] = 'allow';
|
|
28
|
+
HeaderName["CACHE_CONTROL"] = 'cache-control';
|
|
29
|
+
HeaderName["CONTENT_DISPOSITION"] = 'content-disposition';
|
|
30
|
+
HeaderName["CONTENT_ENCODING"] = 'content-encoding';
|
|
31
|
+
HeaderName["CONTENT_LENGTH"] = 'content-length';
|
|
32
|
+
HeaderName["CONTENT_RANGE"] = 'content-range';
|
|
33
|
+
HeaderName["CONTENT_TYPE"] = 'content-type';
|
|
34
|
+
HeaderName["COOKIE"] = 'cookie';
|
|
35
|
+
HeaderName["ETag"] = 'etag';
|
|
36
|
+
HeaderName["HOST"] = 'host';
|
|
37
|
+
HeaderName["IF_MODIFIED_SINCE"] = 'if-modified-since';
|
|
38
|
+
HeaderName["IF_NONE_MATCH"] = 'if-none-match';
|
|
39
|
+
HeaderName["LAST_MODIFIED"] = 'last-modified';
|
|
40
|
+
HeaderName["LOCATION"] = 'location';
|
|
41
|
+
HeaderName["RANGE"] = 'range';
|
|
42
|
+
HeaderName["RATE_LIMIT_LIMIT"] = 'ratelimit-limit';
|
|
43
|
+
HeaderName["RATE_LIMIT_REMAINING"] = 'ratelimit-remaining';
|
|
44
|
+
HeaderName["RATE_LIMIT_RESET"] = 'ratelimit-reset';
|
|
45
|
+
HeaderName["RETRY_AFTER"] = 'retry-after';
|
|
46
|
+
HeaderName["SET_COOKIE"] = 'set-cookie';
|
|
47
|
+
HeaderName["TRANSFER_ENCODING"] = 'transfer-encoding';
|
|
48
|
+
HeaderName["X_FORWARDED_HOST"] = 'x-forwarded-host';
|
|
49
|
+
HeaderName["X_FORWARDED_FOR"] = 'x-forwarded-for';
|
|
50
|
+
HeaderName["X_FORWARDED_PROTO"] = 'x-forwarded-proto';
|
|
51
|
+
})(HeaderName || (HeaderName = {}));
|
|
52
|
+
|
|
53
|
+
function setResponseCacheHeaders(res, options) {
|
|
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(', '));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const GoneSymbol = Symbol.for('ResGone');
|
|
69
|
+
function isResponseGone(res) {
|
|
70
|
+
if (res.headersSent || res.writableEnded) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (GoneSymbol in res) {
|
|
74
|
+
return res[GoneSymbol];
|
|
75
|
+
}
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function appendResponseHeader(res, name, value) {
|
|
80
|
+
let header = res.getHeader(name);
|
|
81
|
+
if (!header) {
|
|
82
|
+
res.setHeader(name, value);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (!Array.isArray(header)) {
|
|
86
|
+
header = [
|
|
87
|
+
header.toString()
|
|
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
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (Array.isArray(value)) {
|
|
117
|
+
header.push(...value);
|
|
118
|
+
} else {
|
|
119
|
+
header.push(`${value}`);
|
|
120
|
+
}
|
|
121
|
+
header = [
|
|
122
|
+
...new Set(header)
|
|
123
|
+
];
|
|
124
|
+
res.setHeader(name, header.join('; '));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/*
|
|
128
|
+
Set-Cookie header field-values are sometimes comma joined in one string. This splits them without choking on commas
|
|
129
|
+
that are within a single set-cookie field-value, such as in the Expires portion.
|
|
130
|
+
|
|
131
|
+
This is uncommon, but explicitly allowed - see https://tools.ietf.org/html/rfc2616#section-4.2
|
|
132
|
+
Node.js does this for every header *except* set-cookie - see https://github.com/nodejs/node/blob/d5e363b77ebaf1caf67cd7528224b651c86815c1/lib/_http_incoming.js#L128
|
|
133
|
+
React Native's fetch does this for *every* header, including set-cookie.
|
|
134
|
+
|
|
135
|
+
Based on: https://github.com/google/j2objc/commit/16820fdbc8f76ca0c33472810ce0cb03d20efe25
|
|
136
|
+
Credits to: https://github.com/tomball for original and https://github.com/chrusart for JavaScript implementation
|
|
137
|
+
*/ function splitCookiesString(input) {
|
|
138
|
+
if (Array.isArray(input)) {
|
|
139
|
+
return input.flatMap((el)=>splitCookiesString(el));
|
|
140
|
+
}
|
|
141
|
+
if (typeof input !== 'string') {
|
|
142
|
+
return [];
|
|
143
|
+
}
|
|
144
|
+
const cookiesStrings = [];
|
|
145
|
+
let pos = 0;
|
|
146
|
+
let start;
|
|
147
|
+
let ch;
|
|
148
|
+
let lastComma;
|
|
149
|
+
let nextStart;
|
|
150
|
+
let cookiesSeparatorFound;
|
|
151
|
+
const skipWhitespace = ()=>{
|
|
152
|
+
while(pos < input.length && /\s/.test(input.charAt(pos))){
|
|
153
|
+
pos += 1;
|
|
154
|
+
}
|
|
155
|
+
return pos < input.length;
|
|
156
|
+
};
|
|
157
|
+
const notSpecialChar = ()=>{
|
|
158
|
+
ch = input.charAt(pos);
|
|
159
|
+
return ch !== '=' && ch !== ';' && ch !== ',';
|
|
160
|
+
};
|
|
161
|
+
while(pos < input.length){
|
|
162
|
+
start = pos;
|
|
163
|
+
cookiesSeparatorFound = false;
|
|
164
|
+
while(skipWhitespace()){
|
|
165
|
+
ch = input.charAt(pos);
|
|
166
|
+
if (ch === ',') {
|
|
167
|
+
// ',' is a cookie separator if we have later first '=', not ';' or ','
|
|
168
|
+
lastComma = pos;
|
|
169
|
+
pos += 1;
|
|
170
|
+
skipWhitespace();
|
|
171
|
+
nextStart = pos;
|
|
172
|
+
while(pos < input.length && notSpecialChar()){
|
|
173
|
+
pos += 1;
|
|
174
|
+
}
|
|
175
|
+
// currently special character
|
|
176
|
+
if (pos < input.length && input.charAt(pos) === '=') {
|
|
177
|
+
// we found cookies separator
|
|
178
|
+
cookiesSeparatorFound = true;
|
|
179
|
+
// pos is inside the next cookie, so back up and return it.
|
|
180
|
+
pos = nextStart;
|
|
181
|
+
cookiesStrings.push(input.substring(start, lastComma));
|
|
182
|
+
start = pos;
|
|
183
|
+
} else {
|
|
184
|
+
// in param ',' or param separator ';',
|
|
185
|
+
// we continue from that comma
|
|
186
|
+
pos = lastComma + 1;
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
pos += 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!cookiesSeparatorFound || pos >= input.length) {
|
|
193
|
+
cookiesStrings.push(input.substring(start, input.length));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return cookiesStrings;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function transformHeaderToTuples(key, value) {
|
|
200
|
+
const output = [];
|
|
201
|
+
if (Array.isArray(value)) {
|
|
202
|
+
for(let j = 0; j < value.length; j++){
|
|
203
|
+
output.push([
|
|
204
|
+
key,
|
|
205
|
+
value[j]
|
|
206
|
+
]);
|
|
207
|
+
}
|
|
208
|
+
} else if (value !== undefined) {
|
|
209
|
+
output.push([
|
|
210
|
+
key,
|
|
211
|
+
String(value)
|
|
212
|
+
]);
|
|
213
|
+
}
|
|
214
|
+
return output;
|
|
215
|
+
}
|
|
216
|
+
function transformHeadersToTuples(input) {
|
|
217
|
+
const output = [];
|
|
218
|
+
const keys = Object.keys(input);
|
|
219
|
+
for(let i = 0; i < keys.length; i++){
|
|
220
|
+
const key = keys[i].toLowerCase();
|
|
221
|
+
output.push(...transformHeaderToTuples(key, input[key]));
|
|
222
|
+
}
|
|
223
|
+
return output;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function isObject(item) {
|
|
227
|
+
return !!item && typeof item === 'object' && !Array.isArray(item);
|
|
228
|
+
}
|
|
15
229
|
|
|
16
230
|
/**
|
|
17
231
|
* Determine if object is a Stats object.
|
|
@@ -20,15 +234,17 @@ import { createServer } from 'node:http';
|
|
|
20
234
|
* @return {boolean}
|
|
21
235
|
* @api private
|
|
22
236
|
*/ function isStatsObject(obj) {
|
|
23
|
-
/* istanbul ignore next */ if (typeof Stats === 'function' && obj instanceof Stats) {
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
237
|
// quack quack
|
|
27
|
-
return
|
|
238
|
+
return isObject(obj) && 'ctime' in obj && Object.prototype.toString.call(obj.ctime) === '[object Date]' && 'mtime' in obj && Object.prototype.toString.call(obj.mtime) === '[object Date]' && 'ino' in obj && typeof obj.ino === 'number' && 'size' in obj && typeof obj.size === 'number';
|
|
239
|
+
}
|
|
240
|
+
async function sha1(str) {
|
|
241
|
+
const enc = new TextEncoder();
|
|
242
|
+
const hash = await subtle.digest('SHA-1', enc.encode(str));
|
|
243
|
+
return btoa(String.fromCharCode(...new Uint8Array(hash)));
|
|
28
244
|
}
|
|
29
245
|
/**
|
|
30
246
|
* Generate an ETag.
|
|
31
|
-
*/ function generateETag(input) {
|
|
247
|
+
*/ async function generateETag(input) {
|
|
32
248
|
if (isStatsObject(input)) {
|
|
33
249
|
const mtime = input.mtime.getTime().toString(16);
|
|
34
250
|
const size = input.size.toString(16);
|
|
@@ -40,30 +256,26 @@ import { createServer } from 'node:http';
|
|
|
40
256
|
}
|
|
41
257
|
const entity = Buffer.isBuffer(input) ? input.toString('utf-8') : input;
|
|
42
258
|
// compute hash of entity
|
|
43
|
-
const hash =
|
|
44
|
-
return `"${entity.length.toString(16)}-${hash}"`;
|
|
259
|
+
const hash = await sha1(entity);
|
|
260
|
+
return `"${entity.length.toString(16)}-${hash.substring(0, 27)}"`;
|
|
45
261
|
}
|
|
46
262
|
/**
|
|
47
263
|
* Create a simple ETag.
|
|
48
|
-
*/ function createEtag(input, options) {
|
|
264
|
+
*/ async function createEtag(input, options) {
|
|
49
265
|
options = options || {};
|
|
50
266
|
const weak = typeof options.weak === 'boolean' ? options.weak : isStatsObject(input);
|
|
51
267
|
// generate entity tag
|
|
52
|
-
const tag = generateETag(input);
|
|
268
|
+
const tag = await generateETag(input);
|
|
53
269
|
return weak ? `W/${tag}` : tag;
|
|
54
270
|
}
|
|
55
271
|
|
|
56
|
-
function isObject(item) {
|
|
57
|
-
return !!item && typeof item === 'object' && !Array.isArray(item);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
272
|
function buildEtagFn(input) {
|
|
61
273
|
if (typeof input === 'function') {
|
|
62
274
|
return input;
|
|
63
275
|
}
|
|
64
276
|
input = input ?? true;
|
|
65
277
|
if (input === false) {
|
|
66
|
-
return ()=>undefined;
|
|
278
|
+
return ()=>Promise.resolve(undefined);
|
|
67
279
|
}
|
|
68
280
|
let options = {
|
|
69
281
|
weak: true
|
|
@@ -71,7 +283,7 @@ function buildEtagFn(input) {
|
|
|
71
283
|
if (isObject(input)) {
|
|
72
284
|
options = merge(input, options);
|
|
73
285
|
}
|
|
74
|
-
return (body, encoding, size)=>{
|
|
286
|
+
return async (body, encoding, size)=>{
|
|
75
287
|
const buff = Buffer.isBuffer(body) ? body : Buffer.from(body, encoding);
|
|
76
288
|
if (typeof options.threshold !== 'undefined') {
|
|
77
289
|
size = size ?? Buffer.byteLength(buff);
|
|
@@ -96,11 +308,11 @@ function buildTrustProxyFn(input) {
|
|
|
96
308
|
if (typeof input === 'string') {
|
|
97
309
|
input = input.split(',').map((value)=>value.trim());
|
|
98
310
|
}
|
|
99
|
-
return
|
|
311
|
+
return compile(input || []);
|
|
100
312
|
}
|
|
101
313
|
|
|
102
314
|
function isInstance(input, name) {
|
|
103
|
-
return
|
|
315
|
+
return isObject(input) && input['@instanceof'] === Symbol.for(name);
|
|
104
316
|
}
|
|
105
317
|
|
|
106
318
|
function getMimeType(type) {
|
|
@@ -120,8 +332,25 @@ function getCharsetForMimeType(type) {
|
|
|
120
332
|
return undefined;
|
|
121
333
|
}
|
|
122
334
|
|
|
123
|
-
|
|
124
|
-
|
|
335
|
+
/**
|
|
336
|
+
* Based on https://github.com/unjs/pathe v1.1.1 (055f50a6f1131f4e5c56cf259dd8816168fba329)
|
|
337
|
+
*/ function normalizeWindowsPath(input = '') {
|
|
338
|
+
if (!input || !input.includes('\\')) {
|
|
339
|
+
return input;
|
|
340
|
+
}
|
|
341
|
+
return input.replace(/\\/g, '/');
|
|
342
|
+
}
|
|
343
|
+
const EXTNAME_RE = /.(\.[^./]+)$/;
|
|
344
|
+
function extname(input) {
|
|
345
|
+
const match = EXTNAME_RE.exec(normalizeWindowsPath(input));
|
|
346
|
+
return match && match[1] || '';
|
|
347
|
+
}
|
|
348
|
+
function basename(input, extension) {
|
|
349
|
+
const lastSegment = normalizeWindowsPath(input).split('/').pop();
|
|
350
|
+
if (!lastSegment) {
|
|
351
|
+
return input;
|
|
352
|
+
}
|
|
353
|
+
return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
|
|
125
354
|
}
|
|
126
355
|
|
|
127
356
|
function isPromise(p) {
|
|
@@ -130,24 +359,14 @@ function isPromise(p) {
|
|
|
130
359
|
typeof p.then === 'function');
|
|
131
360
|
}
|
|
132
361
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
/* istanbul ignore next */ if (typeof done === 'function') {
|
|
142
|
-
done();
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
/* istanbul ignore next */ res.once('error', (e)=>{
|
|
146
|
-
clearTimeout(instance);
|
|
147
|
-
if (typeof done === 'function') {
|
|
148
|
-
done(e);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
362
|
+
function isNodeStream(input) {
|
|
363
|
+
return isObject(input) && typeof input.pipe === 'function' && typeof input.read === 'function';
|
|
364
|
+
}
|
|
365
|
+
function isWebStream(input) {
|
|
366
|
+
return isObject(input) && typeof input.pipeTo === 'function';
|
|
367
|
+
}
|
|
368
|
+
function isStream(data) {
|
|
369
|
+
return isNodeStream(data) || isWebStream(data);
|
|
151
370
|
}
|
|
152
371
|
|
|
153
372
|
const TRAILING_SLASH_RE = /\/$|\/\?/;
|
|
@@ -167,22 +386,9 @@ function withoutTrailingSlash(input = '', queryParams = false) {
|
|
|
167
386
|
const [s0, ...s] = input.split('?');
|
|
168
387
|
return (s0.slice(0, -1) || '/') + (s.length ? `?${s.join('?')}` : '');
|
|
169
388
|
}
|
|
170
|
-
function withTrailingSlash(input = '', queryParams = false) {
|
|
171
|
-
if (!queryParams) {
|
|
172
|
-
return input.endsWith('/') ? input : `${input}/`;
|
|
173
|
-
}
|
|
174
|
-
if (hasTrailingSlash(input, true)) {
|
|
175
|
-
return input || '/';
|
|
176
|
-
}
|
|
177
|
-
const [s0, ...s] = input.split('?');
|
|
178
|
-
return `${s0}/${s.length ? `?${s.join('?')}` : ''}`;
|
|
179
|
-
}
|
|
180
389
|
function hasLeadingSlash(input = '') {
|
|
181
390
|
return input.startsWith('/');
|
|
182
391
|
}
|
|
183
|
-
function withoutLeadingSlash(input = '') {
|
|
184
|
-
return (hasLeadingSlash(input) ? input.substr(1) : input) || '/';
|
|
185
|
-
}
|
|
186
392
|
function withLeadingSlash(input = '') {
|
|
187
393
|
return hasLeadingSlash(input) ? input : `/${input}`;
|
|
188
394
|
}
|
|
@@ -190,91 +396,73 @@ function cleanDoubleSlashes(input = '') {
|
|
|
190
396
|
return input.split('://').map((str)=>str.replace(/\/{2,}/g, '/')).join('://');
|
|
191
397
|
}
|
|
192
398
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return new Continu({
|
|
196
|
-
defaults: {
|
|
197
|
-
env: process.env.NODE_ENV || 'development',
|
|
198
|
-
trustProxy: ()=>false,
|
|
199
|
-
subdomainOffset: 2,
|
|
200
|
-
etag: buildEtagFn(),
|
|
201
|
-
proxyIpMax: 0
|
|
202
|
-
},
|
|
203
|
-
transformers: {
|
|
204
|
-
etag: (value)=>buildEtagFn(value),
|
|
205
|
-
trustProxy: (value)=>buildTrustProxyFn(value)
|
|
206
|
-
},
|
|
207
|
-
validators: {
|
|
208
|
-
env: (value)=>zod.string().safeParse(value),
|
|
209
|
-
trustProxy: (value)=>zod.any().safeParse(value),
|
|
210
|
-
subdomainOffset: (value)=>zod.number().nonnegative().safeParse(value),
|
|
211
|
-
etag: (value)=>zod.any().safeParse(value),
|
|
212
|
-
proxyIpMax: (value)=>zod.number().nonnegative().safeParse(value)
|
|
213
|
-
}
|
|
214
|
-
});
|
|
399
|
+
function isWebBlob(input) {
|
|
400
|
+
return typeof Blob !== 'undefined' && input instanceof Blob;
|
|
215
401
|
}
|
|
216
|
-
function
|
|
217
|
-
|
|
218
|
-
return instance;
|
|
219
|
-
}
|
|
220
|
-
instance = buildConfig();
|
|
221
|
-
return instance;
|
|
402
|
+
function isWebResponse(input) {
|
|
403
|
+
return typeof Response !== 'undefined' && input instanceof Response;
|
|
222
404
|
}
|
|
223
|
-
|
|
224
|
-
|
|
405
|
+
|
|
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
|
+
}
|
|
225
418
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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}"` : ''}`);
|
|
230
425
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
+
}
|
|
234
438
|
}
|
|
235
439
|
|
|
236
|
-
|
|
237
|
-
(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
HeaderName["HOST"] = 'host';
|
|
263
|
-
HeaderName["IF_MODIFIED_SINCE"] = 'if-modified-since';
|
|
264
|
-
HeaderName["IF_NONE_MATCH"] = 'if-none-match';
|
|
265
|
-
HeaderName["LAST_MODIFIED"] = 'last-modified';
|
|
266
|
-
HeaderName["LOCATION"] = 'location';
|
|
267
|
-
HeaderName["RANGE"] = 'range';
|
|
268
|
-
HeaderName["RATE_LIMIT_LIMIT"] = 'ratelimit-limit';
|
|
269
|
-
HeaderName["RATE_LIMIT_REMAINING"] = 'ratelimit-remaining';
|
|
270
|
-
HeaderName["RATE_LIMIT_RESET"] = 'ratelimit-reset';
|
|
271
|
-
HeaderName["RETRY_AFTER"] = 'retry-after';
|
|
272
|
-
HeaderName["SET_COOKIE"] = 'set-cookie';
|
|
273
|
-
HeaderName["TRANSFER_ENCODING"] = 'transfer-encoding';
|
|
274
|
-
HeaderName["X_FORWARDED_HOST"] = 'x-forwarded-host';
|
|
275
|
-
HeaderName["X_FORWARDED_FOR"] = 'x-forwarded-for';
|
|
276
|
-
HeaderName["X_FORWARDED_PROTO"] = 'x-forwarded-proto';
|
|
277
|
-
})(HeaderName || (HeaderName = {}));
|
|
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
|
+
}
|
|
278
466
|
|
|
279
467
|
const BodySymbol = Symbol.for('ReqBody');
|
|
280
468
|
function useRequestBody(req, key) {
|
|
@@ -537,14 +725,24 @@ function matchRequestContentType(req, contentType) {
|
|
|
537
725
|
return header.split('; ').shift() === getMimeType(contentType);
|
|
538
726
|
}
|
|
539
727
|
|
|
728
|
+
const routerSymbol = Symbol.for('ReqRouterID');
|
|
729
|
+
function setRequestRouterIds(req, ids) {
|
|
730
|
+
req[routerSymbol] = ids;
|
|
731
|
+
}
|
|
732
|
+
function useRequestRouterIds(req) {
|
|
733
|
+
if (routerSymbol in req) {
|
|
734
|
+
return req[routerSymbol];
|
|
735
|
+
}
|
|
736
|
+
return undefined;
|
|
737
|
+
}
|
|
738
|
+
|
|
540
739
|
function getRequestHostName(req, options) {
|
|
541
740
|
options = options || {};
|
|
542
741
|
let trustProxy;
|
|
543
742
|
if (typeof options.trustProxy !== 'undefined') {
|
|
544
743
|
trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
545
744
|
} else {
|
|
546
|
-
|
|
547
|
-
trustProxy = config.get('trustProxy');
|
|
745
|
+
trustProxy = findRouterOption('trustProxy', useRequestRouterIds(req));
|
|
548
746
|
}
|
|
549
747
|
let hostname = req.headers[HeaderName.X_FORWARDED_HOST];
|
|
550
748
|
if (!hostname || !req.socket.remoteAddress || !trustProxy(req.socket.remoteAddress, 0)) {
|
|
@@ -570,10 +768,10 @@ function getRequestIP(req, options) {
|
|
|
570
768
|
if (typeof options.trustProxy !== 'undefined') {
|
|
571
769
|
trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
572
770
|
} else {
|
|
573
|
-
|
|
574
|
-
trustProxy = config.get('trustProxy');
|
|
771
|
+
trustProxy = findRouterOption('trustProxy', useRequestRouterIds(req));
|
|
575
772
|
}
|
|
576
|
-
|
|
773
|
+
const addrs = all(req, trustProxy);
|
|
774
|
+
return addrs[addrs.length - 1];
|
|
577
775
|
}
|
|
578
776
|
|
|
579
777
|
const ReqMountPathSymbol = Symbol.for('ReqMountPath');
|
|
@@ -631,8 +829,7 @@ function getRequestProtocol(req, options) {
|
|
|
631
829
|
if (typeof options.trustProxy !== 'undefined') {
|
|
632
830
|
trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
633
831
|
} else {
|
|
634
|
-
|
|
635
|
-
trustProxy = config.get('trustProxy');
|
|
832
|
+
trustProxy = findRouterOption('trustProxy', useRequestRouterIds(req));
|
|
636
833
|
}
|
|
637
834
|
let protocol = options.default;
|
|
638
835
|
/* istanbul ignore next */ if (hasOwnProperty(req.socket, 'encrypted') && !!req.socket.encrypted) {
|
|
@@ -700,121 +897,7 @@ function extendRequestQuery(req, key, value) {
|
|
|
700
897
|
setRequestQuery(req, key, value);
|
|
701
898
|
}
|
|
702
899
|
|
|
703
|
-
function
|
|
704
|
-
options = options || {};
|
|
705
|
-
const cacheControls = [
|
|
706
|
-
'public'
|
|
707
|
-
].concat(options.cacheControls || []);
|
|
708
|
-
if (options.maxAge !== undefined) {
|
|
709
|
-
cacheControls.push(`max-age=${+options.maxAge}`, `s-maxage=${+options.maxAge}`);
|
|
710
|
-
}
|
|
711
|
-
if (options.modifiedTime) {
|
|
712
|
-
const modifiedTime = typeof options.modifiedTime === 'string' ? new Date(options.modifiedTime) : options.modifiedTime;
|
|
713
|
-
res.setHeader('last-modified', modifiedTime.toUTCString());
|
|
714
|
-
}
|
|
715
|
-
res.setHeader('cache-control', cacheControls.join(', '));
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
function appendResponseHeader(res, name, value) {
|
|
719
|
-
let header = res.getHeader(name);
|
|
720
|
-
if (!header) {
|
|
721
|
-
res.setHeader(name, value);
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
if (!Array.isArray(header)) {
|
|
725
|
-
header = [
|
|
726
|
-
header.toString()
|
|
727
|
-
];
|
|
728
|
-
}
|
|
729
|
-
res.setHeader(name, [
|
|
730
|
-
...header,
|
|
731
|
-
value
|
|
732
|
-
]);
|
|
733
|
-
}
|
|
734
|
-
function appendResponseHeaderDirective(res, name, value) {
|
|
735
|
-
let header = res.getHeader(name);
|
|
736
|
-
if (!header) {
|
|
737
|
-
if (Array.isArray(value)) {
|
|
738
|
-
res.setHeader(name, value.join('; '));
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
res.setHeader(name, value);
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
if (!Array.isArray(header)) {
|
|
745
|
-
if (typeof header === 'string') {
|
|
746
|
-
// split header by directive(s)
|
|
747
|
-
header = header.split('; ');
|
|
748
|
-
}
|
|
749
|
-
if (typeof header === 'number') {
|
|
750
|
-
header = [
|
|
751
|
-
header.toString()
|
|
752
|
-
];
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
if (Array.isArray(value)) {
|
|
756
|
-
header.push(...value);
|
|
757
|
-
} else {
|
|
758
|
-
header.push(`${value}`);
|
|
759
|
-
}
|
|
760
|
-
header = [
|
|
761
|
-
...new Set(header)
|
|
762
|
-
];
|
|
763
|
-
res.setHeader(name, header.join('; '));
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
function setResponseContentTypeByFileName(res, fileName) {
|
|
767
|
-
const ext = path.extname(fileName);
|
|
768
|
-
if (ext) {
|
|
769
|
-
let type = getMimeType(ext.substring(1));
|
|
770
|
-
if (type) {
|
|
771
|
-
const charset = getCharsetForMimeType(type);
|
|
772
|
-
if (charset) {
|
|
773
|
-
type += `; charset=${charset}`;
|
|
774
|
-
}
|
|
775
|
-
res.setHeader(HeaderName.CONTENT_TYPE, type);
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
/* istanbul ignore next */ function onResponseFinished(res, cb) {
|
|
780
|
-
let called;
|
|
781
|
-
const callCallback = (err)=>{
|
|
782
|
-
if (called) return;
|
|
783
|
-
called = true;
|
|
784
|
-
cb(err);
|
|
785
|
-
};
|
|
786
|
-
res.on('finish', ()=>{
|
|
787
|
-
callCallback();
|
|
788
|
-
});
|
|
789
|
-
res.on('close', ()=>{
|
|
790
|
-
callCallback();
|
|
791
|
-
});
|
|
792
|
-
res.on('error', (err)=>{
|
|
793
|
-
callCallback(err);
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
function setResponseHeaderAttachment(res, filename) {
|
|
798
|
-
if (typeof filename === 'string') {
|
|
799
|
-
setResponseContentTypeByFileName(res, filename);
|
|
800
|
-
}
|
|
801
|
-
res.setHeader(HeaderName.CONTENT_DISPOSITION, `attachment${filename ? `; filename="${filename}"` : ''}`);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
function setResponseHeaderContentType(res, input, ifNotExists) {
|
|
805
|
-
if (ifNotExists) {
|
|
806
|
-
const header = res.getHeader(HeaderName.CONTENT_TYPE);
|
|
807
|
-
if (header) {
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
const contentType = getMimeType(input);
|
|
812
|
-
if (contentType) {
|
|
813
|
-
res.setHeader(HeaderName.CONTENT_TYPE, contentType);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
function send(res, chunk) {
|
|
900
|
+
async function send(res, chunk) {
|
|
818
901
|
switch(typeof chunk){
|
|
819
902
|
case 'string':
|
|
820
903
|
{
|
|
@@ -859,10 +942,12 @@ function send(res, chunk) {
|
|
|
859
942
|
}
|
|
860
943
|
res.setHeader(HeaderName.CONTENT_LENGTH, `${len}`);
|
|
861
944
|
}
|
|
862
|
-
const config = useConfig();
|
|
863
|
-
const etagFn = config.get('etag');
|
|
864
945
|
if (typeof len !== 'undefined') {
|
|
865
|
-
const
|
|
946
|
+
const etagFn = findRouterOption('etag', useRequestRouterIds(res.req));
|
|
947
|
+
const chunkHash = await etagFn(chunk, encoding, len);
|
|
948
|
+
if (isResponseGone(res)) {
|
|
949
|
+
return Promise.resolve();
|
|
950
|
+
}
|
|
866
951
|
if (typeof chunkHash === 'string') {
|
|
867
952
|
res.setHeader(HeaderName.ETag, chunkHash);
|
|
868
953
|
if (res.req.headers[HeaderName.IF_NONE_MATCH] === chunkHash) {
|
|
@@ -883,16 +968,20 @@ function send(res, chunk) {
|
|
|
883
968
|
res.removeHeader(HeaderName.TRANSFER_ENCODING);
|
|
884
969
|
chunk = '';
|
|
885
970
|
}
|
|
971
|
+
if (isResponseGone(res)) {
|
|
972
|
+
return Promise.resolve();
|
|
973
|
+
}
|
|
886
974
|
if (res.req.method === 'HEAD') {
|
|
887
975
|
// skip body for HEAD
|
|
888
976
|
res.end();
|
|
889
|
-
return;
|
|
977
|
+
return Promise.resolve();
|
|
890
978
|
}
|
|
891
979
|
if (typeof encoding !== 'undefined') {
|
|
892
980
|
res.end(chunk, encoding);
|
|
893
|
-
return;
|
|
981
|
+
return Promise.resolve();
|
|
894
982
|
}
|
|
895
983
|
res.end(chunk);
|
|
984
|
+
return Promise.resolve();
|
|
896
985
|
}
|
|
897
986
|
|
|
898
987
|
function sendAccepted(res, chunk) {
|
|
@@ -907,87 +996,116 @@ function sendCreated(res, chunk) {
|
|
|
907
996
|
return send(res, chunk);
|
|
908
997
|
}
|
|
909
998
|
|
|
910
|
-
function sendStream(res, stream,
|
|
911
|
-
|
|
912
|
-
stream.
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
999
|
+
async function sendStream(res, stream, next) {
|
|
1000
|
+
if (isWebStream(stream)) {
|
|
1001
|
+
return stream.pipeTo(new WritableStream({
|
|
1002
|
+
write (chunk) {
|
|
1003
|
+
res.write(chunk);
|
|
1004
|
+
}
|
|
1005
|
+
})).then(()=>{
|
|
1006
|
+
if (next) {
|
|
1007
|
+
return next();
|
|
1008
|
+
}
|
|
919
1009
|
res.end();
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1010
|
+
return Promise.resolve();
|
|
1011
|
+
}).catch((err)=>{
|
|
1012
|
+
if (next) {
|
|
1013
|
+
return next(err);
|
|
1014
|
+
}
|
|
1015
|
+
return Promise.reject(err);
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
return new Promise((resolve, reject)=>{
|
|
1019
|
+
stream.on('open', ()=>{
|
|
1020
|
+
stream.pipe(res);
|
|
1021
|
+
});
|
|
1022
|
+
/* istanbul ignore next */ stream.on('error', (err)=>{
|
|
1023
|
+
if (next) {
|
|
1024
|
+
Promise.resolve().then(()=>next(err)).then(()=>resolve()).catch((e)=>reject(e));
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
926
1027
|
res.end();
|
|
927
|
-
|
|
1028
|
+
reject(err);
|
|
1029
|
+
});
|
|
1030
|
+
stream.on('close', ()=>{
|
|
1031
|
+
if (next) {
|
|
1032
|
+
Promise.resolve().then(()=>next()).then(()=>resolve()).catch((e)=>reject(e));
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
res.end();
|
|
1036
|
+
resolve();
|
|
1037
|
+
});
|
|
928
1038
|
});
|
|
929
1039
|
}
|
|
930
1040
|
|
|
931
|
-
function
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
function sendFile(res, filePath, fn) {
|
|
939
|
-
let options;
|
|
940
|
-
if (typeof filePath === 'string') {
|
|
941
|
-
options = {
|
|
942
|
-
filePath
|
|
943
|
-
};
|
|
944
|
-
} else {
|
|
945
|
-
options = filePath;
|
|
946
|
-
}
|
|
947
|
-
const fileName = path.basename(options.filePath);
|
|
948
|
-
if (options.attachment) {
|
|
949
|
-
const dispositionHeader = res.getHeader(HeaderName.CONTENT_DISPOSITION);
|
|
950
|
-
if (!dispositionHeader) {
|
|
951
|
-
setResponseHeaderAttachment(res, fileName);
|
|
1041
|
+
async function sendFile(res, options, next) {
|
|
1042
|
+
let stats;
|
|
1043
|
+
try {
|
|
1044
|
+
stats = await options.stats();
|
|
1045
|
+
} catch (e) {
|
|
1046
|
+
if (next) {
|
|
1047
|
+
return next(e);
|
|
952
1048
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1049
|
+
if (isResponseGone(res)) {
|
|
1050
|
+
return Promise.resolve();
|
|
1051
|
+
}
|
|
1052
|
+
return Promise.reject(e);
|
|
1053
|
+
}
|
|
1054
|
+
const name = options.name || stats.name;
|
|
1055
|
+
if (name) {
|
|
1056
|
+
const fileName = basename(name);
|
|
1057
|
+
if (options.attachment) {
|
|
1058
|
+
const dispositionHeader = res.getHeader(HeaderName.CONTENT_DISPOSITION);
|
|
1059
|
+
if (!dispositionHeader) {
|
|
1060
|
+
setResponseHeaderAttachment(res, fileName);
|
|
963
1061
|
}
|
|
964
|
-
|
|
1062
|
+
} else {
|
|
1063
|
+
setResponseContentTypeByFileName(res, fileName);
|
|
965
1064
|
}
|
|
966
|
-
|
|
1065
|
+
}
|
|
1066
|
+
const contentOptions = {};
|
|
1067
|
+
if (stats.size) {
|
|
967
1068
|
const rangeHeader = res.req.headers[HeaderName.RANGE];
|
|
968
1069
|
if (rangeHeader) {
|
|
969
1070
|
const [x, y] = rangeHeader.replace('bytes=', '').split('-');
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
if (
|
|
973
|
-
|
|
1071
|
+
contentOptions.end = Math.min(parseInt(y, 10) || stats.size - 1, stats.size - 1);
|
|
1072
|
+
contentOptions.start = parseInt(x, 10) || 0;
|
|
1073
|
+
if (contentOptions.end >= stats.size) {
|
|
1074
|
+
contentOptions.end = stats.size - 1;
|
|
974
1075
|
}
|
|
975
|
-
if (
|
|
1076
|
+
if (contentOptions.start >= stats.size) {
|
|
976
1077
|
res.setHeader(HeaderName.CONTENT_RANGE, `bytes */${stats.size}`);
|
|
977
1078
|
res.statusCode = 416;
|
|
978
1079
|
res.end();
|
|
979
|
-
return;
|
|
1080
|
+
return Promise.resolve();
|
|
980
1081
|
}
|
|
981
|
-
res.setHeader(HeaderName.CONTENT_RANGE, `bytes ${
|
|
982
|
-
res.setHeader(HeaderName.CONTENT_LENGTH,
|
|
1082
|
+
res.setHeader(HeaderName.CONTENT_RANGE, `bytes ${contentOptions.start}-${contentOptions.end}/${stats.size}`);
|
|
1083
|
+
res.setHeader(HeaderName.CONTENT_LENGTH, contentOptions.end - contentOptions.start + 1);
|
|
983
1084
|
} else {
|
|
984
1085
|
res.setHeader(HeaderName.CONTENT_LENGTH, stats.size);
|
|
985
1086
|
}
|
|
986
1087
|
res.setHeader(HeaderName.ACCEPT_RANGES, 'bytes');
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1088
|
+
if (stats.mtime) {
|
|
1089
|
+
const mtime = new Date(stats.mtime);
|
|
1090
|
+
res.setHeader(HeaderName.LAST_MODIFIED, mtime.toUTCString());
|
|
1091
|
+
res.setHeader(HeaderName.ETag, `W/"${stats.size}-${mtime.getTime()}"`);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
try {
|
|
1095
|
+
const content = await options.content(contentOptions);
|
|
1096
|
+
if (isStream(content)) {
|
|
1097
|
+
return await sendStream(res, content, next);
|
|
1098
|
+
}
|
|
1099
|
+
return await send(res, content);
|
|
1100
|
+
} catch (e) {
|
|
1101
|
+
if (next) {
|
|
1102
|
+
return next(e);
|
|
1103
|
+
}
|
|
1104
|
+
if (isResponseGone(res)) {
|
|
1105
|
+
return Promise.resolve();
|
|
1106
|
+
}
|
|
1107
|
+
return Promise.reject(e);
|
|
1108
|
+
}
|
|
991
1109
|
}
|
|
992
1110
|
|
|
993
1111
|
function sendFormat(res, input) {
|
|
@@ -1009,19 +1127,406 @@ function sendRedirect(res, location, statusCode = 302) {
|
|
|
1009
1127
|
return send(res, html);
|
|
1010
1128
|
}
|
|
1011
1129
|
|
|
1012
|
-
function
|
|
1013
|
-
if (
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1130
|
+
function sendWebResponse(res, webResponse) {
|
|
1131
|
+
if (webResponse.redirected) {
|
|
1132
|
+
res.setHeader(HeaderName.LOCATION, webResponse.url);
|
|
1133
|
+
}
|
|
1134
|
+
if (webResponse.status) {
|
|
1135
|
+
res.statusCode = webResponse.status;
|
|
1136
|
+
}
|
|
1137
|
+
if (webResponse.statusText) {
|
|
1138
|
+
res.statusMessage = webResponse.statusText;
|
|
1139
|
+
}
|
|
1140
|
+
webResponse.headers.forEach((value, key)=>{
|
|
1141
|
+
if (key === HeaderName.SET_COOKIE) {
|
|
1142
|
+
res.appendHeader(key, splitCookiesString(value));
|
|
1143
|
+
} else {
|
|
1144
|
+
res.setHeader(key, value);
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
if (webResponse.body) {
|
|
1148
|
+
return sendStream(res, webResponse.body);
|
|
1149
|
+
}
|
|
1150
|
+
res.end();
|
|
1151
|
+
return Promise.resolve();
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
function sendWebBlob(res, blob) {
|
|
1155
|
+
setResponseHeaderContentType(res, blob.type);
|
|
1156
|
+
return sendStream(res, blob.stream());
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
function createResponse(request) {
|
|
1160
|
+
let output;
|
|
1161
|
+
let encoding;
|
|
1162
|
+
const write = (chunk, chunkEncoding, callback)=>{
|
|
1163
|
+
if (typeof chunk !== 'undefined') {
|
|
1164
|
+
const chunkEncoded = typeof chunk === 'string' ? Buffer.from(chunk, chunkEncoding || encoding || 'utf8') : chunk;
|
|
1165
|
+
if (typeof output !== 'undefined') {
|
|
1166
|
+
output = Buffer.concat([
|
|
1167
|
+
output,
|
|
1168
|
+
chunkEncoded
|
|
1169
|
+
]);
|
|
1170
|
+
} else {
|
|
1171
|
+
output = chunkEncoded;
|
|
1017
1172
|
}
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1173
|
+
}
|
|
1174
|
+
encoding = chunkEncoding;
|
|
1175
|
+
if (callback) {
|
|
1176
|
+
callback();
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
const writable = new Writable({
|
|
1180
|
+
decodeStrings: false,
|
|
1181
|
+
write (chunk, arg2, arg3) {
|
|
1182
|
+
const chunkEncoding = typeof arg2 === 'string' ? encoding : 'utf-8';
|
|
1183
|
+
let cb;
|
|
1184
|
+
if (typeof arg2 === 'function') {
|
|
1185
|
+
cb = arg2;
|
|
1186
|
+
} else if (typeof arg3 === 'function') {
|
|
1187
|
+
cb = arg3;
|
|
1188
|
+
}
|
|
1189
|
+
write(chunk, chunkEncoding, cb);
|
|
1190
|
+
return true;
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
Object.defineProperty(writable, 'body', {
|
|
1194
|
+
get () {
|
|
1195
|
+
if (output) {
|
|
1196
|
+
const arrayBuffer = new ArrayBuffer(output.length);
|
|
1197
|
+
const view = new Uint8Array(arrayBuffer);
|
|
1198
|
+
for(let i = 0; i < output.length; ++i){
|
|
1199
|
+
view[i] = output[i];
|
|
1200
|
+
}
|
|
1201
|
+
return arrayBuffer;
|
|
1202
|
+
}
|
|
1203
|
+
return new ArrayBuffer(0);
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
const headers = {};
|
|
1207
|
+
Object.assign(writable, {
|
|
1208
|
+
req: request,
|
|
1209
|
+
chunkedEncoding: false,
|
|
1210
|
+
connection: null,
|
|
1211
|
+
headersSent: false,
|
|
1212
|
+
sendDate: false,
|
|
1213
|
+
shouldKeepAlive: false,
|
|
1214
|
+
socket: null,
|
|
1215
|
+
statusCode: 200,
|
|
1216
|
+
statusMessage: '',
|
|
1217
|
+
strictContentLength: false,
|
|
1218
|
+
useChunkedEncodingByDefault: false,
|
|
1219
|
+
finished: false,
|
|
1220
|
+
addTrailers (_headers) {},
|
|
1221
|
+
appendHeader (name, value) {
|
|
1222
|
+
if (name === HeaderName.SET_COOKIE) {
|
|
1223
|
+
value = splitCookiesString(value);
|
|
1224
|
+
}
|
|
1225
|
+
name = name.toLowerCase();
|
|
1226
|
+
const current = headers[name];
|
|
1227
|
+
const all = [
|
|
1228
|
+
...Array.isArray(current) ? current : [
|
|
1229
|
+
current
|
|
1230
|
+
],
|
|
1231
|
+
...Array.isArray(value) ? value : [
|
|
1232
|
+
value
|
|
1233
|
+
]
|
|
1234
|
+
].filter(Boolean);
|
|
1235
|
+
headers[name] = all.length > 1 ? all : all[0];
|
|
1236
|
+
return this;
|
|
1237
|
+
},
|
|
1238
|
+
assignSocket (_socket) {},
|
|
1239
|
+
detachSocket (_socket) {},
|
|
1240
|
+
flushHeaders () {},
|
|
1241
|
+
getHeader (name) {
|
|
1242
|
+
return headers[name.toLowerCase()];
|
|
1243
|
+
},
|
|
1244
|
+
getHeaderNames () {
|
|
1245
|
+
return Object.keys(headers);
|
|
1246
|
+
},
|
|
1247
|
+
getHeaders () {
|
|
1248
|
+
return headers;
|
|
1249
|
+
},
|
|
1250
|
+
hasHeader (name) {
|
|
1251
|
+
return hasOwnProperty(headers, name.toLowerCase());
|
|
1252
|
+
},
|
|
1253
|
+
removeHeader (name) {
|
|
1254
|
+
delete headers[name.toLowerCase()];
|
|
1255
|
+
},
|
|
1256
|
+
setHeader (name, value) {
|
|
1257
|
+
if (name === HeaderName.SET_COOKIE && typeof value !== 'number') {
|
|
1258
|
+
value = splitCookiesString(value);
|
|
1259
|
+
}
|
|
1260
|
+
headers[name.toLowerCase()] = value;
|
|
1261
|
+
return this;
|
|
1262
|
+
},
|
|
1263
|
+
setTimeout (_msecs, _callback) {
|
|
1264
|
+
return this;
|
|
1265
|
+
},
|
|
1266
|
+
writeContinue (_callback) {},
|
|
1267
|
+
writeEarlyHints (_hints, callback) {
|
|
1268
|
+
if (typeof callback !== 'undefined') {
|
|
1269
|
+
callback();
|
|
1270
|
+
}
|
|
1271
|
+
},
|
|
1272
|
+
writeProcessing () {},
|
|
1273
|
+
writeHead (statusCode, arg1, arg2) {
|
|
1274
|
+
this.statusCode = statusCode;
|
|
1275
|
+
if (typeof arg1 === 'string') {
|
|
1276
|
+
this.statusMessage = arg1;
|
|
1277
|
+
arg1 = undefined;
|
|
1278
|
+
}
|
|
1279
|
+
const headers = arg2 || arg1;
|
|
1280
|
+
if (headers) {
|
|
1281
|
+
if (Array.isArray(headers)) {
|
|
1282
|
+
for(let i = 0; i < headers.length; i++){
|
|
1283
|
+
const keys = Object.keys(headers[i]);
|
|
1284
|
+
for(let j = 0; j < keys.length; j++){
|
|
1285
|
+
this.setHeader(keys[i], headers[i][keys[j]]);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
} else {
|
|
1289
|
+
const keys = Object.keys(headers);
|
|
1290
|
+
for(let i = 0; i < keys.length; i++){
|
|
1291
|
+
this.setHeader(keys[i], headers[keys[i]]);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1296
|
+
// @ts-ignore
|
|
1297
|
+
this.headersSent = true;
|
|
1298
|
+
return this;
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
return writable;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
async function dispatchNodeRequest(router, req, res) {
|
|
1305
|
+
try {
|
|
1306
|
+
const dispatched = await router.dispatch({
|
|
1307
|
+
req,
|
|
1308
|
+
res
|
|
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);
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
function createRequest(context) {
|
|
1332
|
+
let readable;
|
|
1333
|
+
if (context.body) {
|
|
1334
|
+
if (isWebStream(context.body)) {
|
|
1335
|
+
readable = Readable.fromWeb(context.body);
|
|
1336
|
+
} else {
|
|
1337
|
+
readable = Readable.from(context.body);
|
|
1338
|
+
}
|
|
1339
|
+
} else {
|
|
1340
|
+
readable = new Readable();
|
|
1341
|
+
}
|
|
1342
|
+
const headers = context.headers || {};
|
|
1343
|
+
const rawHeaders = [];
|
|
1344
|
+
let keys = Object.keys(headers);
|
|
1345
|
+
for(let i = 0; i < keys.length; i++){
|
|
1346
|
+
const header = headers[keys[i]];
|
|
1347
|
+
if (Array.isArray(header)) {
|
|
1348
|
+
for(let j = 0; j < header.length; j++){
|
|
1349
|
+
rawHeaders.push(keys[i], header[j]);
|
|
1350
|
+
}
|
|
1351
|
+
} else if (typeof header === 'string') {
|
|
1352
|
+
rawHeaders.push(keys[i], header);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
const headersDistinct = {};
|
|
1356
|
+
keys = Object.keys(headers);
|
|
1357
|
+
for(let i = 0; i < keys.length; i++){
|
|
1358
|
+
const header = headers[keys[i]];
|
|
1359
|
+
if (Array.isArray(header)) {
|
|
1360
|
+
headersDistinct[keys[i]] = header;
|
|
1361
|
+
}
|
|
1362
|
+
if (typeof header === 'string') {
|
|
1363
|
+
headersDistinct[keys[i]] = [
|
|
1364
|
+
header
|
|
1365
|
+
];
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
Object.defineProperty(readable, 'connection', {
|
|
1369
|
+
get () {
|
|
1370
|
+
return {};
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
1373
|
+
Object.defineProperty(readable, 'socket', {
|
|
1374
|
+
get () {
|
|
1375
|
+
return {};
|
|
1376
|
+
}
|
|
1377
|
+
});
|
|
1378
|
+
Object.assign(readable, {
|
|
1379
|
+
aborted: false,
|
|
1380
|
+
complete: true,
|
|
1381
|
+
headers,
|
|
1382
|
+
headersDistinct,
|
|
1383
|
+
httpVersion: '1.1',
|
|
1384
|
+
httpVersionMajor: 1,
|
|
1385
|
+
httpVersionMinor: 1,
|
|
1386
|
+
method: context.method || 'GET',
|
|
1387
|
+
rawHeaders,
|
|
1388
|
+
rawTrailers: [],
|
|
1389
|
+
trailers: {},
|
|
1390
|
+
trailersDistinct: {},
|
|
1391
|
+
url: context.url || '/',
|
|
1392
|
+
setTimeout (_msecs, _callback) {
|
|
1393
|
+
return this;
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
return readable;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
async function dispatchRawRequest(router, request, options = {}) {
|
|
1400
|
+
const req = createRequest({
|
|
1401
|
+
url: request.path,
|
|
1402
|
+
method: request.method,
|
|
1403
|
+
body: request.body,
|
|
1404
|
+
headers: request.headers
|
|
1405
|
+
});
|
|
1406
|
+
const res = createResponse(req);
|
|
1407
|
+
const getHeaders = ()=>{
|
|
1408
|
+
const output = {};
|
|
1409
|
+
const headers = res.getHeaders();
|
|
1410
|
+
const keys = Object.keys(headers);
|
|
1411
|
+
for(let i = 0; i < keys.length; i++){
|
|
1412
|
+
const header = headers[keys[i]];
|
|
1413
|
+
if (typeof header === 'number') {
|
|
1414
|
+
output[keys[i]] = `${header}`;
|
|
1415
|
+
} else if (header) {
|
|
1416
|
+
output[keys[i]] = header;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return output;
|
|
1420
|
+
};
|
|
1421
|
+
try {
|
|
1422
|
+
const dispatched = await router.dispatch({
|
|
1423
|
+
req,
|
|
1424
|
+
res
|
|
1425
|
+
});
|
|
1426
|
+
if (dispatched) {
|
|
1427
|
+
return {
|
|
1428
|
+
status: res.statusCode,
|
|
1429
|
+
statusMessage: res.statusMessage,
|
|
1430
|
+
headers: getHeaders(),
|
|
1431
|
+
body: res.body
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
return {
|
|
1435
|
+
status: 404,
|
|
1436
|
+
headers: getHeaders(),
|
|
1437
|
+
body: res.body
|
|
1438
|
+
};
|
|
1439
|
+
} catch (e) {
|
|
1440
|
+
if (options.throwOnError) {
|
|
1441
|
+
throw e;
|
|
1442
|
+
}
|
|
1443
|
+
return {
|
|
1444
|
+
status: 500,
|
|
1445
|
+
headers: getHeaders(),
|
|
1446
|
+
body: res.body
|
|
1447
|
+
};
|
|
1021
1448
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1449
|
+
}
|
|
1450
|
+
function createRawDispatcher(router) {
|
|
1451
|
+
return async (request)=>dispatchRawRequest(router, request);
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
async function dispatchWebRequest(router, request, options = {}) {
|
|
1455
|
+
const url = new URL(request.url);
|
|
1456
|
+
const headers = {};
|
|
1457
|
+
request.headers.forEach((value, key)=>{
|
|
1458
|
+
headers[key] = value;
|
|
1459
|
+
});
|
|
1460
|
+
const res = await dispatchRawRequest(router, {
|
|
1461
|
+
method: request.method,
|
|
1462
|
+
path: url.pathname + url.search,
|
|
1463
|
+
headers,
|
|
1464
|
+
body: request.body
|
|
1465
|
+
}, options);
|
|
1466
|
+
let body;
|
|
1467
|
+
if (request.method === MethodName.HEAD || res.status === 304 || res.status === 101 || res.status === 204 || res.status === 205) {
|
|
1468
|
+
body = null;
|
|
1469
|
+
} else {
|
|
1470
|
+
body = res.body;
|
|
1024
1471
|
}
|
|
1472
|
+
return new Response(body, {
|
|
1473
|
+
headers: transformHeadersToTuples(res.headers),
|
|
1474
|
+
status: res.status,
|
|
1475
|
+
statusText: res.statusMessage
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
function createWebDispatcher(router) {
|
|
1479
|
+
return async (request)=>dispatchWebRequest(router, request);
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
function cloneDispatcherMeta(input) {
|
|
1483
|
+
if (!input) {
|
|
1484
|
+
return {};
|
|
1485
|
+
}
|
|
1486
|
+
return {
|
|
1487
|
+
path: input.path,
|
|
1488
|
+
mountPath: input.mountPath,
|
|
1489
|
+
error: input.error,
|
|
1490
|
+
routerIds: [
|
|
1491
|
+
...input.routerIds || []
|
|
1492
|
+
],
|
|
1493
|
+
params: cloneDispatcherMetaParams(input.params)
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
function cloneDispatcherMetaParams(input) {
|
|
1497
|
+
if (typeof input === 'undefined') {
|
|
1498
|
+
return {};
|
|
1499
|
+
}
|
|
1500
|
+
const keys = Object.keys(input);
|
|
1501
|
+
const output = {};
|
|
1502
|
+
for(let i = 0; i < keys.length; i++){
|
|
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]];
|
|
1517
|
+
}
|
|
1518
|
+
return t1;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
function createError(input) {
|
|
1522
|
+
if (input instanceof Error) {
|
|
1523
|
+
return input;
|
|
1524
|
+
}
|
|
1525
|
+
const error = new Error();
|
|
1526
|
+
if (typeof input.message === 'string') {
|
|
1527
|
+
error.message = input.message;
|
|
1528
|
+
}
|
|
1529
|
+
return error;
|
|
1025
1530
|
}
|
|
1026
1531
|
|
|
1027
1532
|
function decodeParam(val) {
|
|
@@ -1085,48 +1590,98 @@ class PathMatcher {
|
|
|
1085
1590
|
}
|
|
1086
1591
|
}
|
|
1087
1592
|
|
|
1593
|
+
function isPath(input) {
|
|
1594
|
+
return typeof input === 'string' || input instanceof RegExp;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1088
1597
|
class Layer {
|
|
1089
1598
|
// --------------------------------------------------
|
|
1090
1599
|
isError() {
|
|
1091
1600
|
return this.fn.length === 4;
|
|
1092
1601
|
}
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1602
|
+
// --------------------------------------------------
|
|
1603
|
+
dispatch(event, meta) {
|
|
1604
|
+
setRequestParams(event.req, meta.params || {});
|
|
1605
|
+
setRequestMountPath(event.req, meta.mountPath || '/');
|
|
1606
|
+
setRequestRouterIds(event.req, meta.routerIds || []);
|
|
1607
|
+
if (this.fn.length !== 4 && meta.error || this.fn.length === 4 && !meta.error) {
|
|
1608
|
+
return Promise.reject(meta.error);
|
|
1609
|
+
}
|
|
1610
|
+
const timeout = findRouterOption('timeout', meta.routerIds);
|
|
1611
|
+
return new Promise((resolve, reject)=>{
|
|
1612
|
+
let timeoutInstance;
|
|
1613
|
+
let handled = false;
|
|
1614
|
+
const unsubscribe = ()=>{
|
|
1615
|
+
if (timeoutInstance) {
|
|
1616
|
+
clearTimeout(timeoutInstance);
|
|
1617
|
+
}
|
|
1618
|
+
event.res.off('close', onFinished);
|
|
1619
|
+
event.res.off('error', onFinished);
|
|
1620
|
+
};
|
|
1621
|
+
const shutdown = (dispatched, err)=>{
|
|
1622
|
+
if (handled) {
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
handled = true;
|
|
1626
|
+
unsubscribe();
|
|
1627
|
+
if (err) {
|
|
1628
|
+
reject(createError(err));
|
|
1629
|
+
} else {
|
|
1630
|
+
resolve(dispatched);
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
1633
|
+
const onFinished = (err)=>shutdown(true, err);
|
|
1634
|
+
const onNext = (err)=>shutdown(false, err);
|
|
1635
|
+
event.res.once('close', onFinished);
|
|
1636
|
+
event.res.once('error', onFinished);
|
|
1637
|
+
if (timeout) {
|
|
1638
|
+
timeoutInstance = setTimeout(()=>{
|
|
1639
|
+
handled = true;
|
|
1640
|
+
unsubscribe();
|
|
1641
|
+
event.res.statusCode = 504;
|
|
1642
|
+
event.res.statusMessage = 'Gateway Timeout';
|
|
1643
|
+
event.res.end();
|
|
1644
|
+
}, timeout);
|
|
1645
|
+
}
|
|
1646
|
+
try {
|
|
1647
|
+
let output;
|
|
1648
|
+
if (meta.error) {
|
|
1649
|
+
output = this.fn(meta.error, event.req, event.res, onNext);
|
|
1650
|
+
} else {
|
|
1651
|
+
output = this.fn(event.req, event.res, onNext);
|
|
1652
|
+
}
|
|
1653
|
+
const handle = (data)=>{
|
|
1654
|
+
if (typeof data === 'undefined' || handled) {
|
|
1655
|
+
return Promise.resolve();
|
|
1107
1656
|
}
|
|
1657
|
+
handled = true;
|
|
1658
|
+
unsubscribe();
|
|
1659
|
+
return this.sendOutput(event.res, data).then(()=>resolve(true)).catch((e)=>reject(createError(e)));
|
|
1660
|
+
};
|
|
1661
|
+
if (isPromise(output)) {
|
|
1662
|
+
output.then((r)=>handle(r)).catch((e)=>reject(createError(e)));
|
|
1663
|
+
return;
|
|
1108
1664
|
}
|
|
1109
|
-
|
|
1665
|
+
Promise.resolve().then(()=>handle(output)).catch((e)=>reject(createError(e)));
|
|
1666
|
+
} catch (error) {
|
|
1667
|
+
onNext(error);
|
|
1110
1668
|
}
|
|
1111
|
-
|
|
1112
|
-
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
sendOutput(res, input) {
|
|
1672
|
+
if (input instanceof Error) {
|
|
1673
|
+
return Promise.reject(input);
|
|
1113
1674
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
return;
|
|
1675
|
+
if (isStream(input)) {
|
|
1676
|
+
return sendStream(res, input);
|
|
1117
1677
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
next(e);
|
|
1124
|
-
} else {
|
|
1125
|
-
next(new BadRequestError({
|
|
1126
|
-
message: 'The request could not be processed by the handler.'
|
|
1127
|
-
}));
|
|
1128
|
-
}
|
|
1678
|
+
if (isWebBlob(input)) {
|
|
1679
|
+
return sendWebBlob(res, input);
|
|
1680
|
+
}
|
|
1681
|
+
if (isWebResponse(input)) {
|
|
1682
|
+
return sendWebResponse(res, input);
|
|
1129
1683
|
}
|
|
1684
|
+
return send(res, input);
|
|
1130
1685
|
}
|
|
1131
1686
|
// --------------------------------------------------
|
|
1132
1687
|
matchPath(path) {
|
|
@@ -1144,6 +1699,9 @@ class Layer {
|
|
|
1144
1699
|
}
|
|
1145
1700
|
|
|
1146
1701
|
function isLayerInstance(input) {
|
|
1702
|
+
if (input instanceof Layer) {
|
|
1703
|
+
return true;
|
|
1704
|
+
}
|
|
1147
1705
|
return isInstance(input, 'Layer');
|
|
1148
1706
|
}
|
|
1149
1707
|
|
|
@@ -1168,44 +1726,46 @@ class Route {
|
|
|
1168
1726
|
return keys;
|
|
1169
1727
|
}
|
|
1170
1728
|
// --------------------------------------------------
|
|
1171
|
-
dispatch(
|
|
1172
|
-
/* istanbul ignore next */ if (!req.method) {
|
|
1173
|
-
|
|
1174
|
-
return;
|
|
1729
|
+
async dispatch(event, meta) {
|
|
1730
|
+
/* istanbul ignore next */ if (!event.req.method) {
|
|
1731
|
+
return false;
|
|
1175
1732
|
}
|
|
1176
|
-
let name = req.method.toLowerCase();
|
|
1733
|
+
let name = event.req.method.toLowerCase();
|
|
1177
1734
|
if (name === MethodName.HEAD && !hasOwnProperty(this.layers, name)) {
|
|
1178
1735
|
name = MethodName.GET;
|
|
1179
1736
|
}
|
|
1180
1737
|
const layers = this.layers[name];
|
|
1181
1738
|
/* istanbul ignore next */ if (typeof layers === 'undefined' || layers.length === 0 || typeof meta.path === 'undefined') {
|
|
1182
|
-
|
|
1183
|
-
return;
|
|
1739
|
+
return false;
|
|
1184
1740
|
}
|
|
1185
|
-
const layerMeta =
|
|
1186
|
-
...meta
|
|
1187
|
-
};
|
|
1741
|
+
const layerMeta = cloneDispatcherMeta(meta);
|
|
1188
1742
|
const output = this.pathMatcher.exec(meta.path);
|
|
1189
1743
|
if (output) {
|
|
1190
|
-
layerMeta.params =
|
|
1744
|
+
layerMeta.params = mergeDispatcherMetaParams(layerMeta.params, output.params);
|
|
1191
1745
|
}
|
|
1192
|
-
let
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
if (index >= layers.length) {
|
|
1196
|
-
setImmediate(done, err);
|
|
1197
|
-
return;
|
|
1198
|
-
}
|
|
1199
|
-
const layer = layers[index];
|
|
1746
|
+
let err;
|
|
1747
|
+
for(let i = 0; i < layers.length; i++){
|
|
1748
|
+
const layer = layers[i];
|
|
1200
1749
|
if (err && !layer.isError()) {
|
|
1201
|
-
|
|
1202
|
-
return;
|
|
1750
|
+
continue;
|
|
1203
1751
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
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
|
+
}
|
|
1768
|
+
return false;
|
|
1209
1769
|
}
|
|
1210
1770
|
// --------------------------------------------------
|
|
1211
1771
|
register(method, ...handlers) {
|
|
@@ -1258,42 +1818,50 @@ class Route {
|
|
|
1258
1818
|
}
|
|
1259
1819
|
|
|
1260
1820
|
function isRouteInstance(input) {
|
|
1821
|
+
if (input instanceof Route) {
|
|
1822
|
+
return true;
|
|
1823
|
+
}
|
|
1261
1824
|
return isInstance(input, 'Route');
|
|
1262
1825
|
}
|
|
1263
1826
|
|
|
1827
|
+
function transformRouterOptions(input) {
|
|
1828
|
+
if (typeof input.etag !== 'undefined') {
|
|
1829
|
+
input.etag = buildEtagFn(input.etag);
|
|
1830
|
+
}
|
|
1831
|
+
if (typeof input.trustProxy !== 'undefined') {
|
|
1832
|
+
input.trustProxy = buildTrustProxyFn(input.trustProxy);
|
|
1833
|
+
}
|
|
1834
|
+
return input;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
let nextId = 0;
|
|
1838
|
+
function generateRouterID() {
|
|
1839
|
+
return ++nextId;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1264
1842
|
function isRouterInstance(input) {
|
|
1843
|
+
if (input instanceof Router) {
|
|
1844
|
+
return true;
|
|
1845
|
+
}
|
|
1265
1846
|
return isInstance(input, 'Router');
|
|
1266
1847
|
}
|
|
1267
1848
|
class Router {
|
|
1268
1849
|
// --------------------------------------------------
|
|
1269
|
-
setPathMatcherOptions(input) {
|
|
1270
|
-
this.pathMatcherOptions = input;
|
|
1271
|
-
if (this.pathMatcher) {
|
|
1272
|
-
this.pathMatcher.regexpOptions = this.pathMatcherOptions;
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
1850
|
setPath(value) {
|
|
1276
1851
|
if (value === '/' || !isPath(value)) {
|
|
1277
|
-
this.path = '/';
|
|
1278
1852
|
return;
|
|
1279
1853
|
}
|
|
1854
|
+
let path;
|
|
1280
1855
|
if (typeof value === 'string') {
|
|
1281
|
-
|
|
1856
|
+
path = withLeadingSlash(withoutTrailingSlash(`${value}`));
|
|
1282
1857
|
} else {
|
|
1283
|
-
|
|
1858
|
+
path = value;
|
|
1284
1859
|
}
|
|
1285
|
-
this.pathMatcher = new PathMatcher(
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
return (req, res)=>{
|
|
1291
|
-
this.dispatch(req, res);
|
|
1292
|
-
};
|
|
1293
|
-
}
|
|
1294
|
-
/* istanbul ignore next */ listen(port) {
|
|
1295
|
-
const server = createServer(this.createListener());
|
|
1296
|
-
return server.listen(port);
|
|
1860
|
+
this.pathMatcher = new PathMatcher(path, {
|
|
1861
|
+
end: false,
|
|
1862
|
+
sensitive: false,
|
|
1863
|
+
...this.pathMatcherOptions ? this.pathMatcherOptions : {}
|
|
1864
|
+
});
|
|
1297
1865
|
}
|
|
1298
1866
|
// --------------------------------------------------
|
|
1299
1867
|
matchPath(path) {
|
|
@@ -1303,35 +1871,9 @@ class Router {
|
|
|
1303
1871
|
return true;
|
|
1304
1872
|
}
|
|
1305
1873
|
// --------------------------------------------------
|
|
1306
|
-
dispatch(
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
let allowedMethods = [];
|
|
1310
|
-
if (this.isRoot && typeof this.timeout === 'number') {
|
|
1311
|
-
createRequestTimeout(res, this.timeout, done);
|
|
1312
|
-
}
|
|
1313
|
-
const fn = (err)=>{
|
|
1314
|
-
/* istanbul ignore if */ if (!this.isRoot) {
|
|
1315
|
-
if (typeof done !== 'undefined') {
|
|
1316
|
-
setImmediate(()=>done(err));
|
|
1317
|
-
}
|
|
1318
|
-
return;
|
|
1319
|
-
}
|
|
1320
|
-
if (typeof err !== 'undefined') {
|
|
1321
|
-
res.statusCode = 400;
|
|
1322
|
-
res.end();
|
|
1323
|
-
return;
|
|
1324
|
-
}
|
|
1325
|
-
if (req.method && req.method.toLowerCase() === MethodName.OPTIONS) {
|
|
1326
|
-
const options = allowedMethods.map((key)=>key.toUpperCase()).join(',');
|
|
1327
|
-
res.setHeader(HeaderName.ALLOW, options);
|
|
1328
|
-
send(res, options);
|
|
1329
|
-
return;
|
|
1330
|
-
}
|
|
1331
|
-
res.statusCode = 404;
|
|
1332
|
-
res.end();
|
|
1333
|
-
};
|
|
1334
|
-
let path = meta.path || useRequestPath(req);
|
|
1874
|
+
async dispatch(event, meta = {}) {
|
|
1875
|
+
const allowedMethods = [];
|
|
1876
|
+
let path = meta.path || useRequestPath(event.req);
|
|
1335
1877
|
if (this.pathMatcher) {
|
|
1336
1878
|
const output = this.pathMatcher.exec(path);
|
|
1337
1879
|
if (typeof output !== 'undefined') {
|
|
@@ -1345,74 +1887,78 @@ class Router {
|
|
|
1345
1887
|
}
|
|
1346
1888
|
}
|
|
1347
1889
|
meta.path = path;
|
|
1890
|
+
if (meta.routerIds) {
|
|
1891
|
+
meta.routerIds.push(this.id);
|
|
1892
|
+
} else {
|
|
1893
|
+
meta.routerIds = [
|
|
1894
|
+
this.id
|
|
1895
|
+
];
|
|
1896
|
+
}
|
|
1348
1897
|
if (!meta.mountPath) {
|
|
1349
1898
|
meta.mountPath = '/';
|
|
1350
1899
|
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
index++;
|
|
1360
|
-
layer = this.stack[index];
|
|
1361
|
-
if (isLayerInstance(layer)) {
|
|
1362
|
-
if (!layer.isError() && err) {
|
|
1363
|
-
continue;
|
|
1364
|
-
}
|
|
1365
|
-
match = layer.matchPath(path);
|
|
1900
|
+
let err;
|
|
1901
|
+
let layer;
|
|
1902
|
+
let match = false;
|
|
1903
|
+
for(let i = 0; i < this.stack.length; i++){
|
|
1904
|
+
layer = this.stack[i];
|
|
1905
|
+
if (layer instanceof Layer) {
|
|
1906
|
+
if (!layer.isError() && err) {
|
|
1907
|
+
continue;
|
|
1366
1908
|
}
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1909
|
+
match = layer.matchPath(path);
|
|
1910
|
+
}
|
|
1911
|
+
if (isRouterInstance(layer)) {
|
|
1912
|
+
match = layer.matchPath(path);
|
|
1913
|
+
}
|
|
1914
|
+
if (isRouteInstance(layer)) {
|
|
1915
|
+
match = layer.matchPath(path);
|
|
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());
|
|
1377
1920
|
}
|
|
1378
1921
|
}
|
|
1379
1922
|
}
|
|
1380
|
-
if (!match
|
|
1381
|
-
|
|
1382
|
-
return;
|
|
1923
|
+
if (!match) {
|
|
1924
|
+
continue;
|
|
1383
1925
|
}
|
|
1384
|
-
const layerMeta =
|
|
1385
|
-
...meta
|
|
1386
|
-
};
|
|
1926
|
+
const layerMeta = cloneDispatcherMeta(meta);
|
|
1387
1927
|
if (isLayerInstance(layer)) {
|
|
1388
1928
|
const output = layer.exec(path);
|
|
1389
1929
|
if (output) {
|
|
1390
|
-
layerMeta.params =
|
|
1930
|
+
layerMeta.params = mergeDispatcherMetaParams(layerMeta.params, output.params);
|
|
1391
1931
|
layerMeta.mountPath = cleanDoubleSlashes(`${layerMeta.mountPath || ''}/${output.path}`);
|
|
1392
1932
|
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
if (isLayerInstance(layer) && layer.isError()) {
|
|
1396
|
-
layer.dispatch(req, res, layerMeta, next, err);
|
|
1397
|
-
return;
|
|
1933
|
+
if (err) {
|
|
1934
|
+
layerMeta.error = err;
|
|
1398
1935
|
}
|
|
1399
|
-
|
|
1400
|
-
|
|
1936
|
+
} else if (err) {
|
|
1937
|
+
continue;
|
|
1401
1938
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
/* istanbul ignore next */ dispatchAsync(req, res) {
|
|
1407
|
-
return new Promise((resolve, reject)=>{
|
|
1408
|
-
this.dispatch(req, res, {}, (err)=>{
|
|
1409
|
-
if (err) {
|
|
1410
|
-
reject(err);
|
|
1411
|
-
return;
|
|
1939
|
+
try {
|
|
1940
|
+
const dispatched = await layer.dispatch(event, layerMeta);
|
|
1941
|
+
if (dispatched) {
|
|
1942
|
+
return true;
|
|
1412
1943
|
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1944
|
+
} catch (e) {
|
|
1945
|
+
if (e instanceof Error) {
|
|
1946
|
+
err = e;
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
if (err) {
|
|
1951
|
+
throw err;
|
|
1952
|
+
}
|
|
1953
|
+
if (event.req.method && event.req.method.toLowerCase() === MethodName.OPTIONS) {
|
|
1954
|
+
const options = distinctArray(allowedMethods).map((key)=>key.toUpperCase()).join(',');
|
|
1955
|
+
if (!isResponseGone(event.res)) {
|
|
1956
|
+
event.res.setHeader(HeaderName.ALLOW, options);
|
|
1957
|
+
await send(event.res, options);
|
|
1958
|
+
}
|
|
1959
|
+
return true;
|
|
1960
|
+
}
|
|
1961
|
+
return false;
|
|
1416
1962
|
}
|
|
1417
1963
|
// --------------------------------------------------
|
|
1418
1964
|
route(path) {
|
|
@@ -1426,7 +1972,9 @@ class Router {
|
|
|
1426
1972
|
const route = new Route({
|
|
1427
1973
|
path,
|
|
1428
1974
|
pathMatcher: {
|
|
1429
|
-
|
|
1975
|
+
...this.pathMatcherOptions ? {
|
|
1976
|
+
sensitive: this.pathMatcherOptions.sensitive
|
|
1977
|
+
} : {}
|
|
1430
1978
|
}
|
|
1431
1979
|
});
|
|
1432
1980
|
this.stack.push(route);
|
|
@@ -1481,7 +2029,6 @@ class Router {
|
|
|
1481
2029
|
if (path) {
|
|
1482
2030
|
item.setPath(path);
|
|
1483
2031
|
}
|
|
1484
|
-
item.setPathMatcherOptions(this.pathMatcherOptions);
|
|
1485
2032
|
this.stack.push(item);
|
|
1486
2033
|
continue;
|
|
1487
2034
|
}
|
|
@@ -1491,7 +2038,9 @@ class Router {
|
|
|
1491
2038
|
pathMatcher: {
|
|
1492
2039
|
strict: false,
|
|
1493
2040
|
end: false,
|
|
1494
|
-
|
|
2041
|
+
...this.pathMatcherOptions ? {
|
|
2042
|
+
sensitive: this.pathMatcherOptions.sensitive
|
|
2043
|
+
} : {}
|
|
1495
2044
|
}
|
|
1496
2045
|
}, item));
|
|
1497
2046
|
}
|
|
@@ -1499,23 +2048,19 @@ class Router {
|
|
|
1499
2048
|
return this;
|
|
1500
2049
|
}
|
|
1501
2050
|
// --------------------------------------------------
|
|
1502
|
-
constructor(
|
|
2051
|
+
constructor(options = {}){
|
|
1503
2052
|
this['@instanceof'] = Symbol.for('Router');
|
|
1504
2053
|
/**
|
|
1505
2054
|
* Array of mounted layers, routes & routers.
|
|
1506
2055
|
*
|
|
1507
2056
|
* @protected
|
|
1508
2057
|
*/ this.stack = [];
|
|
1509
|
-
|
|
1510
|
-
this.pathMatcherOptions =
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
...ctx.pathMatcher || {}
|
|
1514
|
-
};
|
|
1515
|
-
this.timeout = ctx.timeout;
|
|
1516
|
-
this.setPath(ctx.path || '/');
|
|
2058
|
+
this.id = generateRouterID();
|
|
2059
|
+
this.pathMatcherOptions = options.pathMatcher;
|
|
2060
|
+
this.setPath(options.path);
|
|
2061
|
+
setRouterOptions(this.id, transformRouterOptions(options));
|
|
1517
2062
|
}
|
|
1518
2063
|
}
|
|
1519
2064
|
|
|
1520
|
-
export { HeaderName, Layer, MethodName, PathMatcher, Route, Router, appendResponseHeader, appendResponseHeaderDirective,
|
|
2065
|
+
export { HeaderName, Layer, MethodName, PathMatcher, Route, Router, appendResponseHeader, appendResponseHeaderDirective, cloneDispatcherMeta, cloneDispatcherMetaParams, createNodeDispatcher, createRawDispatcher, createRequest, createResponse, createWebDispatcher, dispatchNodeRequest, dispatchRawRequest, dispatchWebRequest, extendRequestBody, extendRequestCookies, extendRequestQuery, getRequestAcceptableCharset, getRequestAcceptableCharsets, getRequestAcceptableContentType, getRequestAcceptableContentTypes, getRequestAcceptableEncoding, getRequestAcceptableEncodings, getRequestAcceptableLanguage, getRequestAcceptableLanguages, getRequestHeader, getRequestHostName, getRequestIP, getRequestProtocol, hasRequestBody, hasRequestCookies, hasRequestQuery, isLayerInstance, isPath, isRequestCacheable, isResponseGone, isRouteInstance, isRouterInstance, matchRequestContentType, mergeDispatcherMetaParams, send, sendAccepted, sendCreated, sendFile, sendFormat, sendRedirect, sendStream, sendWebBlob, sendWebResponse, setRequestBody, setRequestCookies, setRequestEnv, setRequestHeader, setRequestMountPath, setRequestParam, setRequestParams, setRequestQuery, setRequestRouterIds, setResponseCacheHeaders, setResponseContentTypeByFileName, setResponseHeaderAttachment, setResponseHeaderContentType, unsetRequestEnv, useRequestBody, useRequestCookie, useRequestCookies, useRequestEnv, useRequestMountPath, useRequestNegotiator, useRequestParam, useRequestParams, useRequestPath, useRequestQuery, useRequestRouterIds };
|
|
1521
2066
|
//# sourceMappingURL=index.mjs.map
|