@jnode/server 2.0.2 → 2.2.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 +24 -8
- package/package.json +1 -1
- package/src/handlers.js +50 -33
- package/src/routers.js +64 -18
- package/src/server.js +10 -6
package/README.md
CHANGED
|
@@ -83,6 +83,7 @@ Also, we provide some powerful built-in routers and handlers so you can start bu
|
|
|
83
83
|
- `options` [\<Object\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)
|
|
84
84
|
- `maxRoutingSteps` [\<number\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#number_type) The max steps for routing; when exceeded but still getting another router, it'll throw the client a **508** error. **Default:** `50`.
|
|
85
85
|
- `enableHTTP2` [\<boolean\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#boolean_type) Enable HTTP/2 support (with `node:http2` [Compatibility API](https://nodejs.org/docs/latest/api/http2.html#compatibility-api)). **Default:** `false`.
|
|
86
|
+
- `codeHandlers` [\<Object\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) Global status code handlers. Keys are HTTP status codes, values are [handlers-extended](#handler-extended).
|
|
86
87
|
- Options in [`http.createServer()`](https://nodejs.org/docs/latest/api/http.html#httpcreateserveroptions-requestlistener), [`https.createServer()`](https://nodejs.org/docs/latest/api/https.html#httpscreateserveroptions-requestlistener), [`http2.createServer()`](https://nodejs.org/docs/latest/api/http2.html#http2createserveroptions-onrequesthandler), or [`http2.createSecureServer()`](https://nodejs.org/docs/latest/api/http2.html#http2createsecureserveroptions-onrequesthandler).
|
|
87
88
|
- Returns: [\<server.Server\>](#class-serverserver)
|
|
88
89
|
|
|
@@ -264,10 +265,13 @@ We provide two methods to use them:
|
|
|
264
265
|
- `<METHOD>/<path_segment>[/<path_segment>...]` [router](#class-serverrouter) | [handler-extended](#handler-extended) Used when the method matches; equals to `'/<path_segment>': r.Method({ '<METHOD>': <value> })`. E.G., `'GET/meow': 'Meow!'` (only works for HTTP method `'GET'`).
|
|
265
266
|
- `@<METHOD>/<path_segment>[/<path_segment>...]` [router](#class-serverrouter) | [handler-extended](#handler-extended) Used when both the path resolver ends and the method matches; equals to `'/<path_segment>': r.Path(r.Method({ '<METHOD>': <value> }))`. E.G., `'@GET/meow': 'Meow!'` (only works for path `'/meow'` but not `'/meow/something'` and request method is `'GET'`).
|
|
266
267
|
- `*` [router](#class-serverrouter) | [handler-extended](#handler-extended) Any path segment. E.G. `'*': h.Text('Meow? Nothing here!', { statusCode: 404 })`.
|
|
268
|
+
- `/%:<path_parameter_name>` [router](#class-serverrouter) | [handler-extended](#handler-extended) Match any segment (if exists) and save the segment to `ctx.params` by `<path_parameter_name>`. Do the similar thing as [`PathArgRouter`](#router-pathargrouterparamname-next).
|
|
267
269
|
|
|
268
270
|
`PathRouter` is probably the most important router; almost every server needs it!
|
|
269
271
|
|
|
270
|
-
|
|
272
|
+
Please note that when defining only `/%:arg/b` and `/a/c`, requesting `/a/b` **WILL NOT** return the value of `/%:arg/b`. Instead, it will return `404`. This limitation is in place to improve performance, and we believe in designing a great API. If you still need this functionality, you can build your own router.
|
|
273
|
+
|
|
274
|
+
By the way, if you’re looking for a universal matching character, use `'/%:'` instead of `'/*'` (this will match to `'*'` in a literal sense).
|
|
271
275
|
|
|
272
276
|
#### How it works?
|
|
273
277
|
|
|
@@ -283,7 +287,8 @@ map = {
|
|
|
283
287
|
'@/a/b': 'C!',
|
|
284
288
|
'GET/a': 'D!',
|
|
285
289
|
'@GET/a/b': 'E!',
|
|
286
|
-
'
|
|
290
|
+
'@GET/%:arg/c': 'F!',
|
|
291
|
+
'*': 'G!'
|
|
287
292
|
};
|
|
288
293
|
|
|
289
294
|
// we will parse into
|
|
@@ -305,11 +310,19 @@ parsed = {
|
|
|
305
310
|
}
|
|
306
311
|
}
|
|
307
312
|
},
|
|
308
|
-
'
|
|
313
|
+
':': {
|
|
314
|
+
'/c': {
|
|
315
|
+
'@': {
|
|
316
|
+
'GET': 'F!',
|
|
317
|
+
'::GET': [ 'arg' ]
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
'*': 'G!'
|
|
309
322
|
};
|
|
310
323
|
```
|
|
311
324
|
|
|
312
|
-
This format
|
|
325
|
+
This format allows for fast and flexible path routing while maintaining simplicity for developers. However, as mentioned earlier, some specialized path matching will not be supported.
|
|
313
326
|
|
|
314
327
|
### Router: `HostRouter(end, map)`
|
|
315
328
|
|
|
@@ -318,6 +331,7 @@ This format enables fast and flexible path routing while keeping it simple for d
|
|
|
318
331
|
- `.<host_segment>[.<host_segment>...]` [router](#class-serverrouter) | [handler-extended](#handler-extended) A simple host segment routing (reversed). E.G., `.example.com` will match `example.com`, `.localhost` will match `localhost`.
|
|
319
332
|
- `@.<host_segment>[.<host_segment>...]` [router](#class-serverrouter) | [handler-extended](#handler-extended) Used when the host resolver ends here. E.G., `@.example.com` (only works for exactly `example.com` but not `sub.example.com`).
|
|
320
333
|
- `*` [router](#class-serverrouter) | [handler-extended](#handler-extended) Any host segment.
|
|
334
|
+
- `.%:<host_parameter_name>` [router](#class-serverrouter) | [handler-extended](#handler-extended) Match any segment (if exists) and save the segment to `ctx.params` by `<host_parameter_name>`.
|
|
321
335
|
|
|
322
336
|
### Router: `MethodRouter(methodMap)`
|
|
323
337
|
|
|
@@ -326,9 +340,10 @@ This format enables fast and flexible path routing while keeping it simple for d
|
|
|
326
340
|
- `*` [router](#class-serverrouter) | [handler-extended](#handler-extended) Any method, used as fallback.
|
|
327
341
|
- Returns: [\<MethodRouter\>](#router-methodroutermethodmap) Routes based on HTTP method, returns 405 if no method matches.
|
|
328
342
|
|
|
329
|
-
### Router: `FunctionRouter(fn)`
|
|
343
|
+
### Router: `FunctionRouter(fn, ext)`
|
|
330
344
|
|
|
331
|
-
- `fn` [\<Function\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) A function with signature `(env, ctx) => router | handler-extended`.
|
|
345
|
+
- `fn` [\<Function\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) A function with signature `(env, ctx, ext) => router | handler-extended`.
|
|
346
|
+
- `ext` [\<any\>] Passed to `func`.
|
|
332
347
|
|
|
333
348
|
A simple router that allows you to implement custom routing logic.
|
|
334
349
|
|
|
@@ -409,8 +424,9 @@ Sends a JavaScript object serialized as JSON with `Content-Type: application/jso
|
|
|
409
424
|
|
|
410
425
|
Redirects the request to a specified location. Supports both absolute URLs and dynamic redirects based on remaining path segments.
|
|
411
426
|
|
|
412
|
-
### Handler: `FunctionHandler(func)`
|
|
427
|
+
### Handler: `FunctionHandler(func, ext)`
|
|
413
428
|
|
|
414
|
-
- `func` [\<Function\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) A function with signature `(ctx, env) => void | Promise<void>`.
|
|
429
|
+
- `func` [\<Function\>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) A function with signature `(ctx, env, ext) => void | Promise<void>`.
|
|
430
|
+
- `ext` [\<any\>] Passed to `func`.
|
|
415
431
|
|
|
416
432
|
Allows you to implement custom request handling logic directly within a function.
|
package/package.json
CHANGED
package/src/handlers.js
CHANGED
|
@@ -18,28 +18,38 @@ class DataHandler {
|
|
|
18
18
|
constructor(data, options = {}) {
|
|
19
19
|
this.data = data;
|
|
20
20
|
this.options = options;
|
|
21
|
-
}
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
// prebuild headers
|
|
23
|
+
this._statusCode = this.options.statusCode ?? 200;
|
|
24
24
|
if (typeof this.data === 'string') { // string
|
|
25
|
-
|
|
25
|
+
this.data = Buffer.from(this.data, 'utf8');
|
|
26
|
+
this._headers = {
|
|
26
27
|
'Content-Type': 'text/plain; charset=utf-8',
|
|
27
|
-
'Content-Length':
|
|
28
|
+
'Content-Length': this.data.length,
|
|
28
29
|
...this.options.headers
|
|
29
|
-
}
|
|
30
|
-
ctx.res.end(this.data, 'utf8');
|
|
30
|
+
};
|
|
31
31
|
} else if (this.data instanceof Uint8Array) { // buffer
|
|
32
|
-
|
|
32
|
+
this._headers = {
|
|
33
33
|
'Content-Type': 'application/octet-stream',
|
|
34
34
|
'Content-Length': this.data.length,
|
|
35
35
|
...this.options.headers
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
ctx.res.writeHead(this.options.statusCode ?? 200, {
|
|
36
|
+
};
|
|
37
|
+
} else if (stream.isReadable(this.data)) {
|
|
38
|
+
this._headers = {
|
|
40
39
|
'Content-Type': 'application/octet-stream',
|
|
41
40
|
...this.options.headers
|
|
42
|
-
}
|
|
41
|
+
};
|
|
42
|
+
} else {
|
|
43
|
+
throw new Error('Unsupported data type.');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async handle(ctx, env) {
|
|
48
|
+
if (this.data instanceof Uint8Array) { // buffer
|
|
49
|
+
ctx.res.writeHead(this._statusCode, this._headers);
|
|
50
|
+
ctx.res.end(this.data);
|
|
51
|
+
} else if (stream.isReadable(this.data)) { // stream
|
|
52
|
+
ctx.res.writeHead(this._statusCode, this._headers);
|
|
43
53
|
|
|
44
54
|
try {
|
|
45
55
|
await stream.promises.pipeline(this.data, ctx.res);
|
|
@@ -184,19 +194,21 @@ class FolderHandler {
|
|
|
184
194
|
// JSON handler: JSON object
|
|
185
195
|
class JSONHandler {
|
|
186
196
|
constructor(obj, options = {}) {
|
|
187
|
-
this.
|
|
197
|
+
this.data = Buffer.from(JSON.stringify(obj), 'utf8');
|
|
188
198
|
this.options = options;
|
|
189
|
-
}
|
|
190
199
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
ctx.res.writeHead(this.options.statusCode ?? 200, {
|
|
200
|
+
// prebuild headers
|
|
201
|
+
this._statusCode = this.options.statusCode ?? 200;
|
|
202
|
+
this._headers = {
|
|
195
203
|
'Content-Type': 'application/json; charset=utf-8',
|
|
196
204
|
'Content-Length': Buffer.byteLength(data, 'utf8'),
|
|
197
205
|
...this.options.headers
|
|
198
|
-
}
|
|
199
|
-
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
handle(ctx, env) {
|
|
210
|
+
ctx.res.writeHead(this.statusCode, this._headers);
|
|
211
|
+
ctx.res.end(this.data);
|
|
200
212
|
}
|
|
201
213
|
}
|
|
202
214
|
|
|
@@ -205,29 +217,34 @@ class RedirectHandler {
|
|
|
205
217
|
constructor(location, options = {}) {
|
|
206
218
|
this.location = location;
|
|
207
219
|
this.options = options;
|
|
208
|
-
}
|
|
209
220
|
|
|
210
|
-
|
|
211
|
-
|
|
221
|
+
// prebuild headers
|
|
222
|
+
this._statusCode = this.options.statusCode ?? 307;
|
|
223
|
+
this._headers = {
|
|
212
224
|
'Location': this.options.base ?
|
|
213
225
|
this.options.base +
|
|
214
226
|
(this.options.base.endsWith('/') ? '' : '/') +
|
|
215
227
|
env.path.slice(env.pathPointer).map(encodeURIComponent).join('/') :
|
|
216
228
|
this.location,
|
|
217
229
|
...this.options.headers
|
|
218
|
-
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
handle(ctx, env) {
|
|
234
|
+
ctx.res.writeHead(this._statusCode, this._headers);
|
|
219
235
|
ctx.res.end();
|
|
220
236
|
}
|
|
221
237
|
}
|
|
222
238
|
|
|
223
239
|
// function handler: custom function
|
|
224
240
|
class FunctionHandler {
|
|
225
|
-
constructor(func) {
|
|
241
|
+
constructor(func, ext) {
|
|
226
242
|
this.func = func;
|
|
243
|
+
this.ext = ext;
|
|
227
244
|
}
|
|
228
245
|
|
|
229
246
|
handle(ctx, env) {
|
|
230
|
-
return this.func(ctx, env);
|
|
247
|
+
return this.func(ctx, env, this.ext);
|
|
231
248
|
}
|
|
232
249
|
}
|
|
233
250
|
|
|
@@ -235,12 +252,12 @@ class FunctionHandler {
|
|
|
235
252
|
module.exports = {
|
|
236
253
|
DataHandler, TextHandler: DataHandler, FileHandler, FolderHandler, JSONHandler, RedirectHandler, FunctionHandler,
|
|
237
254
|
handlerConstructors: {
|
|
238
|
-
Data: (
|
|
239
|
-
Text: (
|
|
240
|
-
File: (
|
|
241
|
-
Folder: (
|
|
242
|
-
JSON: (
|
|
243
|
-
Redirect: (
|
|
244
|
-
Function: (
|
|
255
|
+
Data: (data, options) => new DataHandler(data, options),
|
|
256
|
+
Text: (data, options) => new DataHandler(data, options),
|
|
257
|
+
File: (file, options) => new FileHandler(file, options),
|
|
258
|
+
Folder: (folder, options) => new FolderHandler(folder, options),
|
|
259
|
+
JSON: (obj, options) => new JSONHandler(obj, options),
|
|
260
|
+
Redirect: (location, options) => new RedirectHandler(location, options),
|
|
261
|
+
Function: (func, ext) => new FunctionHandler(func, ext)
|
|
245
262
|
}
|
|
246
263
|
};
|
package/src/routers.js
CHANGED
|
@@ -29,20 +29,30 @@ class PathRouter {
|
|
|
29
29
|
// key format: '[@ (path end check)][METHOD (method check)]/path/segments'
|
|
30
30
|
// example: '@ GET /cat/names', 'POST /cats', '/api'
|
|
31
31
|
const routeEnd = key.startsWith('@');
|
|
32
|
-
const routeMethod = key.substring(routeEnd ? 1 : 0, firstSlashIndex).trim()
|
|
33
|
-
const routePath = key.substring(firstSlashIndex).split('/').slice(1)
|
|
32
|
+
const routeMethod = key.substring(routeEnd ? 1 : 0, firstSlashIndex).trim();
|
|
33
|
+
const routePath = key.substring(firstSlashIndex).split('/').slice(1);
|
|
34
34
|
|
|
35
35
|
// expand map
|
|
36
36
|
let current = this.map;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
let args = [];
|
|
38
|
+
for (let segment of routePath) {
|
|
39
|
+
if (segment.startsWith('%:')) {
|
|
40
|
+
args.push(segment.substring(2));
|
|
41
|
+
if (!current[':']) current[':'] = {};
|
|
42
|
+
current = current[':'];
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
segment = '/' + decodeURIComponent(segment);
|
|
47
|
+
if (!current[segment]) current[segment] = {};
|
|
48
|
+
current = current[segment];
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
// '*' for non-end check, '@' for end check
|
|
43
52
|
// '*' for any method, 'METHOD' for specific method
|
|
44
53
|
if (!current[routeEnd ? '@' : '*']) current[routeEnd ? '@' : '*'] = {};
|
|
45
54
|
current[routeEnd ? '@' : '*'][routeMethod || '*'] = value;
|
|
55
|
+
if (args.length > 0) current[routeEnd ? '@' : '*']['::' + (routeMethod || '*')] = args;
|
|
46
56
|
}
|
|
47
57
|
}
|
|
48
58
|
|
|
@@ -52,14 +62,21 @@ class PathRouter {
|
|
|
52
62
|
let result = this.map['*'];
|
|
53
63
|
let resultPointer = env.pathPointer;
|
|
54
64
|
let current = this.map;
|
|
65
|
+
let currentArgs = [];
|
|
66
|
+
let resultArgNames;
|
|
55
67
|
while (env.pathPointer < env.path.length) {
|
|
56
|
-
|
|
57
|
-
if (!current[segment]) break;
|
|
68
|
+
let segment = '/' + env.path[env.pathPointer];
|
|
69
|
+
if (!current[segment] && !current[':']) break;
|
|
70
|
+
if (!current[segment]) {
|
|
71
|
+
segment = ':';
|
|
72
|
+
currentArgs.push(env.path[env.pathPointer]);
|
|
73
|
+
}
|
|
58
74
|
|
|
59
75
|
// prepare fallback
|
|
60
76
|
if (current[segment]['*']?.['*'] || current[segment]['*']?.[ctx.method]) {
|
|
61
77
|
result = current[segment]['*'][ctx.method] ?? current[segment]['*']['*'];
|
|
62
78
|
resultPointer = env.pathPointer + 1;
|
|
79
|
+
resultArgNames = current[segment]['*']['::' + ctx.method] ?? current[segment]['*']['::*'];
|
|
63
80
|
}
|
|
64
81
|
|
|
65
82
|
current = current[segment];
|
|
@@ -69,10 +86,17 @@ class PathRouter {
|
|
|
69
86
|
if (env.pathPointer >= env.path.length && (current['@']?.['*'] || current['@']?.[ctx.method])) {
|
|
70
87
|
result = current['@'][ctx.method] ?? current['@']['*'];
|
|
71
88
|
resultPointer = env.pathPointer;
|
|
89
|
+
resultArgNames = current['@']['::' + ctx.method] ?? current['@']['::*'];
|
|
72
90
|
}
|
|
73
91
|
}
|
|
74
92
|
|
|
75
93
|
env.pathPointer = resultPointer;
|
|
94
|
+
if (resultArgNames) {
|
|
95
|
+
const len = resultArgNames.length;
|
|
96
|
+
for (let i = 0; i < len; i++) {
|
|
97
|
+
ctx.params[resultArgNames[i]] = currentArgs[i];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
76
100
|
return result;
|
|
77
101
|
}
|
|
78
102
|
}
|
|
@@ -103,13 +127,22 @@ class HostRouter {
|
|
|
103
127
|
|
|
104
128
|
// expand map
|
|
105
129
|
let current = this.map;
|
|
130
|
+
let args = [];
|
|
106
131
|
for (const segment of routeDomain) {
|
|
132
|
+
if (segment.startsWith('%:')) {
|
|
133
|
+
args.push(segment.substring(2));
|
|
134
|
+
if (!current[':']) current[':'] = {};
|
|
135
|
+
current = current[':'];
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
107
139
|
if (!current['.' + segment]) current['.' + segment] = {};
|
|
108
140
|
current = current['.' + segment];
|
|
109
141
|
}
|
|
110
142
|
|
|
111
143
|
// '*' for non-end check, '@' for end check
|
|
112
144
|
current[routeEnd ? '@' : '*'] = value;
|
|
145
|
+
if (args.length > 0) current['::'] = args;
|
|
113
146
|
}
|
|
114
147
|
}
|
|
115
148
|
|
|
@@ -119,14 +152,21 @@ class HostRouter {
|
|
|
119
152
|
let result = this.map['*'];
|
|
120
153
|
let resultPointer = env.hostPointer;
|
|
121
154
|
let current = this.map;
|
|
155
|
+
let currentArgs = [];
|
|
156
|
+
let resultArgNames;
|
|
122
157
|
while (env.hostPointer < env.host.length) {
|
|
123
|
-
|
|
124
|
-
if (!current[segment]) break;
|
|
158
|
+
let segment = '.' + env.host[env.hostPointer];
|
|
159
|
+
if (!current[segment] && !current[':']) break;
|
|
160
|
+
if (!current[segment]) {
|
|
161
|
+
segment = ':';
|
|
162
|
+
currentArgs.push(env.host[env.hostPointer]);
|
|
163
|
+
}
|
|
125
164
|
|
|
126
165
|
// prepare fallback
|
|
127
166
|
if (current[segment]['*']) {
|
|
128
167
|
result = current[segment]['*'];
|
|
129
168
|
resultPointer = env.hostPointer + 1;
|
|
169
|
+
resultArgNames = current[segment]['::'];
|
|
130
170
|
}
|
|
131
171
|
|
|
132
172
|
current = current[segment];
|
|
@@ -140,6 +180,12 @@ class HostRouter {
|
|
|
140
180
|
}
|
|
141
181
|
|
|
142
182
|
env.hostPointer = resultPointer;
|
|
183
|
+
if (resultArgNames) {
|
|
184
|
+
const len = resultArgNames.length;
|
|
185
|
+
for (let i = 0; i < len; i++) {
|
|
186
|
+
ctx.params[resultArgNames[i]] = currentArgs[i];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
143
189
|
return result;
|
|
144
190
|
}
|
|
145
191
|
}
|
|
@@ -157,12 +203,13 @@ class MethodRouter {
|
|
|
157
203
|
|
|
158
204
|
// function router: a simple router that allows you to make custom routing logic
|
|
159
205
|
class FunctionRouter {
|
|
160
|
-
constructor(fn) {
|
|
206
|
+
constructor(fn, ext) {
|
|
161
207
|
this.fn = fn;
|
|
208
|
+
this.ext = ext;
|
|
162
209
|
}
|
|
163
210
|
|
|
164
211
|
route(env, ctx) {
|
|
165
|
-
return this.fn(env, ctx);
|
|
212
|
+
return this.fn(env, ctx, this.ext);
|
|
166
213
|
}
|
|
167
214
|
}
|
|
168
215
|
|
|
@@ -213,12 +260,11 @@ class SetCodeRouter {
|
|
|
213
260
|
module.exports = {
|
|
214
261
|
PathRouter, HostRouter, MethodRouter, FunctionRouter, PathArgRouter, HostArgRouter, SetCodeRouter,
|
|
215
262
|
routerConstructors: {
|
|
216
|
-
Path: (
|
|
217
|
-
Host: (
|
|
218
|
-
Method: (
|
|
219
|
-
Function: (
|
|
220
|
-
PathArg: (
|
|
221
|
-
|
|
222
|
-
SetCode: (...args) => new SetCodeRouter(...args)
|
|
263
|
+
Path: (end, map) => new PathRouter(end, map),
|
|
264
|
+
Host: (end, map) => new HostRouter(end, map),
|
|
265
|
+
Method: (methodMap) => new MethodRouter(methodMap),
|
|
266
|
+
Function: (fn, ext) => new FunctionRouter(fn, ext),
|
|
267
|
+
PathArg: (name, next) => new PathArgRouter(name, next),
|
|
268
|
+
SetCode: (handlers, next) => new SetCodeRouter(handlers, next)
|
|
223
269
|
}
|
|
224
270
|
};
|
package/src/server.js
CHANGED
|
@@ -14,6 +14,7 @@ const http2 = require('http2');
|
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const stream = require('stream');
|
|
16
16
|
const EventEmitter = require('events');
|
|
17
|
+
const constants = require('constants');
|
|
17
18
|
|
|
18
19
|
// server class
|
|
19
20
|
class Server extends EventEmitter {
|
|
@@ -106,11 +107,12 @@ class Server extends EventEmitter {
|
|
|
106
107
|
} else if (typeof handler === 'function') { // function
|
|
107
108
|
await handler(ctx, env);
|
|
108
109
|
} else if (typeof handler === 'string') { // string
|
|
110
|
+
const data = Buffer.from(handler, 'utf8');
|
|
109
111
|
ctx.res.writeHead(200, {
|
|
110
112
|
'Content-Type': 'text/plain; charset=utf-8',
|
|
111
|
-
'Content-Length':
|
|
113
|
+
'Content-Length': data.length
|
|
112
114
|
});
|
|
113
|
-
ctx.res.end(
|
|
115
|
+
ctx.res.end(data);
|
|
114
116
|
} else if (handler instanceof Uint8Array) { // buffer
|
|
115
117
|
ctx.res.writeHead(200, {
|
|
116
118
|
'Content-Type': 'application/octet-stream',
|
|
@@ -130,11 +132,12 @@ class Server extends EventEmitter {
|
|
|
130
132
|
} else if (typeof handler === 'function') { // function
|
|
131
133
|
await handler(ctx, env);
|
|
132
134
|
} else if (typeof handler === 'string') { // string
|
|
135
|
+
const data = Buffer.from(handler, 'utf8');
|
|
133
136
|
ctx.res.writeHead(code, {
|
|
134
137
|
'Content-Type': 'text/plain; charset=utf-8',
|
|
135
|
-
'Content-Length':
|
|
138
|
+
'Content-Length': data.length
|
|
136
139
|
});
|
|
137
|
-
ctx.res.end(
|
|
140
|
+
ctx.res.end(data);
|
|
138
141
|
} else if (handler instanceof Uint8Array) { // buffer
|
|
139
142
|
ctx.res.writeHead(code, {
|
|
140
143
|
'Content-Type': 'application/octet-stream',
|
|
@@ -164,11 +167,12 @@ class Server extends EventEmitter {
|
|
|
164
167
|
} else if (typeof handler === 'function') { // function
|
|
165
168
|
await handler(ctx, env);
|
|
166
169
|
} else if (typeof handler === 'string') { // string
|
|
170
|
+
const data = Buffer.from(handler, 'utf8');
|
|
167
171
|
ctx.res.writeHead(code, {
|
|
168
172
|
'Content-Type': 'text/plain; charset=utf-8',
|
|
169
|
-
'Content-Length':
|
|
173
|
+
'Content-Length': data.length
|
|
170
174
|
});
|
|
171
|
-
ctx.res.end(
|
|
175
|
+
ctx.res.end(data);
|
|
172
176
|
} else if (handler instanceof Uint8Array) { // buffer
|
|
173
177
|
ctx.res.writeHead(code, {
|
|
174
178
|
'Content-Type': 'application/octet-stream',
|