routup 1.0.3 → 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 +163 -26
- 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 +7 -0
- package/dist/dispatcher/adapters/raw/module.d.ts +4 -0
- package/dist/dispatcher/adapters/raw/type.d.ts +18 -0
- package/dist/{route → dispatcher/adapters/web}/index.d.ts +0 -1
- 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 +32 -0
- package/dist/dispatcher/utils.d.ts +5 -0
- 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 -1
- 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 +1202 -629
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.mjs +1172 -597
- package/dist/index.mjs.map +1 -1
- package/dist/layer/constants.d.ts +1 -0
- package/dist/layer/module.d.ts +13 -10
- package/dist/layer/type.d.ts +6 -4
- package/dist/path/index.d.ts +1 -0
- package/dist/path/matcher.d.ts +5 -6
- package/dist/path/type.d.ts +1 -0
- package/dist/path/utils.d.ts +2 -0
- 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/{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 +13 -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 +3 -0
- package/dist/response/helpers/send-web-blob.d.ts +3 -0
- package/dist/response/helpers/send-web-response.d.ts +3 -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 +3 -0
- package/dist/response/module.d.ts +3 -0
- package/dist/response/types.d.ts +4 -0
- package/dist/router/constants.d.ts +1 -0
- package/dist/router/index.d.ts +1 -1
- package/dist/router/module.d.ts +34 -50
- package/dist/router/utils.d.ts +3 -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 +41 -0
- package/dist/types.d.ts +10 -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/is-instance.d.ts +1 -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 +21 -19
- package/dist/config/module.d.ts +0 -8
- package/dist/config/type.d.ts +0 -34
- 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/route/module.d.ts +0 -27
- package/dist/route/type.d.ts +0 -6
- package/dist/route/utils.d.ts +0 -2
- 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,184 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import process from 'node:process';
|
|
3
|
-
import zod from 'zod';
|
|
4
|
-
import crypto from 'node:crypto';
|
|
5
|
-
import { Stats, stat, createReadStream } from 'node:fs';
|
|
1
|
+
import { HTTPError } from '@ebec/http';
|
|
6
2
|
import { merge, hasOwnProperty, distinctArray } from 'smob';
|
|
3
|
+
import { Buffer } from 'buffer';
|
|
4
|
+
import { subtle } from 'uncrypto';
|
|
7
5
|
import { compile, all } from 'proxy-addr';
|
|
8
6
|
import { getType, get } from 'mime-explorer';
|
|
9
|
-
import { GatewayTimeoutErrorOptions, BadRequestError } from '@ebec/http';
|
|
10
7
|
import Negotiator from 'negotiator';
|
|
11
|
-
import {
|
|
12
|
-
import path from 'node:path';
|
|
8
|
+
import { Readable, Writable } from 'readable-stream';
|
|
13
9
|
import { pathToRegexp } from 'path-to-regexp';
|
|
14
|
-
|
|
10
|
+
|
|
11
|
+
var MethodName;
|
|
12
|
+
(function(MethodName) {
|
|
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";
|
|
20
|
+
})(MethodName || (MethodName = {}));
|
|
21
|
+
var HeaderName;
|
|
22
|
+
(function(HeaderName) {
|
|
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";
|
|
52
|
+
})(HeaderName || (HeaderName = {}));
|
|
53
|
+
|
|
54
|
+
class ErrorProxy extends HTTPError {
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function isError(input) {
|
|
58
|
+
return input instanceof ErrorProxy;
|
|
59
|
+
}
|
|
60
|
+
|
|
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;
|
|
71
|
+
}
|
|
72
|
+
if (typeof input === 'string') {
|
|
73
|
+
return new ErrorProxy(input);
|
|
74
|
+
}
|
|
75
|
+
return new ErrorProxy({
|
|
76
|
+
cause: input
|
|
77
|
+
}, input);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
Set-Cookie header field-values are sometimes comma joined in one string. This splits them without choking on commas
|
|
82
|
+
that are within a single set-cookie field-value, such as in the Expires portion.
|
|
83
|
+
|
|
84
|
+
This is uncommon, but explicitly allowed - see https://tools.ietf.org/html/rfc2616#section-4.2
|
|
85
|
+
Node.js does this for every header *except* set-cookie - see https://github.com/nodejs/node/blob/d5e363b77ebaf1caf67cd7528224b651c86815c1/lib/_http_incoming.js#L128
|
|
86
|
+
React Native's fetch does this for *every* header, including set-cookie.
|
|
87
|
+
|
|
88
|
+
Based on: https://github.com/google/j2objc/commit/16820fdbc8f76ca0c33472810ce0cb03d20efe25
|
|
89
|
+
Credits to: https://github.com/tomball for original and https://github.com/chrusart for JavaScript implementation
|
|
90
|
+
*/ function splitCookiesString(input) {
|
|
91
|
+
if (Array.isArray(input)) {
|
|
92
|
+
return input.flatMap((el)=>splitCookiesString(el));
|
|
93
|
+
}
|
|
94
|
+
if (typeof input !== 'string') {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
const cookiesStrings = [];
|
|
98
|
+
let pos = 0;
|
|
99
|
+
let start;
|
|
100
|
+
let ch;
|
|
101
|
+
let lastComma;
|
|
102
|
+
let nextStart;
|
|
103
|
+
let cookiesSeparatorFound;
|
|
104
|
+
const skipWhitespace = ()=>{
|
|
105
|
+
while(pos < input.length && /\s/.test(input.charAt(pos))){
|
|
106
|
+
pos += 1;
|
|
107
|
+
}
|
|
108
|
+
return pos < input.length;
|
|
109
|
+
};
|
|
110
|
+
const notSpecialChar = ()=>{
|
|
111
|
+
ch = input.charAt(pos);
|
|
112
|
+
return ch !== '=' && ch !== ';' && ch !== ',';
|
|
113
|
+
};
|
|
114
|
+
while(pos < input.length){
|
|
115
|
+
start = pos;
|
|
116
|
+
cookiesSeparatorFound = false;
|
|
117
|
+
while(skipWhitespace()){
|
|
118
|
+
ch = input.charAt(pos);
|
|
119
|
+
if (ch === ',') {
|
|
120
|
+
// ',' is a cookie separator if we have later first '=', not ';' or ','
|
|
121
|
+
lastComma = pos;
|
|
122
|
+
pos += 1;
|
|
123
|
+
skipWhitespace();
|
|
124
|
+
nextStart = pos;
|
|
125
|
+
while(pos < input.length && notSpecialChar()){
|
|
126
|
+
pos += 1;
|
|
127
|
+
}
|
|
128
|
+
// currently special character
|
|
129
|
+
if (pos < input.length && input.charAt(pos) === '=') {
|
|
130
|
+
// we found cookies separator
|
|
131
|
+
cookiesSeparatorFound = true;
|
|
132
|
+
// pos is inside the next cookie, so back up and return it.
|
|
133
|
+
pos = nextStart;
|
|
134
|
+
cookiesStrings.push(input.substring(start, lastComma));
|
|
135
|
+
start = pos;
|
|
136
|
+
} else {
|
|
137
|
+
// in param ',' or param separator ';',
|
|
138
|
+
// we continue from that comma
|
|
139
|
+
pos = lastComma + 1;
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
pos += 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (!cookiesSeparatorFound || pos >= input.length) {
|
|
146
|
+
cookiesStrings.push(input.substring(start, input.length));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return cookiesStrings;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function transformHeaderToTuples(key, value) {
|
|
153
|
+
const output = [];
|
|
154
|
+
if (Array.isArray(value)) {
|
|
155
|
+
for(let j = 0; j < value.length; j++){
|
|
156
|
+
output.push([
|
|
157
|
+
key,
|
|
158
|
+
value[j]
|
|
159
|
+
]);
|
|
160
|
+
}
|
|
161
|
+
} else if (value !== undefined) {
|
|
162
|
+
output.push([
|
|
163
|
+
key,
|
|
164
|
+
String(value)
|
|
165
|
+
]);
|
|
166
|
+
}
|
|
167
|
+
return output;
|
|
168
|
+
}
|
|
169
|
+
function transformHeadersToTuples(input) {
|
|
170
|
+
const output = [];
|
|
171
|
+
const keys = Object.keys(input);
|
|
172
|
+
for(let i = 0; i < keys.length; i++){
|
|
173
|
+
const key = keys[i].toLowerCase();
|
|
174
|
+
output.push(...transformHeaderToTuples(key, input[key]));
|
|
175
|
+
}
|
|
176
|
+
return output;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isObject(item) {
|
|
180
|
+
return !!item && typeof item === 'object' && !Array.isArray(item);
|
|
181
|
+
}
|
|
15
182
|
|
|
16
183
|
/**
|
|
17
184
|
* Determine if object is a Stats object.
|
|
@@ -20,15 +187,17 @@ import { createServer } from 'node:http';
|
|
|
20
187
|
* @return {boolean}
|
|
21
188
|
* @api private
|
|
22
189
|
*/ function isStatsObject(obj) {
|
|
23
|
-
/* istanbul ignore next */ if (typeof Stats === 'function' && obj instanceof Stats) {
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
190
|
// quack quack
|
|
27
|
-
return
|
|
191
|
+
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';
|
|
192
|
+
}
|
|
193
|
+
async function sha1(str) {
|
|
194
|
+
const enc = new TextEncoder();
|
|
195
|
+
const hash = await subtle.digest('SHA-1', enc.encode(str));
|
|
196
|
+
return btoa(String.fromCharCode(...new Uint8Array(hash)));
|
|
28
197
|
}
|
|
29
198
|
/**
|
|
30
199
|
* Generate an ETag.
|
|
31
|
-
*/ function generateETag(input) {
|
|
200
|
+
*/ async function generateETag(input) {
|
|
32
201
|
if (isStatsObject(input)) {
|
|
33
202
|
const mtime = input.mtime.getTime().toString(16);
|
|
34
203
|
const size = input.size.toString(16);
|
|
@@ -40,30 +209,26 @@ import { createServer } from 'node:http';
|
|
|
40
209
|
}
|
|
41
210
|
const entity = Buffer.isBuffer(input) ? input.toString('utf-8') : input;
|
|
42
211
|
// compute hash of entity
|
|
43
|
-
const hash =
|
|
44
|
-
return `"${entity.length.toString(16)}-${hash}"`;
|
|
212
|
+
const hash = await sha1(entity);
|
|
213
|
+
return `"${entity.length.toString(16)}-${hash.substring(0, 27)}"`;
|
|
45
214
|
}
|
|
46
215
|
/**
|
|
47
216
|
* Create a simple ETag.
|
|
48
|
-
*/ function createEtag(input, options) {
|
|
217
|
+
*/ async function createEtag(input, options) {
|
|
49
218
|
options = options || {};
|
|
50
219
|
const weak = typeof options.weak === 'boolean' ? options.weak : isStatsObject(input);
|
|
51
220
|
// generate entity tag
|
|
52
|
-
const tag = generateETag(input);
|
|
221
|
+
const tag = await generateETag(input);
|
|
53
222
|
return weak ? `W/${tag}` : tag;
|
|
54
223
|
}
|
|
55
224
|
|
|
56
|
-
function isObject(item) {
|
|
57
|
-
return !!item && typeof item === 'object' && !Array.isArray(item);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
225
|
function buildEtagFn(input) {
|
|
61
226
|
if (typeof input === 'function') {
|
|
62
227
|
return input;
|
|
63
228
|
}
|
|
64
229
|
input = input ?? true;
|
|
65
230
|
if (input === false) {
|
|
66
|
-
return ()=>undefined;
|
|
231
|
+
return ()=>Promise.resolve(undefined);
|
|
67
232
|
}
|
|
68
233
|
let options = {
|
|
69
234
|
weak: true
|
|
@@ -71,7 +236,7 @@ function buildEtagFn(input) {
|
|
|
71
236
|
if (isObject(input)) {
|
|
72
237
|
options = merge(input, options);
|
|
73
238
|
}
|
|
74
|
-
return (body, encoding, size)=>{
|
|
239
|
+
return async (body, encoding, size)=>{
|
|
75
240
|
const buff = Buffer.isBuffer(body) ? body : Buffer.from(body, encoding);
|
|
76
241
|
if (typeof options.threshold !== 'undefined') {
|
|
77
242
|
size = size ?? Buffer.byteLength(buff);
|
|
@@ -99,8 +264,11 @@ function buildTrustProxyFn(input) {
|
|
|
99
264
|
return compile(input || []);
|
|
100
265
|
}
|
|
101
266
|
|
|
102
|
-
function isInstance(input,
|
|
103
|
-
|
|
267
|
+
function isInstance(input, sym) {
|
|
268
|
+
if (!isObject(input)) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
return input['@instanceof'] === sym;
|
|
104
272
|
}
|
|
105
273
|
|
|
106
274
|
function getMimeType(type) {
|
|
@@ -120,8 +288,25 @@ function getCharsetForMimeType(type) {
|
|
|
120
288
|
return undefined;
|
|
121
289
|
}
|
|
122
290
|
|
|
123
|
-
|
|
124
|
-
|
|
291
|
+
/**
|
|
292
|
+
* Based on https://github.com/unjs/pathe v1.1.1 (055f50a6f1131f4e5c56cf259dd8816168fba329)
|
|
293
|
+
*/ function normalizeWindowsPath(input = '') {
|
|
294
|
+
if (!input || !input.includes('\\')) {
|
|
295
|
+
return input;
|
|
296
|
+
}
|
|
297
|
+
return input.replace(/\\/g, '/');
|
|
298
|
+
}
|
|
299
|
+
const EXTNAME_RE = /.(\.[^./]+)$/;
|
|
300
|
+
function extname(input) {
|
|
301
|
+
const match = EXTNAME_RE.exec(normalizeWindowsPath(input));
|
|
302
|
+
return match && match[1] || '';
|
|
303
|
+
}
|
|
304
|
+
function basename(input, extension) {
|
|
305
|
+
const lastSegment = normalizeWindowsPath(input).split('/').pop();
|
|
306
|
+
if (!lastSegment) {
|
|
307
|
+
return input;
|
|
308
|
+
}
|
|
309
|
+
return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
|
|
125
310
|
}
|
|
126
311
|
|
|
127
312
|
function isPromise(p) {
|
|
@@ -130,24 +315,14 @@ function isPromise(p) {
|
|
|
130
315
|
typeof p.then === 'function');
|
|
131
316
|
}
|
|
132
317
|
|
|
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
|
-
});
|
|
318
|
+
function isNodeStream(input) {
|
|
319
|
+
return isObject(input) && typeof input.pipe === 'function' && typeof input.read === 'function';
|
|
320
|
+
}
|
|
321
|
+
function isWebStream(input) {
|
|
322
|
+
return isObject(input) && typeof input.pipeTo === 'function';
|
|
323
|
+
}
|
|
324
|
+
function isStream(data) {
|
|
325
|
+
return isNodeStream(data) || isWebStream(data);
|
|
151
326
|
}
|
|
152
327
|
|
|
153
328
|
const TRAILING_SLASH_RE = /\/$|\/\?/;
|
|
@@ -167,115 +342,26 @@ function withoutTrailingSlash(input = '', queryParams = false) {
|
|
|
167
342
|
const [s0, ...s] = input.split('?');
|
|
168
343
|
return (s0.slice(0, -1) || '/') + (s.length ? `?${s.join('?')}` : '');
|
|
169
344
|
}
|
|
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
345
|
function hasLeadingSlash(input = '') {
|
|
181
346
|
return input.startsWith('/');
|
|
182
347
|
}
|
|
183
|
-
function withoutLeadingSlash(input = '') {
|
|
184
|
-
return (hasLeadingSlash(input) ? input.substr(1) : input) || '/';
|
|
185
|
-
}
|
|
186
348
|
function withLeadingSlash(input = '') {
|
|
187
349
|
return hasLeadingSlash(input) ? input : `/${input}`;
|
|
188
350
|
}
|
|
189
351
|
function cleanDoubleSlashes(input = '') {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
let instance;
|
|
194
|
-
function buildConfig() {
|
|
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
|
-
});
|
|
215
|
-
}
|
|
216
|
-
function useConfig() {
|
|
217
|
-
if (typeof instance !== 'undefined') {
|
|
218
|
-
return instance;
|
|
352
|
+
if (input.indexOf('://') !== -1) {
|
|
353
|
+
return input.split('://').map((str)=>cleanDoubleSlashes(str)).join('://');
|
|
219
354
|
}
|
|
220
|
-
|
|
221
|
-
return instance;
|
|
355
|
+
return input.replace(/\/+/g, '/');
|
|
222
356
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
function setConfigOption(key, value) {
|
|
227
|
-
const config = useConfig();
|
|
228
|
-
config.setRaw(key, value);
|
|
229
|
-
return config.get();
|
|
357
|
+
|
|
358
|
+
function isWebBlob(input) {
|
|
359
|
+
return typeof Blob !== 'undefined' && input instanceof Blob;
|
|
230
360
|
}
|
|
231
|
-
function
|
|
232
|
-
|
|
233
|
-
return config.get(key);
|
|
361
|
+
function isWebResponse(input) {
|
|
362
|
+
return typeof Response !== 'undefined' && input instanceof Response;
|
|
234
363
|
}
|
|
235
364
|
|
|
236
|
-
var MethodName;
|
|
237
|
-
(function(MethodName) {
|
|
238
|
-
MethodName["GET"] = 'get';
|
|
239
|
-
MethodName["POST"] = 'post';
|
|
240
|
-
MethodName["PUT"] = 'put';
|
|
241
|
-
MethodName["PATCH"] = 'patch';
|
|
242
|
-
MethodName["DELETE"] = 'delete';
|
|
243
|
-
MethodName["OPTIONS"] = 'options';
|
|
244
|
-
MethodName["HEAD"] = 'head';
|
|
245
|
-
})(MethodName || (MethodName = {}));
|
|
246
|
-
var HeaderName;
|
|
247
|
-
(function(HeaderName) {
|
|
248
|
-
HeaderName["ACCEPT"] = 'accept';
|
|
249
|
-
HeaderName["ACCEPT_CHARSET"] = 'accept-charset';
|
|
250
|
-
HeaderName["ACCEPT_ENCODING"] = 'accept-encoding';
|
|
251
|
-
HeaderName["ACCEPT_LANGUAGE"] = 'accept-language';
|
|
252
|
-
HeaderName["ACCEPT_RANGES"] = 'accept-ranges';
|
|
253
|
-
HeaderName["ALLOW"] = 'allow';
|
|
254
|
-
HeaderName["CACHE_CONTROL"] = 'cache-control';
|
|
255
|
-
HeaderName["CONTENT_DISPOSITION"] = 'content-disposition';
|
|
256
|
-
HeaderName["CONTENT_ENCODING"] = 'content-encoding';
|
|
257
|
-
HeaderName["CONTENT_LENGTH"] = 'content-length';
|
|
258
|
-
HeaderName["CONTENT_RANGE"] = 'content-range';
|
|
259
|
-
HeaderName["CONTENT_TYPE"] = 'content-type';
|
|
260
|
-
HeaderName["COOKIE"] = 'cookie';
|
|
261
|
-
HeaderName["ETag"] = 'etag';
|
|
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 = {}));
|
|
278
|
-
|
|
279
365
|
const BodySymbol = Symbol.for('ReqBody');
|
|
280
366
|
function useRequestBody(req, key) {
|
|
281
367
|
let body;
|
|
@@ -537,14 +623,48 @@ function matchRequestContentType(req, contentType) {
|
|
|
537
623
|
return header.split('; ').shift() === getMimeType(contentType);
|
|
538
624
|
}
|
|
539
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
|
+
|
|
650
|
+
const routerSymbol = Symbol.for('ReqRouterID');
|
|
651
|
+
function setRequestRouterPath(req, path) {
|
|
652
|
+
req[routerSymbol] = path;
|
|
653
|
+
}
|
|
654
|
+
function useRequestRouterPath(req) {
|
|
655
|
+
if (routerSymbol in req) {
|
|
656
|
+
return req[routerSymbol];
|
|
657
|
+
}
|
|
658
|
+
return undefined;
|
|
659
|
+
}
|
|
660
|
+
|
|
540
661
|
function getRequestHostName(req, options) {
|
|
541
662
|
options = options || {};
|
|
542
663
|
let trustProxy;
|
|
543
664
|
if (typeof options.trustProxy !== 'undefined') {
|
|
544
665
|
trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
545
666
|
} else {
|
|
546
|
-
|
|
547
|
-
trustProxy = config.get('trustProxy');
|
|
667
|
+
trustProxy = findRouterOption('trustProxy', useRequestRouterPath(req));
|
|
548
668
|
}
|
|
549
669
|
let hostname = req.headers[HeaderName.X_FORWARDED_HOST];
|
|
550
670
|
if (!hostname || !req.socket.remoteAddress || !trustProxy(req.socket.remoteAddress, 0)) {
|
|
@@ -570,8 +690,7 @@ function getRequestIP(req, options) {
|
|
|
570
690
|
if (typeof options.trustProxy !== 'undefined') {
|
|
571
691
|
trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
572
692
|
} else {
|
|
573
|
-
|
|
574
|
-
trustProxy = config.get('trustProxy');
|
|
693
|
+
trustProxy = findRouterOption('trustProxy', useRequestRouterPath(req));
|
|
575
694
|
}
|
|
576
695
|
const addrs = all(req, trustProxy);
|
|
577
696
|
return addrs[addrs.length - 1];
|
|
@@ -632,8 +751,7 @@ function getRequestProtocol(req, options) {
|
|
|
632
751
|
if (typeof options.trustProxy !== 'undefined') {
|
|
633
752
|
trustProxy = buildTrustProxyFn(options.trustProxy);
|
|
634
753
|
} else {
|
|
635
|
-
|
|
636
|
-
trustProxy = config.get('trustProxy');
|
|
754
|
+
trustProxy = findRouterOption('trustProxy', useRequestRouterPath(req));
|
|
637
755
|
}
|
|
638
756
|
let protocol = options.default;
|
|
639
757
|
/* istanbul ignore next */ if (hasOwnProperty(req.socket, 'encrypted') && !!req.socket.encrypted) {
|
|
@@ -701,6 +819,78 @@ function extendRequestQuery(req, key, value) {
|
|
|
701
819
|
setRequestQuery(req, key, value);
|
|
702
820
|
}
|
|
703
821
|
|
|
822
|
+
function createRequest(context) {
|
|
823
|
+
let readable;
|
|
824
|
+
if (context.body) {
|
|
825
|
+
if (isWebStream(context.body)) {
|
|
826
|
+
readable = Readable.fromWeb(context.body);
|
|
827
|
+
} else {
|
|
828
|
+
readable = Readable.from(context.body);
|
|
829
|
+
}
|
|
830
|
+
} else {
|
|
831
|
+
readable = new Readable();
|
|
832
|
+
}
|
|
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]);
|
|
841
|
+
}
|
|
842
|
+
} else if (typeof header === 'string') {
|
|
843
|
+
rawHeaders.push(keys[i], header);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
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
|
+
}
|
|
858
|
+
}
|
|
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
|
+
|
|
704
894
|
function setResponseCacheHeaders(res, options) {
|
|
705
895
|
options = options || {};
|
|
706
896
|
const cacheControls = [
|
|
@@ -716,6 +906,17 @@ function setResponseCacheHeaders(res, options) {
|
|
|
716
906
|
res.setHeader('cache-control', cacheControls.join(', '));
|
|
717
907
|
}
|
|
718
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
|
+
|
|
719
920
|
function appendResponseHeader(res, name, value) {
|
|
720
921
|
let header = res.getHeader(name);
|
|
721
922
|
if (!header) {
|
|
@@ -765,7 +966,7 @@ function appendResponseHeaderDirective(res, name, value) {
|
|
|
765
966
|
}
|
|
766
967
|
|
|
767
968
|
function setResponseContentTypeByFileName(res, fileName) {
|
|
768
|
-
const ext =
|
|
969
|
+
const ext = extname(fileName);
|
|
769
970
|
if (ext) {
|
|
770
971
|
let type = getMimeType(ext.substring(1));
|
|
771
972
|
if (type) {
|
|
@@ -777,23 +978,6 @@ function setResponseContentTypeByFileName(res, fileName) {
|
|
|
777
978
|
}
|
|
778
979
|
}
|
|
779
980
|
}
|
|
780
|
-
/* istanbul ignore next */ function onResponseFinished(res, cb) {
|
|
781
|
-
let called;
|
|
782
|
-
const callCallback = (err)=>{
|
|
783
|
-
if (called) return;
|
|
784
|
-
called = true;
|
|
785
|
-
cb(err);
|
|
786
|
-
};
|
|
787
|
-
res.on('finish', ()=>{
|
|
788
|
-
callCallback();
|
|
789
|
-
});
|
|
790
|
-
res.on('close', ()=>{
|
|
791
|
-
callCallback();
|
|
792
|
-
});
|
|
793
|
-
res.on('error', (err)=>{
|
|
794
|
-
callCallback(err);
|
|
795
|
-
});
|
|
796
|
-
}
|
|
797
981
|
|
|
798
982
|
function setResponseHeaderAttachment(res, filename) {
|
|
799
983
|
if (typeof filename === 'string') {
|
|
@@ -815,7 +999,7 @@ function setResponseHeaderContentType(res, input, ifNotExists) {
|
|
|
815
999
|
}
|
|
816
1000
|
}
|
|
817
1001
|
|
|
818
|
-
function send(res, chunk) {
|
|
1002
|
+
async function send(res, chunk) {
|
|
819
1003
|
switch(typeof chunk){
|
|
820
1004
|
case 'string':
|
|
821
1005
|
{
|
|
@@ -826,11 +1010,9 @@ function send(res, chunk) {
|
|
|
826
1010
|
case 'number':
|
|
827
1011
|
case 'object':
|
|
828
1012
|
{
|
|
829
|
-
if (chunk
|
|
830
|
-
chunk = '';
|
|
831
|
-
} else if (Buffer.isBuffer(chunk)) {
|
|
1013
|
+
if (Buffer.isBuffer(chunk)) {
|
|
832
1014
|
setResponseHeaderContentType(res, 'bin', true);
|
|
833
|
-
} else {
|
|
1015
|
+
} else if (chunk !== null) {
|
|
834
1016
|
chunk = JSON.stringify(chunk);
|
|
835
1017
|
setResponseHeaderContentType(res, 'application/json', true);
|
|
836
1018
|
}
|
|
@@ -845,7 +1027,7 @@ function send(res, chunk) {
|
|
|
845
1027
|
}
|
|
846
1028
|
// populate Content-Length
|
|
847
1029
|
let len;
|
|
848
|
-
if (chunk !== undefined) {
|
|
1030
|
+
if (chunk !== undefined && chunk !== null) {
|
|
849
1031
|
if (Buffer.isBuffer(chunk)) {
|
|
850
1032
|
// get length of Buffer
|
|
851
1033
|
len = chunk.length;
|
|
@@ -860,10 +1042,12 @@ function send(res, chunk) {
|
|
|
860
1042
|
}
|
|
861
1043
|
res.setHeader(HeaderName.CONTENT_LENGTH, `${len}`);
|
|
862
1044
|
}
|
|
863
|
-
const config = useConfig();
|
|
864
|
-
const etagFn = config.get('etag');
|
|
865
1045
|
if (typeof len !== 'undefined') {
|
|
866
|
-
const
|
|
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
|
+
}
|
|
867
1051
|
if (typeof chunkHash === 'string') {
|
|
868
1052
|
res.setHeader(HeaderName.ETag, chunkHash);
|
|
869
1053
|
if (res.req.headers[HeaderName.IF_NONE_MATCH] === chunkHash) {
|
|
@@ -876,24 +1060,30 @@ function send(res, chunk) {
|
|
|
876
1060
|
res.removeHeader(HeaderName.CONTENT_TYPE);
|
|
877
1061
|
res.removeHeader(HeaderName.CONTENT_LENGTH);
|
|
878
1062
|
res.removeHeader(HeaderName.TRANSFER_ENCODING);
|
|
879
|
-
chunk = '';
|
|
880
1063
|
}
|
|
881
1064
|
// alter headers for 205
|
|
882
1065
|
if (res.statusCode === 205) {
|
|
883
1066
|
res.setHeader(HeaderName.CONTENT_LENGTH, 0);
|
|
884
1067
|
res.removeHeader(HeaderName.TRANSFER_ENCODING);
|
|
885
|
-
|
|
1068
|
+
}
|
|
1069
|
+
if (isResponseGone(res)) {
|
|
1070
|
+
return Promise.resolve();
|
|
886
1071
|
}
|
|
887
1072
|
if (res.req.method === 'HEAD') {
|
|
888
1073
|
// skip body for HEAD
|
|
889
1074
|
res.end();
|
|
890
|
-
return;
|
|
1075
|
+
return Promise.resolve();
|
|
1076
|
+
}
|
|
1077
|
+
if (typeof chunk === 'undefined' || chunk === null) {
|
|
1078
|
+
res.end();
|
|
1079
|
+
return Promise.resolve();
|
|
891
1080
|
}
|
|
892
1081
|
if (typeof encoding !== 'undefined') {
|
|
893
1082
|
res.end(chunk, encoding);
|
|
894
|
-
return;
|
|
1083
|
+
return Promise.resolve();
|
|
895
1084
|
}
|
|
896
1085
|
res.end(chunk);
|
|
1086
|
+
return Promise.resolve();
|
|
897
1087
|
}
|
|
898
1088
|
|
|
899
1089
|
function sendAccepted(res, chunk) {
|
|
@@ -908,87 +1098,116 @@ function sendCreated(res, chunk) {
|
|
|
908
1098
|
return send(res, chunk);
|
|
909
1099
|
}
|
|
910
1100
|
|
|
911
|
-
function sendStream(res, stream,
|
|
912
|
-
|
|
913
|
-
stream.
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
1101
|
+
async function sendStream(res, stream, next) {
|
|
1102
|
+
if (isWebStream(stream)) {
|
|
1103
|
+
return stream.pipeTo(new WritableStream({
|
|
1104
|
+
write (chunk) {
|
|
1105
|
+
res.write(chunk);
|
|
1106
|
+
}
|
|
1107
|
+
})).then(()=>{
|
|
1108
|
+
if (next) {
|
|
1109
|
+
return next();
|
|
1110
|
+
}
|
|
920
1111
|
res.end();
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1112
|
+
return Promise.resolve();
|
|
1113
|
+
}).catch((err)=>{
|
|
1114
|
+
if (next) {
|
|
1115
|
+
return next(err);
|
|
1116
|
+
}
|
|
1117
|
+
return Promise.reject(err);
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
return new Promise((resolve, reject)=>{
|
|
1121
|
+
stream.on('open', ()=>{
|
|
1122
|
+
stream.pipe(res);
|
|
1123
|
+
});
|
|
1124
|
+
/* istanbul ignore next */ stream.on('error', (err)=>{
|
|
1125
|
+
if (next) {
|
|
1126
|
+
Promise.resolve().then(()=>next(err)).then(()=>resolve()).catch((e)=>reject(e));
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
927
1129
|
res.end();
|
|
928
|
-
|
|
1130
|
+
reject(err);
|
|
1131
|
+
});
|
|
1132
|
+
stream.on('close', ()=>{
|
|
1133
|
+
if (next) {
|
|
1134
|
+
Promise.resolve().then(()=>next()).then(()=>resolve()).catch((e)=>reject(e));
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
res.end();
|
|
1138
|
+
resolve();
|
|
1139
|
+
});
|
|
929
1140
|
});
|
|
930
1141
|
}
|
|
931
1142
|
|
|
932
|
-
function
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
function sendFile(res, filePath, fn) {
|
|
940
|
-
let options;
|
|
941
|
-
if (typeof filePath === 'string') {
|
|
942
|
-
options = {
|
|
943
|
-
filePath
|
|
944
|
-
};
|
|
945
|
-
} else {
|
|
946
|
-
options = filePath;
|
|
947
|
-
}
|
|
948
|
-
const fileName = path.basename(options.filePath);
|
|
949
|
-
if (options.attachment) {
|
|
950
|
-
const dispositionHeader = res.getHeader(HeaderName.CONTENT_DISPOSITION);
|
|
951
|
-
if (!dispositionHeader) {
|
|
952
|
-
setResponseHeaderAttachment(res, fileName);
|
|
1143
|
+
async function sendFile(res, options, next) {
|
|
1144
|
+
let stats;
|
|
1145
|
+
try {
|
|
1146
|
+
stats = await options.stats();
|
|
1147
|
+
} catch (e) {
|
|
1148
|
+
if (next) {
|
|
1149
|
+
return next(e);
|
|
953
1150
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1151
|
+
if (isResponseGone(res)) {
|
|
1152
|
+
return Promise.resolve();
|
|
1153
|
+
}
|
|
1154
|
+
return Promise.reject(e);
|
|
1155
|
+
}
|
|
1156
|
+
const name = options.name || stats.name;
|
|
1157
|
+
if (name) {
|
|
1158
|
+
const fileName = basename(name);
|
|
1159
|
+
if (options.attachment) {
|
|
1160
|
+
const dispositionHeader = res.getHeader(HeaderName.CONTENT_DISPOSITION);
|
|
1161
|
+
if (!dispositionHeader) {
|
|
1162
|
+
setResponseHeaderAttachment(res, fileName);
|
|
964
1163
|
}
|
|
965
|
-
|
|
1164
|
+
} else {
|
|
1165
|
+
setResponseContentTypeByFileName(res, fileName);
|
|
966
1166
|
}
|
|
967
|
-
|
|
1167
|
+
}
|
|
1168
|
+
const contentOptions = {};
|
|
1169
|
+
if (stats.size) {
|
|
968
1170
|
const rangeHeader = res.req.headers[HeaderName.RANGE];
|
|
969
1171
|
if (rangeHeader) {
|
|
970
1172
|
const [x, y] = rangeHeader.replace('bytes=', '').split('-');
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
if (
|
|
974
|
-
|
|
1173
|
+
contentOptions.end = Math.min(parseInt(y, 10) || stats.size - 1, stats.size - 1);
|
|
1174
|
+
contentOptions.start = parseInt(x, 10) || 0;
|
|
1175
|
+
if (contentOptions.end >= stats.size) {
|
|
1176
|
+
contentOptions.end = stats.size - 1;
|
|
975
1177
|
}
|
|
976
|
-
if (
|
|
1178
|
+
if (contentOptions.start >= stats.size) {
|
|
977
1179
|
res.setHeader(HeaderName.CONTENT_RANGE, `bytes */${stats.size}`);
|
|
978
1180
|
res.statusCode = 416;
|
|
979
1181
|
res.end();
|
|
980
|
-
return;
|
|
1182
|
+
return Promise.resolve();
|
|
981
1183
|
}
|
|
982
|
-
res.setHeader(HeaderName.CONTENT_RANGE, `bytes ${
|
|
983
|
-
res.setHeader(HeaderName.CONTENT_LENGTH,
|
|
1184
|
+
res.setHeader(HeaderName.CONTENT_RANGE, `bytes ${contentOptions.start}-${contentOptions.end}/${stats.size}`);
|
|
1185
|
+
res.setHeader(HeaderName.CONTENT_LENGTH, contentOptions.end - contentOptions.start + 1);
|
|
984
1186
|
} else {
|
|
985
1187
|
res.setHeader(HeaderName.CONTENT_LENGTH, stats.size);
|
|
986
1188
|
}
|
|
987
1189
|
res.setHeader(HeaderName.ACCEPT_RANGES, 'bytes');
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1190
|
+
if (stats.mtime) {
|
|
1191
|
+
const mtime = new Date(stats.mtime);
|
|
1192
|
+
res.setHeader(HeaderName.LAST_MODIFIED, mtime.toUTCString());
|
|
1193
|
+
res.setHeader(HeaderName.ETag, `W/"${stats.size}-${mtime.getTime()}"`);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
try {
|
|
1197
|
+
const content = await options.content(contentOptions);
|
|
1198
|
+
if (isStream(content)) {
|
|
1199
|
+
return await sendStream(res, content, next);
|
|
1200
|
+
}
|
|
1201
|
+
return await send(res, content);
|
|
1202
|
+
} catch (e) {
|
|
1203
|
+
if (next) {
|
|
1204
|
+
return next(e);
|
|
1205
|
+
}
|
|
1206
|
+
if (isResponseGone(res)) {
|
|
1207
|
+
return Promise.resolve();
|
|
1208
|
+
}
|
|
1209
|
+
return Promise.reject(e);
|
|
1210
|
+
}
|
|
992
1211
|
}
|
|
993
1212
|
|
|
994
1213
|
function sendFormat(res, input) {
|
|
@@ -1010,19 +1229,388 @@ function sendRedirect(res, location, statusCode = 302) {
|
|
|
1010
1229
|
return send(res, html);
|
|
1011
1230
|
}
|
|
1012
1231
|
|
|
1013
|
-
function
|
|
1014
|
-
if (
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1232
|
+
function sendWebResponse(res, webResponse) {
|
|
1233
|
+
if (webResponse.redirected) {
|
|
1234
|
+
res.setHeader(HeaderName.LOCATION, webResponse.url);
|
|
1235
|
+
}
|
|
1236
|
+
if (webResponse.status) {
|
|
1237
|
+
res.statusCode = webResponse.status;
|
|
1238
|
+
}
|
|
1239
|
+
if (webResponse.statusText) {
|
|
1240
|
+
res.statusMessage = webResponse.statusText;
|
|
1241
|
+
}
|
|
1242
|
+
webResponse.headers.forEach((value, key)=>{
|
|
1243
|
+
if (key === HeaderName.SET_COOKIE) {
|
|
1244
|
+
res.appendHeader(key, splitCookiesString(value));
|
|
1245
|
+
} else {
|
|
1246
|
+
res.setHeader(key, value);
|
|
1247
|
+
}
|
|
1248
|
+
});
|
|
1249
|
+
if (webResponse.body) {
|
|
1250
|
+
return sendStream(res, webResponse.body);
|
|
1251
|
+
}
|
|
1252
|
+
res.end();
|
|
1253
|
+
return Promise.resolve();
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
function sendWebBlob(res, blob) {
|
|
1257
|
+
setResponseHeaderContentType(res, blob.type);
|
|
1258
|
+
return sendStream(res, blob.stream());
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
function createResponse(request) {
|
|
1262
|
+
let output;
|
|
1263
|
+
let encoding;
|
|
1264
|
+
const write = (chunk, chunkEncoding, callback)=>{
|
|
1265
|
+
if (typeof chunk !== 'undefined') {
|
|
1266
|
+
const chunkEncoded = typeof chunk === 'string' ? Buffer.from(chunk, chunkEncoding || encoding || 'utf8') : chunk;
|
|
1267
|
+
if (typeof output !== 'undefined') {
|
|
1268
|
+
output = Buffer.concat([
|
|
1269
|
+
output,
|
|
1270
|
+
chunkEncoded
|
|
1271
|
+
]);
|
|
1272
|
+
} else {
|
|
1273
|
+
output = chunkEncoded;
|
|
1018
1274
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1275
|
+
}
|
|
1276
|
+
encoding = chunkEncoding;
|
|
1277
|
+
if (callback) {
|
|
1278
|
+
callback();
|
|
1279
|
+
}
|
|
1280
|
+
};
|
|
1281
|
+
const writable = new Writable({
|
|
1282
|
+
decodeStrings: false,
|
|
1283
|
+
write (chunk, arg2, arg3) {
|
|
1284
|
+
const chunkEncoding = typeof arg2 === 'string' ? encoding : 'utf-8';
|
|
1285
|
+
let cb;
|
|
1286
|
+
if (typeof arg2 === 'function') {
|
|
1287
|
+
cb = arg2;
|
|
1288
|
+
} else if (typeof arg3 === 'function') {
|
|
1289
|
+
cb = arg3;
|
|
1290
|
+
}
|
|
1291
|
+
write(chunk, chunkEncoding, cb);
|
|
1292
|
+
return true;
|
|
1293
|
+
}
|
|
1294
|
+
});
|
|
1295
|
+
Object.defineProperty(writable, 'body', {
|
|
1296
|
+
get () {
|
|
1297
|
+
if (output) {
|
|
1298
|
+
const arrayBuffer = new ArrayBuffer(output.length);
|
|
1299
|
+
const view = new Uint8Array(arrayBuffer);
|
|
1300
|
+
for(let i = 0; i < output.length; ++i){
|
|
1301
|
+
view[i] = output[i];
|
|
1302
|
+
}
|
|
1303
|
+
return arrayBuffer;
|
|
1304
|
+
}
|
|
1305
|
+
return new ArrayBuffer(0);
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
const headers = {};
|
|
1309
|
+
Object.assign(writable, {
|
|
1310
|
+
req: request,
|
|
1311
|
+
chunkedEncoding: false,
|
|
1312
|
+
connection: null,
|
|
1313
|
+
headersSent: false,
|
|
1314
|
+
sendDate: false,
|
|
1315
|
+
shouldKeepAlive: false,
|
|
1316
|
+
socket: null,
|
|
1317
|
+
statusCode: 200,
|
|
1318
|
+
statusMessage: '',
|
|
1319
|
+
strictContentLength: false,
|
|
1320
|
+
useChunkedEncodingByDefault: false,
|
|
1321
|
+
finished: false,
|
|
1322
|
+
addTrailers (_headers) {},
|
|
1323
|
+
appendHeader (name, value) {
|
|
1324
|
+
if (name === HeaderName.SET_COOKIE) {
|
|
1325
|
+
value = splitCookiesString(value);
|
|
1326
|
+
}
|
|
1327
|
+
name = name.toLowerCase();
|
|
1328
|
+
const current = headers[name];
|
|
1329
|
+
const all = [
|
|
1330
|
+
...Array.isArray(current) ? current : [
|
|
1331
|
+
current
|
|
1332
|
+
],
|
|
1333
|
+
...Array.isArray(value) ? value : [
|
|
1334
|
+
value
|
|
1335
|
+
]
|
|
1336
|
+
].filter(Boolean);
|
|
1337
|
+
headers[name] = all.length > 1 ? all : all[0];
|
|
1338
|
+
return this;
|
|
1339
|
+
},
|
|
1340
|
+
assignSocket (_socket) {},
|
|
1341
|
+
detachSocket (_socket) {},
|
|
1342
|
+
flushHeaders () {},
|
|
1343
|
+
getHeader (name) {
|
|
1344
|
+
return headers[name.toLowerCase()];
|
|
1345
|
+
},
|
|
1346
|
+
getHeaderNames () {
|
|
1347
|
+
return Object.keys(headers);
|
|
1348
|
+
},
|
|
1349
|
+
getHeaders () {
|
|
1350
|
+
return headers;
|
|
1351
|
+
},
|
|
1352
|
+
hasHeader (name) {
|
|
1353
|
+
return hasOwnProperty(headers, name.toLowerCase());
|
|
1354
|
+
},
|
|
1355
|
+
removeHeader (name) {
|
|
1356
|
+
delete headers[name.toLowerCase()];
|
|
1357
|
+
},
|
|
1358
|
+
setHeader (name, value) {
|
|
1359
|
+
if (name === HeaderName.SET_COOKIE && typeof value !== 'number') {
|
|
1360
|
+
value = splitCookiesString(value);
|
|
1361
|
+
}
|
|
1362
|
+
headers[name.toLowerCase()] = value;
|
|
1363
|
+
return this;
|
|
1364
|
+
},
|
|
1365
|
+
setTimeout (_msecs, _callback) {
|
|
1366
|
+
return this;
|
|
1367
|
+
},
|
|
1368
|
+
writeContinue (_callback) {},
|
|
1369
|
+
writeEarlyHints (_hints, callback) {
|
|
1370
|
+
if (typeof callback !== 'undefined') {
|
|
1371
|
+
callback();
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
writeProcessing () {},
|
|
1375
|
+
writeHead (statusCode, arg1, arg2) {
|
|
1376
|
+
this.statusCode = statusCode;
|
|
1377
|
+
if (typeof arg1 === 'string') {
|
|
1378
|
+
this.statusMessage = arg1;
|
|
1379
|
+
arg1 = undefined;
|
|
1380
|
+
}
|
|
1381
|
+
const headers = arg2 || arg1;
|
|
1382
|
+
if (headers) {
|
|
1383
|
+
if (Array.isArray(headers)) {
|
|
1384
|
+
for(let i = 0; i < headers.length; i++){
|
|
1385
|
+
const keys = Object.keys(headers[i]);
|
|
1386
|
+
for(let j = 0; j < keys.length; j++){
|
|
1387
|
+
this.setHeader(keys[i], headers[i][keys[j]]);
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
} else {
|
|
1391
|
+
const keys = Object.keys(headers);
|
|
1392
|
+
for(let i = 0; i < keys.length; i++){
|
|
1393
|
+
this.setHeader(keys[i], headers[keys[i]]);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
1398
|
+
// @ts-ignore
|
|
1399
|
+
this.headersSent = true;
|
|
1400
|
+
return this;
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
return writable;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
function buildDispatcherMeta(input) {
|
|
1407
|
+
return {
|
|
1408
|
+
mountPath: input.mountPath || '/',
|
|
1409
|
+
params: input.params || {},
|
|
1410
|
+
path: input.path || '/',
|
|
1411
|
+
routerPath: []
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
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 {};
|
|
1428
|
+
}
|
|
1429
|
+
const keys = Object.keys(input);
|
|
1430
|
+
if (keys.length === 0) {
|
|
1431
|
+
return {};
|
|
1432
|
+
}
|
|
1433
|
+
const output = {};
|
|
1434
|
+
for(let i = 0; i < keys.length; i++){
|
|
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;
|
|
1022
1449
|
}
|
|
1023
|
-
|
|
1024
|
-
|
|
1450
|
+
for(let i = 0; i < keys.length; i++){
|
|
1451
|
+
t1[keys[i]] = t2[keys[i]];
|
|
1025
1452
|
}
|
|
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;
|
|
1466
|
+
}
|
|
1467
|
+
if (!isResponseGone(res)) {
|
|
1468
|
+
res.statusCode = 404;
|
|
1469
|
+
res.end();
|
|
1470
|
+
}
|
|
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();
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
function createNodeDispatcher(router) {
|
|
1486
|
+
return (req, res)=>{
|
|
1487
|
+
// eslint-disable-next-line no-void
|
|
1488
|
+
void dispatchNodeRequest(router, req, res);
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
async function dispatchRawRequest(router, request, options = {}) {
|
|
1493
|
+
const req = createRequest({
|
|
1494
|
+
url: request.path,
|
|
1495
|
+
method: request.method,
|
|
1496
|
+
body: request.body,
|
|
1497
|
+
headers: request.headers
|
|
1498
|
+
});
|
|
1499
|
+
const res = createResponse(req);
|
|
1500
|
+
const getHeaders = ()=>{
|
|
1501
|
+
const output = {};
|
|
1502
|
+
const headers = res.getHeaders();
|
|
1503
|
+
const keys = Object.keys(headers);
|
|
1504
|
+
for(let i = 0; i < keys.length; i++){
|
|
1505
|
+
const header = headers[keys[i]];
|
|
1506
|
+
if (typeof header === 'number') {
|
|
1507
|
+
output[keys[i]] = `${header}`;
|
|
1508
|
+
} else if (header) {
|
|
1509
|
+
output[keys[i]] = header;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return output;
|
|
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
|
+
});
|
|
1520
|
+
try {
|
|
1521
|
+
const dispatched = await router.dispatch({
|
|
1522
|
+
req,
|
|
1523
|
+
res
|
|
1524
|
+
}, buildDispatcherMeta({
|
|
1525
|
+
path: useRequestPath(req)
|
|
1526
|
+
}));
|
|
1527
|
+
if (dispatched) {
|
|
1528
|
+
return createRawResponse();
|
|
1529
|
+
}
|
|
1530
|
+
return createRawResponse({
|
|
1531
|
+
status: 404
|
|
1532
|
+
});
|
|
1533
|
+
} catch (e) {
|
|
1534
|
+
if (options.throwOnError) {
|
|
1535
|
+
throw e;
|
|
1536
|
+
}
|
|
1537
|
+
if (isError(e)) {
|
|
1538
|
+
return createRawResponse({
|
|
1539
|
+
status: e.statusCode,
|
|
1540
|
+
statusMessage: e.statusMessage
|
|
1541
|
+
});
|
|
1542
|
+
}
|
|
1543
|
+
return createRawResponse({
|
|
1544
|
+
status: 500
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
function createRawDispatcher(router) {
|
|
1549
|
+
return async (request)=>dispatchRawRequest(router, request);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
async function dispatchWebRequest(router, request, options = {}) {
|
|
1553
|
+
const url = new URL(request.url);
|
|
1554
|
+
const headers = {};
|
|
1555
|
+
request.headers.forEach((value, key)=>{
|
|
1556
|
+
headers[key] = value;
|
|
1557
|
+
});
|
|
1558
|
+
const res = await dispatchRawRequest(router, {
|
|
1559
|
+
method: request.method,
|
|
1560
|
+
path: url.pathname + url.search,
|
|
1561
|
+
headers,
|
|
1562
|
+
body: request.body
|
|
1563
|
+
}, options);
|
|
1564
|
+
let body;
|
|
1565
|
+
if (request.method === MethodName.HEAD || res.status === 304 || res.status === 101 || res.status === 204 || res.status === 205) {
|
|
1566
|
+
body = null;
|
|
1567
|
+
} else {
|
|
1568
|
+
body = res.body;
|
|
1569
|
+
}
|
|
1570
|
+
return new Response(body, {
|
|
1571
|
+
headers: transformHeadersToTuples(res.headers),
|
|
1572
|
+
status: res.status,
|
|
1573
|
+
statusText: res.statusMessage
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
function createWebDispatcher(router) {
|
|
1577
|
+
return async (request)=>dispatchWebRequest(router, request);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
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
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
return {
|
|
1594
|
+
type: HandlerType.CORE,
|
|
1595
|
+
...input
|
|
1596
|
+
};
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
function errorHandler(input) {
|
|
1600
|
+
if (typeof input === 'function') {
|
|
1601
|
+
return {
|
|
1602
|
+
type: HandlerType.ERROR,
|
|
1603
|
+
fn: input
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
return {
|
|
1607
|
+
type: HandlerType.ERROR,
|
|
1608
|
+
...input
|
|
1609
|
+
};
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
function isHandler(input) {
|
|
1613
|
+
return isObject(input) && typeof input.fn === 'function' && typeof input.type === 'string';
|
|
1026
1614
|
}
|
|
1027
1615
|
|
|
1028
1616
|
function decodeParam(val) {
|
|
@@ -1033,268 +1621,219 @@ function decodeParam(val) {
|
|
|
1033
1621
|
}
|
|
1034
1622
|
class PathMatcher {
|
|
1035
1623
|
test(path) {
|
|
1036
|
-
const fastSlash = this.path === '/' && this.regexpOptions.end === false;
|
|
1037
|
-
if (fastSlash) {
|
|
1038
|
-
return true;
|
|
1039
|
-
}
|
|
1040
1624
|
return this.regexp.test(path);
|
|
1041
1625
|
}
|
|
1042
1626
|
exec(path) {
|
|
1043
|
-
|
|
1044
|
-
const fastSlash = this.path === '/' && this.regexpOptions.end === false;
|
|
1045
|
-
if (fastSlash) {
|
|
1627
|
+
if (this.path === '/' && this.regexpOptions.end === false) {
|
|
1046
1628
|
return {
|
|
1047
1629
|
path: '/',
|
|
1048
1630
|
params: {}
|
|
1049
1631
|
};
|
|
1050
1632
|
}
|
|
1051
|
-
|
|
1052
|
-
if (!match) {
|
|
1053
|
-
return undefined;
|
|
1054
|
-
}
|
|
1055
|
-
if (this.path instanceof RegExp) {
|
|
1633
|
+
if (this.path === '*') {
|
|
1056
1634
|
return {
|
|
1057
1635
|
path,
|
|
1058
1636
|
params: {
|
|
1059
|
-
0: decodeParam(
|
|
1637
|
+
0: decodeParam(path)
|
|
1060
1638
|
}
|
|
1061
1639
|
};
|
|
1062
1640
|
}
|
|
1063
|
-
const
|
|
1641
|
+
const match = this.regexp.exec(path);
|
|
1642
|
+
if (!match) {
|
|
1643
|
+
return undefined;
|
|
1644
|
+
}
|
|
1645
|
+
const params = {};
|
|
1064
1646
|
for(let i = 1; i < match.length; i++){
|
|
1065
1647
|
const key = this.regexpKeys[i - 1];
|
|
1066
1648
|
const prop = key.name;
|
|
1067
1649
|
const val = decodeParam(match[i]);
|
|
1068
1650
|
if (typeof val !== 'undefined') {
|
|
1069
|
-
|
|
1651
|
+
params[prop] = val;
|
|
1070
1652
|
}
|
|
1071
1653
|
}
|
|
1072
1654
|
return {
|
|
1073
1655
|
path: match[0],
|
|
1074
|
-
params
|
|
1656
|
+
params
|
|
1075
1657
|
};
|
|
1076
1658
|
}
|
|
1077
1659
|
constructor(path, options){
|
|
1078
1660
|
this.regexpKeys = [];
|
|
1079
1661
|
this.path = path;
|
|
1080
1662
|
this.regexpOptions = options || {};
|
|
1081
|
-
|
|
1082
|
-
this.regexp = path;
|
|
1083
|
-
} else {
|
|
1084
|
-
this.regexp = pathToRegexp(path, this.regexpKeys, options);
|
|
1085
|
-
}
|
|
1663
|
+
this.regexp = pathToRegexp(path, this.regexpKeys, options);
|
|
1086
1664
|
}
|
|
1087
1665
|
}
|
|
1088
1666
|
|
|
1667
|
+
function isPath(input) {
|
|
1668
|
+
return typeof input === 'string' || input instanceof RegExp;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
const LayerSymbol = Symbol.for('Layer');
|
|
1672
|
+
|
|
1089
1673
|
class Layer {
|
|
1090
1674
|
// --------------------------------------------------
|
|
1091
|
-
|
|
1092
|
-
return this.
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
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;
|
|
1683
|
+
}
|
|
1684
|
+
// --------------------------------------------------
|
|
1685
|
+
dispatch(event, meta) {
|
|
1686
|
+
if (this.pathMatcher) {
|
|
1687
|
+
const pathMatch = this.pathMatcher.exec(meta.path);
|
|
1688
|
+
if (pathMatch) {
|
|
1689
|
+
meta.params = mergeDispatcherMetaParams(meta.params, pathMatch.params);
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
setRequestParams(event.req, meta.params);
|
|
1693
|
+
setRequestMountPath(event.req, meta.mountPath);
|
|
1694
|
+
setRequestRouterPath(event.req, meta.routerPath);
|
|
1695
|
+
return new Promise((resolve, reject)=>{
|
|
1696
|
+
let handled = false;
|
|
1697
|
+
const unsubscribe = ()=>{
|
|
1698
|
+
event.res.off('close', onFinished);
|
|
1699
|
+
event.res.off('error', onFinished);
|
|
1700
|
+
};
|
|
1701
|
+
const shutdown = (dispatched, err)=>{
|
|
1702
|
+
if (handled) {
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
handled = true;
|
|
1706
|
+
unsubscribe();
|
|
1707
|
+
if (err) {
|
|
1708
|
+
reject(createError(err));
|
|
1709
|
+
} else {
|
|
1710
|
+
resolve(dispatched);
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
const onFinished = (err)=>shutdown(true, err);
|
|
1714
|
+
const onNext = (err)=>shutdown(false, err);
|
|
1715
|
+
event.res.once('close', onFinished);
|
|
1716
|
+
event.res.once('error', onFinished);
|
|
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
|
+
};
|
|
1725
|
+
try {
|
|
1726
|
+
let output;
|
|
1727
|
+
if (this.handler.type === HandlerType.ERROR) {
|
|
1728
|
+
if (meta.error) {
|
|
1729
|
+
output = this.handler.fn(meta.error, event.req, event.res, onNext);
|
|
1108
1730
|
}
|
|
1731
|
+
} else {
|
|
1732
|
+
output = this.handler.fn(event.req, event.res, onNext);
|
|
1109
1733
|
}
|
|
1110
|
-
|
|
1734
|
+
if (isPromise(output)) {
|
|
1735
|
+
output.then((r)=>handle(r)).catch((e)=>reject(createError(e)));
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
Promise.resolve().then(()=>handle(output)).catch((e)=>reject(createError(e)));
|
|
1739
|
+
} catch (error) {
|
|
1740
|
+
onNext(error);
|
|
1111
1741
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
sendOutput(res, input) {
|
|
1745
|
+
if (input instanceof Error) {
|
|
1746
|
+
return Promise.reject(createError(input));
|
|
1114
1747
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
return;
|
|
1748
|
+
if (isStream(input)) {
|
|
1749
|
+
return sendStream(res, input);
|
|
1118
1750
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
next(e);
|
|
1125
|
-
} else {
|
|
1126
|
-
next(new BadRequestError({
|
|
1127
|
-
message: 'The request could not be processed by the handler.'
|
|
1128
|
-
}));
|
|
1129
|
-
}
|
|
1751
|
+
if (isWebBlob(input)) {
|
|
1752
|
+
return sendWebBlob(res, input);
|
|
1753
|
+
}
|
|
1754
|
+
if (isWebResponse(input)) {
|
|
1755
|
+
return sendWebResponse(res, input);
|
|
1130
1756
|
}
|
|
1757
|
+
return send(res, input);
|
|
1131
1758
|
}
|
|
1132
1759
|
// --------------------------------------------------
|
|
1133
1760
|
matchPath(path) {
|
|
1761
|
+
if (!this.pathMatcher) {
|
|
1762
|
+
return true;
|
|
1763
|
+
}
|
|
1134
1764
|
return this.pathMatcher.test(path);
|
|
1135
1765
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
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;
|
|
1138
1775
|
}
|
|
1139
1776
|
// --------------------------------------------------
|
|
1140
|
-
constructor(
|
|
1141
|
-
this['@instanceof'] =
|
|
1142
|
-
this.
|
|
1143
|
-
|
|
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
|
+
}
|
|
1144
1785
|
}
|
|
1145
1786
|
}
|
|
1146
1787
|
|
|
1147
1788
|
function isLayerInstance(input) {
|
|
1148
|
-
return isInstance(input,
|
|
1789
|
+
return isInstance(input, LayerSymbol);
|
|
1149
1790
|
}
|
|
1150
1791
|
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
return this.pathMatcher.test(path);
|
|
1155
|
-
}
|
|
1156
|
-
matchMethod(method) {
|
|
1157
|
-
let name = method.toLowerCase();
|
|
1158
|
-
if (name === MethodName.HEAD && !hasOwnProperty(this.layers, name)) {
|
|
1159
|
-
name = MethodName.GET;
|
|
1160
|
-
}
|
|
1161
|
-
return Object.prototype.hasOwnProperty.call(this.layers, name);
|
|
1162
|
-
}
|
|
1163
|
-
// --------------------------------------------------
|
|
1164
|
-
getMethods() {
|
|
1165
|
-
const keys = Object.keys(this.layers);
|
|
1166
|
-
if (hasOwnProperty(this.layers, MethodName.GET) && !hasOwnProperty(this.layers, MethodName.HEAD)) {
|
|
1167
|
-
keys.push(MethodName.HEAD);
|
|
1168
|
-
}
|
|
1169
|
-
return keys;
|
|
1170
|
-
}
|
|
1171
|
-
// --------------------------------------------------
|
|
1172
|
-
dispatch(req, res, meta, done) {
|
|
1173
|
-
/* istanbul ignore next */ if (!req.method) {
|
|
1174
|
-
done();
|
|
1175
|
-
return;
|
|
1176
|
-
}
|
|
1177
|
-
let name = req.method.toLowerCase();
|
|
1178
|
-
if (name === MethodName.HEAD && !hasOwnProperty(this.layers, name)) {
|
|
1179
|
-
name = MethodName.GET;
|
|
1180
|
-
}
|
|
1181
|
-
const layers = this.layers[name];
|
|
1182
|
-
/* istanbul ignore next */ if (typeof layers === 'undefined' || layers.length === 0 || typeof meta.path === 'undefined') {
|
|
1183
|
-
done();
|
|
1184
|
-
return;
|
|
1185
|
-
}
|
|
1186
|
-
const layerMeta = {
|
|
1187
|
-
...meta
|
|
1188
|
-
};
|
|
1189
|
-
const output = this.pathMatcher.exec(meta.path);
|
|
1190
|
-
if (output) {
|
|
1191
|
-
layerMeta.params = merge({}, meta.params || {}, output.params);
|
|
1192
|
-
}
|
|
1193
|
-
let index = -1;
|
|
1194
|
-
const next = (err)=>{
|
|
1195
|
-
index++;
|
|
1196
|
-
if (index >= layers.length) {
|
|
1197
|
-
setImmediate(done, err);
|
|
1198
|
-
return;
|
|
1199
|
-
}
|
|
1200
|
-
const layer = layers[index];
|
|
1201
|
-
if (err && !layer.isError()) {
|
|
1202
|
-
next(err);
|
|
1203
|
-
return;
|
|
1204
|
-
}
|
|
1205
|
-
layer.dispatch(req, res, {
|
|
1206
|
-
...layerMeta
|
|
1207
|
-
}, next);
|
|
1208
|
-
};
|
|
1209
|
-
next();
|
|
1210
|
-
}
|
|
1211
|
-
// --------------------------------------------------
|
|
1212
|
-
register(method, ...handlers) {
|
|
1213
|
-
this.layers[method] = [];
|
|
1214
|
-
for(let i = 0; i < handlers.length; i++){
|
|
1215
|
-
const layer = new Layer({
|
|
1216
|
-
path: this.path,
|
|
1217
|
-
pathMatcher: this.pathMatcherOptions
|
|
1218
|
-
}, handlers[i]);
|
|
1219
|
-
this.layers[method].push(layer);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
get(...handlers) {
|
|
1223
|
-
return this.register(MethodName.GET, ...handlers);
|
|
1224
|
-
}
|
|
1225
|
-
post(...handlers) {
|
|
1226
|
-
return this.register(MethodName.POST, ...handlers);
|
|
1227
|
-
}
|
|
1228
|
-
put(...handlers) {
|
|
1229
|
-
return this.register(MethodName.PUT, ...handlers);
|
|
1230
|
-
}
|
|
1231
|
-
patch(...handlers) {
|
|
1232
|
-
return this.register(MethodName.PATCH, ...handlers);
|
|
1233
|
-
}
|
|
1234
|
-
delete(...handlers) {
|
|
1235
|
-
return this.register(MethodName.DELETE, ...handlers);
|
|
1236
|
-
}
|
|
1237
|
-
head(...handlers) {
|
|
1238
|
-
return this.register(MethodName.HEAD, ...handlers);
|
|
1792
|
+
function isPluginInstallContext(input) {
|
|
1793
|
+
if (!isObject(input) || !isObject(input.options)) {
|
|
1794
|
+
return false;
|
|
1239
1795
|
}
|
|
1240
|
-
|
|
1241
|
-
return
|
|
1796
|
+
if (typeof input.name !== 'undefined' && typeof input.name !== 'string') {
|
|
1797
|
+
return false;
|
|
1242
1798
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1799
|
+
return typeof input.path === 'undefined' || isPath(input.path);
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
function transformRouterOptions(input) {
|
|
1803
|
+
if (typeof input.etag !== 'undefined') {
|
|
1804
|
+
input.etag = buildEtagFn(input.etag);
|
|
1246
1805
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
this['@instanceof'] = Symbol.for('Route');
|
|
1250
|
-
this.layers = {};
|
|
1251
|
-
this.path = options.path;
|
|
1252
|
-
this.pathMatcherOptions = {
|
|
1253
|
-
end: true,
|
|
1254
|
-
strict: this.isStrictPath(),
|
|
1255
|
-
...options.pathMatcher
|
|
1256
|
-
};
|
|
1257
|
-
this.pathMatcher = new PathMatcher(this.path, this.pathMatcherOptions);
|
|
1806
|
+
if (typeof input.trustProxy !== 'undefined') {
|
|
1807
|
+
input.trustProxy = buildTrustProxyFn(input.trustProxy);
|
|
1258
1808
|
}
|
|
1809
|
+
return input;
|
|
1259
1810
|
}
|
|
1260
1811
|
|
|
1261
|
-
|
|
1262
|
-
return isInstance(input, 'Route');
|
|
1263
|
-
}
|
|
1812
|
+
const RouterSymbol = Symbol.for('Router');
|
|
1264
1813
|
|
|
1814
|
+
let nextId = 0;
|
|
1815
|
+
function generateRouterID() {
|
|
1816
|
+
return ++nextId;
|
|
1817
|
+
}
|
|
1265
1818
|
function isRouterInstance(input) {
|
|
1266
|
-
return isInstance(input,
|
|
1819
|
+
return isInstance(input, RouterSymbol);
|
|
1267
1820
|
}
|
|
1821
|
+
|
|
1268
1822
|
class Router {
|
|
1269
1823
|
// --------------------------------------------------
|
|
1270
|
-
setPathMatcherOptions(input) {
|
|
1271
|
-
this.pathMatcherOptions = input;
|
|
1272
|
-
if (this.pathMatcher) {
|
|
1273
|
-
this.pathMatcher.regexpOptions = this.pathMatcherOptions;
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
1824
|
setPath(value) {
|
|
1277
1825
|
if (value === '/' || !isPath(value)) {
|
|
1278
|
-
this.path = '/';
|
|
1279
1826
|
return;
|
|
1280
1827
|
}
|
|
1828
|
+
let path;
|
|
1281
1829
|
if (typeof value === 'string') {
|
|
1282
|
-
|
|
1830
|
+
path = withLeadingSlash(withoutTrailingSlash(`${value}`));
|
|
1283
1831
|
} else {
|
|
1284
|
-
|
|
1832
|
+
path = value;
|
|
1285
1833
|
}
|
|
1286
|
-
this.pathMatcher = new PathMatcher(
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
createListener() {
|
|
1290
|
-
this.isRoot = true;
|
|
1291
|
-
return (req, res)=>{
|
|
1292
|
-
this.dispatch(req, res);
|
|
1293
|
-
};
|
|
1294
|
-
}
|
|
1295
|
-
/* istanbul ignore next */ listen(port) {
|
|
1296
|
-
const server = createServer(this.createListener());
|
|
1297
|
-
return server.listen(port);
|
|
1834
|
+
this.pathMatcher = new PathMatcher(path, {
|
|
1835
|
+
end: false
|
|
1836
|
+
});
|
|
1298
1837
|
}
|
|
1299
1838
|
// --------------------------------------------------
|
|
1300
1839
|
matchPath(path) {
|
|
@@ -1304,219 +1843,255 @@ class Router {
|
|
|
1304
1843
|
return true;
|
|
1305
1844
|
}
|
|
1306
1845
|
// --------------------------------------------------
|
|
1307
|
-
dispatch(
|
|
1308
|
-
|
|
1309
|
-
meta = meta || {};
|
|
1310
|
-
let allowedMethods = [];
|
|
1311
|
-
if (this.isRoot && typeof this.timeout === 'number') {
|
|
1312
|
-
createRequestTimeout(res, this.timeout, done);
|
|
1313
|
-
}
|
|
1314
|
-
const fn = (err)=>{
|
|
1315
|
-
/* istanbul ignore if */ if (!this.isRoot) {
|
|
1316
|
-
if (typeof done !== 'undefined') {
|
|
1317
|
-
setImmediate(()=>done(err));
|
|
1318
|
-
}
|
|
1319
|
-
return;
|
|
1320
|
-
}
|
|
1321
|
-
if (typeof err !== 'undefined') {
|
|
1322
|
-
res.statusCode = 400;
|
|
1323
|
-
res.end();
|
|
1324
|
-
return;
|
|
1325
|
-
}
|
|
1326
|
-
if (req.method && req.method.toLowerCase() === MethodName.OPTIONS) {
|
|
1327
|
-
const options = allowedMethods.map((key)=>key.toUpperCase()).join(',');
|
|
1328
|
-
res.setHeader(HeaderName.ALLOW, options);
|
|
1329
|
-
send(res, options);
|
|
1330
|
-
return;
|
|
1331
|
-
}
|
|
1332
|
-
res.statusCode = 404;
|
|
1333
|
-
res.end();
|
|
1334
|
-
};
|
|
1335
|
-
let path = meta.path || useRequestPath(req);
|
|
1846
|
+
async dispatch(event, meta) {
|
|
1847
|
+
const allowedMethods = [];
|
|
1336
1848
|
if (this.pathMatcher) {
|
|
1337
|
-
const output = this.pathMatcher.exec(path);
|
|
1849
|
+
const output = this.pathMatcher.exec(meta.path);
|
|
1338
1850
|
if (typeof output !== 'undefined') {
|
|
1339
|
-
meta.mountPath = cleanDoubleSlashes(`${meta.mountPath
|
|
1340
|
-
if (path === output.path) {
|
|
1341
|
-
path = '/';
|
|
1851
|
+
meta.mountPath = cleanDoubleSlashes(`${meta.mountPath}/${output.path}`);
|
|
1852
|
+
if (meta.path === output.path) {
|
|
1853
|
+
meta.path = '/';
|
|
1342
1854
|
} else {
|
|
1343
|
-
path = withLeadingSlash(path.substring(output.path.length));
|
|
1855
|
+
meta.path = withLeadingSlash(meta.path.substring(output.path.length));
|
|
1344
1856
|
}
|
|
1345
|
-
meta.params =
|
|
1857
|
+
meta.params = {
|
|
1858
|
+
...meta.params,
|
|
1859
|
+
...output.params
|
|
1860
|
+
};
|
|
1346
1861
|
}
|
|
1347
1862
|
}
|
|
1348
|
-
meta.
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
let match = false;
|
|
1359
|
-
while(!match && index < this.stack.length){
|
|
1360
|
-
index++;
|
|
1361
|
-
layer = this.stack[index];
|
|
1362
|
-
if (isLayerInstance(layer)) {
|
|
1363
|
-
if (!layer.isError() && err) {
|
|
1364
|
-
continue;
|
|
1365
|
-
}
|
|
1366
|
-
match = layer.matchPath(path);
|
|
1863
|
+
meta.routerPath.push(this.id);
|
|
1864
|
+
let err;
|
|
1865
|
+
let item;
|
|
1866
|
+
let itemMeta;
|
|
1867
|
+
let match = false;
|
|
1868
|
+
for(let i = 0; i < this.stack.length; i++){
|
|
1869
|
+
item = this.stack[i];
|
|
1870
|
+
if (isLayerInstance(item)) {
|
|
1871
|
+
if (item.type !== HandlerType.ERROR && err) {
|
|
1872
|
+
continue;
|
|
1367
1873
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
if (isRouteInstance(layer)) {
|
|
1372
|
-
match = layer.matchPath(path);
|
|
1373
|
-
if (req.method && !layer.matchMethod(req.method)) {
|
|
1874
|
+
match = item.matchPath(meta.path);
|
|
1875
|
+
if (match && event.req.method) {
|
|
1876
|
+
if (!item.matchMethod(event.req.method)) {
|
|
1374
1877
|
match = false;
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1878
|
+
}
|
|
1879
|
+
if (item.method) {
|
|
1880
|
+
allowedMethods.push(item.method);
|
|
1378
1881
|
}
|
|
1379
1882
|
}
|
|
1883
|
+
} else if (isRouterInstance(item)) {
|
|
1884
|
+
match = item.matchPath(meta.path);
|
|
1380
1885
|
}
|
|
1381
|
-
if (!match
|
|
1382
|
-
|
|
1383
|
-
return;
|
|
1384
|
-
}
|
|
1385
|
-
const layerMeta = {
|
|
1386
|
-
...meta
|
|
1387
|
-
};
|
|
1388
|
-
if (isLayerInstance(layer)) {
|
|
1389
|
-
const output = layer.exec(path);
|
|
1390
|
-
if (output) {
|
|
1391
|
-
layerMeta.params = merge(output.params, layerMeta.params || {});
|
|
1392
|
-
layerMeta.mountPath = cleanDoubleSlashes(`${layerMeta.mountPath || ''}/${output.path}`);
|
|
1393
|
-
}
|
|
1886
|
+
if (!match) {
|
|
1887
|
+
continue;
|
|
1394
1888
|
}
|
|
1889
|
+
itemMeta = cloneDispatcherMeta(meta);
|
|
1395
1890
|
if (err) {
|
|
1396
|
-
|
|
1397
|
-
layer.dispatch(req, res, layerMeta, next, err);
|
|
1398
|
-
return;
|
|
1399
|
-
}
|
|
1400
|
-
/* istanbul ignore next */ setImmediate(next, err);
|
|
1401
|
-
return;
|
|
1891
|
+
itemMeta.error = err;
|
|
1402
1892
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
/* istanbul ignore next */ dispatchAsync(req, res) {
|
|
1408
|
-
return new Promise((resolve, reject)=>{
|
|
1409
|
-
this.dispatch(req, res, {}, (err)=>{
|
|
1410
|
-
if (err) {
|
|
1411
|
-
reject(err);
|
|
1412
|
-
return;
|
|
1893
|
+
try {
|
|
1894
|
+
const dispatched = await item.dispatch(event, itemMeta);
|
|
1895
|
+
if (dispatched) {
|
|
1896
|
+
return true;
|
|
1413
1897
|
}
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
route(path) {
|
|
1420
|
-
if (typeof path === 'string' && path.length > 0) {
|
|
1421
|
-
path = withLeadingSlash(path);
|
|
1898
|
+
} catch (e) {
|
|
1899
|
+
if (isError(e)) {
|
|
1900
|
+
err = e;
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1422
1903
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
return this.stack[index];
|
|
1904
|
+
if (err) {
|
|
1905
|
+
throw err;
|
|
1426
1906
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
sensitive: this.pathMatcherOptions.sensitive
|
|
1907
|
+
if (event.req.method && event.req.method.toLowerCase() === MethodName.OPTIONS) {
|
|
1908
|
+
if (allowedMethods.indexOf(MethodName.GET) !== -1) {
|
|
1909
|
+
allowedMethods.push(MethodName.HEAD);
|
|
1431
1910
|
}
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1911
|
+
distinctArray(allowedMethods);
|
|
1912
|
+
const options = allowedMethods.map((key)=>key.toUpperCase()).join(',');
|
|
1913
|
+
if (!isResponseGone(event.res)) {
|
|
1914
|
+
event.res.setHeader(HeaderName.ALLOW, options);
|
|
1915
|
+
await send(event.res, options);
|
|
1916
|
+
}
|
|
1917
|
+
return true;
|
|
1918
|
+
}
|
|
1919
|
+
return false;
|
|
1435
1920
|
}
|
|
1436
|
-
delete(path,
|
|
1437
|
-
|
|
1438
|
-
|
|
1921
|
+
delete(path, handler) {
|
|
1922
|
+
if (isPath(path)) {
|
|
1923
|
+
this.use({
|
|
1924
|
+
...handler,
|
|
1925
|
+
method: MethodName.DELETE,
|
|
1926
|
+
path
|
|
1927
|
+
});
|
|
1928
|
+
return this;
|
|
1929
|
+
}
|
|
1930
|
+
this.use({
|
|
1931
|
+
...path,
|
|
1932
|
+
method: MethodName.DELETE
|
|
1933
|
+
});
|
|
1439
1934
|
return this;
|
|
1440
1935
|
}
|
|
1441
|
-
get(path,
|
|
1442
|
-
|
|
1443
|
-
|
|
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
|
+
});
|
|
1444
1949
|
return this;
|
|
1445
1950
|
}
|
|
1446
|
-
post(path,
|
|
1447
|
-
|
|
1448
|
-
|
|
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
|
+
});
|
|
1449
1964
|
return this;
|
|
1450
1965
|
}
|
|
1451
|
-
put(path,
|
|
1452
|
-
|
|
1453
|
-
|
|
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
|
+
});
|
|
1454
1979
|
return this;
|
|
1455
1980
|
}
|
|
1456
|
-
patch(path,
|
|
1457
|
-
|
|
1458
|
-
|
|
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
|
+
});
|
|
1459
1994
|
return this;
|
|
1460
1995
|
}
|
|
1461
|
-
head(path,
|
|
1462
|
-
|
|
1463
|
-
|
|
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
|
+
});
|
|
1464
2009
|
return this;
|
|
1465
2010
|
}
|
|
1466
|
-
options(path,
|
|
1467
|
-
|
|
1468
|
-
|
|
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
|
+
});
|
|
1469
2024
|
return this;
|
|
1470
2025
|
}
|
|
1471
2026
|
use(...input) {
|
|
1472
2027
|
/* istanbul ignore next */ if (input.length === 0) {
|
|
1473
2028
|
return this;
|
|
1474
2029
|
}
|
|
2030
|
+
const modifyPath = (input)=>{
|
|
2031
|
+
if (typeof input === 'string') {
|
|
2032
|
+
return withLeadingSlash(input);
|
|
2033
|
+
}
|
|
2034
|
+
return input;
|
|
2035
|
+
};
|
|
1475
2036
|
let path;
|
|
1476
|
-
if (isPath(input[0])) {
|
|
1477
|
-
path = input.shift();
|
|
1478
|
-
}
|
|
1479
2037
|
for(let i = 0; i < input.length; i++){
|
|
1480
2038
|
const item = input[i];
|
|
2039
|
+
if (isPath(item)) {
|
|
2040
|
+
path = modifyPath(item);
|
|
2041
|
+
continue;
|
|
2042
|
+
}
|
|
1481
2043
|
if (isRouterInstance(item)) {
|
|
1482
2044
|
if (path) {
|
|
1483
2045
|
item.setPath(path);
|
|
1484
2046
|
}
|
|
1485
|
-
item.setPathMatcherOptions(this.pathMatcherOptions);
|
|
1486
2047
|
this.stack.push(item);
|
|
1487
2048
|
continue;
|
|
1488
2049
|
}
|
|
1489
|
-
if (
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
pathMatcher: {
|
|
1493
|
-
strict: false,
|
|
1494
|
-
end: false,
|
|
1495
|
-
sensitive: this.pathMatcherOptions.sensitive
|
|
1496
|
-
}
|
|
1497
|
-
}, item));
|
|
2050
|
+
if (isHandler(item)) {
|
|
2051
|
+
item.path = path || modifyPath(item.path);
|
|
2052
|
+
this.stack.push(new Layer(item));
|
|
1498
2053
|
}
|
|
1499
2054
|
}
|
|
1500
2055
|
return this;
|
|
1501
2056
|
}
|
|
1502
2057
|
// --------------------------------------------------
|
|
1503
|
-
|
|
1504
|
-
|
|
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
|
+
// --------------------------------------------------
|
|
2082
|
+
constructor(options = {}){
|
|
2083
|
+
this['@instanceof'] = RouterSymbol;
|
|
1505
2084
|
/**
|
|
1506
2085
|
* Array of mounted layers, routes & routers.
|
|
1507
2086
|
*
|
|
1508
2087
|
* @protected
|
|
1509
2088
|
*/ this.stack = [];
|
|
1510
|
-
|
|
1511
|
-
this.
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
...ctx.pathMatcher || {}
|
|
1515
|
-
};
|
|
1516
|
-
this.timeout = ctx.timeout;
|
|
1517
|
-
this.setPath(ctx.path || '/');
|
|
2089
|
+
this.id = generateRouterID();
|
|
2090
|
+
this.name = options.name;
|
|
2091
|
+
this.setPath(options.path);
|
|
2092
|
+
setRouterOptions(this.id, transformRouterOptions(options));
|
|
1518
2093
|
}
|
|
1519
2094
|
}
|
|
1520
2095
|
|
|
1521
|
-
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 };
|
|
1522
2097
|
//# sourceMappingURL=index.mjs.map
|