@maiyunnet/kebab 3.2.17 → 3.2.19
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/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/core.d.ts +7 -0
- package/lib/core.js +15 -0
- package/lib/cron.d.ts +2 -2
- package/lib/fs.js +4 -4
- package/lib/net.js +3 -2
- package/lib/s3.js +12 -0
- package/lib/text.d.ts +1 -0
- package/lib/text.js +1 -0
- package/package.json +1 -1
- package/sys/child.js +8 -8
- package/sys/cmd.js +9 -9
- package/sys/ctr.d.ts +2 -2
- package/sys/ctr.js +18 -3
- package/sys/mod.d.ts +1 -0
- package/sys/route.js +24 -24
- package/www/example/ctr/test.d.ts +2 -1
- package/www/example/ctr/test.js +17 -3
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* --- 本文件用来定义每个目录实体地址的常量 ---
|
|
7
7
|
*/
|
|
8
8
|
/** --- 当前系统版本号 --- */
|
|
9
|
-
export const VER = '3.2.
|
|
9
|
+
export const VER = '3.2.19';
|
|
10
10
|
// --- 服务端用的路径 ---
|
|
11
11
|
const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
|
|
12
12
|
/** --- /xxx/xxx --- */
|
package/lib/core.d.ts
CHANGED
|
@@ -242,3 +242,10 @@ export declare function debug(message?: any, ...optionalParams: any[]): void;
|
|
|
242
242
|
* @param optionalParams 参数
|
|
243
243
|
*/
|
|
244
244
|
export declare function display(message?: any, ...optionalParams: any[]): void;
|
|
245
|
+
/**
|
|
246
|
+
* --- 让 res 发送头部(前提是头部没有被发送才能调用本方法 ---
|
|
247
|
+
* @param res 响应对象
|
|
248
|
+
* @param statusCode 状态码
|
|
249
|
+
* @param headers 头部
|
|
250
|
+
*/
|
|
251
|
+
export declare function writeHead(res: http2.Http2ServerResponse | http.ServerResponse, statusCode: number, headers?: http.OutgoingHttpHeaders): void;
|
package/lib/core.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Last: 2020-4-11 22:34:58, 2022-10-2 14:13:06, 2022-12-28 20:33:24, 2023-12-15 11:49:02, 2024-7-2 15:23:35, 2025-6-13 19:45:53
|
|
5
5
|
*/
|
|
6
6
|
import * as cp from 'child_process';
|
|
7
|
+
import * as http2 from 'http2';
|
|
7
8
|
import * as stream from 'stream';
|
|
8
9
|
import * as os from 'os';
|
|
9
10
|
import * as kebab from '#kebab/index.js';
|
|
@@ -791,3 +792,17 @@ export function display(message, ...optionalParams) {
|
|
|
791
792
|
// eslint-disable-next-line no-console
|
|
792
793
|
console.log(message, ...optionalParams);
|
|
793
794
|
}
|
|
795
|
+
/**
|
|
796
|
+
* --- 让 res 发送头部(前提是头部没有被发送才能调用本方法 ---
|
|
797
|
+
* @param res 响应对象
|
|
798
|
+
* @param statusCode 状态码
|
|
799
|
+
* @param headers 头部
|
|
800
|
+
*/
|
|
801
|
+
export function writeHead(res, statusCode, headers) {
|
|
802
|
+
if (res instanceof http2.Http2ServerResponse) {
|
|
803
|
+
res.writeHead(statusCode, headers);
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
res.writeHead(statusCode, headers);
|
|
807
|
+
}
|
|
808
|
+
}
|
package/lib/cron.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare function run(): void;
|
|
|
14
14
|
export interface IRegular {
|
|
15
15
|
/** --- 任务名称 --- */
|
|
16
16
|
'name': string;
|
|
17
|
-
/** ---
|
|
17
|
+
/** --- 任务日期对象(系统时区) --- */
|
|
18
18
|
'date': {
|
|
19
19
|
/** --- -1, 1 - 12 --- */
|
|
20
20
|
'month': number;
|
|
@@ -31,7 +31,7 @@ export interface IRegular {
|
|
|
31
31
|
callback: (date: string) => void | Promise<void>;
|
|
32
32
|
}
|
|
33
33
|
export interface IRegularData extends IRegular {
|
|
34
|
-
/** ---
|
|
34
|
+
/** --- 上次执行时间字符串,格式:YmdHi(系统时区) --- */
|
|
35
35
|
'last': string;
|
|
36
36
|
/** --- 总执行次数 --- */
|
|
37
37
|
'count': number;
|
package/lib/fs.js
CHANGED
|
@@ -424,19 +424,18 @@ export function createWriteStream(path, options) {
|
|
|
424
424
|
* @param stat 文件的 stat(如果有)
|
|
425
425
|
*/
|
|
426
426
|
export async function readToResponse(path, req, res, stat) {
|
|
427
|
-
res.statusCode = 200;
|
|
428
427
|
stat ??= await stats(path);
|
|
429
428
|
if (!stat) {
|
|
430
429
|
const content = '<h1>404 Not found</h1><hr>Kebab';
|
|
431
|
-
res.statusCode = 404;
|
|
432
430
|
res.setHeader('content-length', Buffer.byteLength(content));
|
|
431
|
+
lCore.writeHead(res, 404);
|
|
433
432
|
res.end(content);
|
|
434
433
|
return;
|
|
435
434
|
}
|
|
436
435
|
// --- 判断缓存以及 MIME 和编码 ---
|
|
437
436
|
let charset = '';
|
|
438
437
|
const mimeData = mime.getData(path);
|
|
439
|
-
if (['htm', 'html', 'css', 'js', 'xml', 'jpg', 'jpeg', 'svg', 'gif', 'png'].includes(mimeData.extension)) {
|
|
438
|
+
if (['htm', 'html', 'css', 'js', 'xml', 'jpg', 'jpeg', 'svg', 'gif', 'png', 'json'].includes(mimeData.extension)) {
|
|
440
439
|
charset = '; charset=utf-8';
|
|
441
440
|
// --- 这些文件可能需要缓存 ---
|
|
442
441
|
const hash = `W/"${stat.size.toString(16)}-${stat.mtime.getTime().toString(16)}"`;
|
|
@@ -447,7 +446,7 @@ export async function readToResponse(path, req, res, stat) {
|
|
|
447
446
|
const noneMatch = req.headers['if-none-match'];
|
|
448
447
|
const modifiedSince = req.headers['if-modified-since'];
|
|
449
448
|
if ((hash === noneMatch) && (lastModified === modifiedSince)) {
|
|
450
|
-
res
|
|
449
|
+
lCore.writeHead(res, 304);
|
|
451
450
|
res.end();
|
|
452
451
|
return;
|
|
453
452
|
}
|
|
@@ -472,5 +471,6 @@ export async function readToResponse(path, req, res, stat) {
|
|
|
472
471
|
}
|
|
473
472
|
// --- 不压缩 ---
|
|
474
473
|
res.setHeader('content-length', stat.size);
|
|
474
|
+
lCore.writeHead(res, 200);
|
|
475
475
|
await pipe(path, res instanceof http2.Http2ServerResponse ? (res.stream ?? res) : res);
|
|
476
476
|
}
|
package/lib/net.js
CHANGED
|
@@ -12,6 +12,7 @@ import * as kebab from '#kebab/index.js';
|
|
|
12
12
|
import * as lFs from '#kebab/lib/fs.js';
|
|
13
13
|
import * as lText from '#kebab/lib/text.js';
|
|
14
14
|
import * as lTime from '#kebab/lib/time.js';
|
|
15
|
+
import * as lCore from './core.js';
|
|
15
16
|
// --- 自己 ---
|
|
16
17
|
import * as fd from './net/formdata.js';
|
|
17
18
|
import * as lRequest from './net/request.js';
|
|
@@ -525,7 +526,7 @@ export async function mproxy(ctr, auth, opt = {}) {
|
|
|
525
526
|
if (rres.headers) {
|
|
526
527
|
filterHeaders(rres.headers, res, opt.filter);
|
|
527
528
|
}
|
|
528
|
-
res
|
|
529
|
+
lCore.writeHead(res, rres.headers?.['http-code'] ?? 200);
|
|
529
530
|
await new Promise((resolve) => {
|
|
530
531
|
stream.pipe(res).on('finish', () => {
|
|
531
532
|
resolve();
|
|
@@ -589,7 +590,7 @@ export async function rproxy(ctr, route, opt = {}) {
|
|
|
589
590
|
if (rres.headers) {
|
|
590
591
|
filterHeaders(rres.headers, res, opt.filter);
|
|
591
592
|
}
|
|
592
|
-
res
|
|
593
|
+
lCore.writeHead(res, rres.headers?.['http-code'] ?? 200);
|
|
593
594
|
await new Promise((resolve) => {
|
|
594
595
|
stream.pipe(res).on('finish', () => {
|
|
595
596
|
resolve();
|
package/lib/s3.js
CHANGED
|
@@ -8,6 +8,8 @@ import * as s3 from '@aws-sdk/client-s3';
|
|
|
8
8
|
import * as ls from '@aws-sdk/lib-storage';
|
|
9
9
|
import * as lCore from '#kebab/lib/core.js';
|
|
10
10
|
import * as lText from '#kebab/lib/text.js';
|
|
11
|
+
/** --- s3 的连接对象 --- */
|
|
12
|
+
const links = [];
|
|
11
13
|
/**
|
|
12
14
|
* s3 文档:https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/
|
|
13
15
|
*/
|
|
@@ -48,6 +50,12 @@ export class S3 {
|
|
|
48
50
|
endpoint = undefined;
|
|
49
51
|
}
|
|
50
52
|
}
|
|
53
|
+
const token = `${opt.service}-${account}-${secretId}-${region}`;
|
|
54
|
+
const link = links.find((item) => item.token === token);
|
|
55
|
+
if (link) {
|
|
56
|
+
this._link = link.link;
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
51
59
|
this._link = new s3.S3Client({
|
|
52
60
|
'region': region,
|
|
53
61
|
'credentials': {
|
|
@@ -56,6 +64,10 @@ export class S3 {
|
|
|
56
64
|
},
|
|
57
65
|
'endpoint': endpoint,
|
|
58
66
|
});
|
|
67
|
+
links.push({
|
|
68
|
+
'token': token,
|
|
69
|
+
'link': this._link,
|
|
70
|
+
});
|
|
59
71
|
}
|
|
60
72
|
/**
|
|
61
73
|
* --- 修改预定义 bucket ---
|
package/lib/text.d.ts
CHANGED
package/lib/text.js
CHANGED
|
@@ -210,6 +210,7 @@ export const REGEXP_DOMAIN = /^.+?\.((?![0-9]).)+$/i;
|
|
|
210
210
|
export function isDomain(domain) {
|
|
211
211
|
return REGEXP_DOMAIN.test(domain);
|
|
212
212
|
}
|
|
213
|
+
/** --- 可打印的 ascii 字符集 --- */
|
|
213
214
|
export const REGEXP_ASCII = /^[\x20-\x7E]*$/;
|
|
214
215
|
/**
|
|
215
216
|
* --- 判断是否在 ascii 字符集内,仅可输入部分 ---
|
package/package.json
CHANGED
package/sys/child.js
CHANGED
|
@@ -89,7 +89,7 @@ async function run() {
|
|
|
89
89
|
}, function (req, res) {
|
|
90
90
|
const host = (req.headers[':authority'] ?? req.headers['host'] ?? '');
|
|
91
91
|
if (!host) {
|
|
92
|
-
res
|
|
92
|
+
lCore.writeHead(res, 403);
|
|
93
93
|
res.end();
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
@@ -141,7 +141,7 @@ async function run() {
|
|
|
141
141
|
httpServer = http.createServer(function (req, res) {
|
|
142
142
|
const host = (req.headers['host'] ?? '');
|
|
143
143
|
if (!host) {
|
|
144
|
-
res
|
|
144
|
+
lCore.writeHead(res, 403);
|
|
145
145
|
res.end();
|
|
146
146
|
return;
|
|
147
147
|
}
|
|
@@ -209,8 +209,8 @@ async function requestHandler(req, res, https) {
|
|
|
209
209
|
return;
|
|
210
210
|
}
|
|
211
211
|
const content = '<h1>504 Gateway Timeout</h1><hr>Kebab';
|
|
212
|
-
res.statusCode = 504;
|
|
213
212
|
res.setHeader('content-length', Buffer.byteLength(content));
|
|
213
|
+
lCore.writeHead(res, 504);
|
|
214
214
|
res.end(content);
|
|
215
215
|
}
|
|
216
216
|
};
|
|
@@ -232,7 +232,7 @@ async function requestHandler(req, res, https) {
|
|
|
232
232
|
/** --- 当前的 vhost 配置文件 --- */
|
|
233
233
|
const vhost = await getVhostByHostname(uri.hostname ?? '');
|
|
234
234
|
if (!vhost) {
|
|
235
|
-
res
|
|
235
|
+
lCore.writeHead(res, 403);
|
|
236
236
|
res.end();
|
|
237
237
|
return;
|
|
238
238
|
/*
|
|
@@ -262,9 +262,9 @@ async function requestHandler(req, res, https) {
|
|
|
262
262
|
let stat = await lFs.stats(vhost.real + now + item);
|
|
263
263
|
if (!stat) {
|
|
264
264
|
const content = '<h1>404 Not found</h1><hr>Kebab';
|
|
265
|
-
res.statusCode = 404;
|
|
266
265
|
res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
267
266
|
res.setHeader('content-length', Buffer.byteLength(content));
|
|
267
|
+
lCore.writeHead(res, 404);
|
|
268
268
|
res.end(content);
|
|
269
269
|
return;
|
|
270
270
|
}
|
|
@@ -298,9 +298,9 @@ async function requestHandler(req, res, https) {
|
|
|
298
298
|
'headers': {}
|
|
299
299
|
}, '[CHILD][requestHandler][E0]' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
|
|
300
300
|
const content = '<h1>500 Server Error</h1><hr>Kebab';
|
|
301
|
-
res.statusCode = 500;
|
|
302
301
|
res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
303
302
|
res.setHeader('content-length', Buffer.byteLength(content));
|
|
303
|
+
lCore.writeHead(res, 500);
|
|
304
304
|
res.end(content);
|
|
305
305
|
return;
|
|
306
306
|
}
|
|
@@ -337,9 +337,9 @@ async function requestHandler(req, res, https) {
|
|
|
337
337
|
catch (e) {
|
|
338
338
|
lCore.log({}, '[CHILD][requestHandler][E1]' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
|
|
339
339
|
const content = '<h1>500 Server Error</h1><hr>Kebab';
|
|
340
|
-
res.statusCode = 500;
|
|
341
340
|
res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
342
341
|
res.setHeader('content-length', Buffer.byteLength(content));
|
|
342
|
+
lCore.writeHead(res, 500);
|
|
343
343
|
res.end(content);
|
|
344
344
|
return;
|
|
345
345
|
}
|
|
@@ -356,9 +356,9 @@ async function requestHandler(req, res, https) {
|
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
358
|
const content = '<h1>403 Forbidden</h1><hr>Kebab';
|
|
359
|
-
res.statusCode = 403;
|
|
360
359
|
res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
361
360
|
res.setHeader('content-length', Buffer.byteLength(content));
|
|
361
|
+
lCore.writeHead(res, 403);
|
|
362
362
|
res.end(content);
|
|
363
363
|
}
|
|
364
364
|
/**
|
package/sys/cmd.js
CHANGED
|
@@ -126,16 +126,16 @@ async function run() {
|
|
|
126
126
|
// --- config - s3 ---
|
|
127
127
|
config.s3 ??= {};
|
|
128
128
|
config.s3['CF'] ??= {};
|
|
129
|
-
config.s3['CF'].account
|
|
130
|
-
config.s3['CF'].sid
|
|
131
|
-
config.s3['CF'].skey
|
|
132
|
-
config.s3['CF'].region
|
|
133
|
-
config.s3['CF'].bucket
|
|
129
|
+
config.s3['CF'].account ??= '';
|
|
130
|
+
config.s3['CF'].sid ??= '';
|
|
131
|
+
config.s3['CF'].skey ??= '';
|
|
132
|
+
config.s3['CF'].region ??= 'auto';
|
|
133
|
+
config.s3['CF'].bucket ??= '';
|
|
134
134
|
config.s3['TENCENT'] ??= {};
|
|
135
|
-
config.s3['TENCENT'].sid
|
|
136
|
-
config.s3['TENCENT'].skey
|
|
137
|
-
config.s3['TENCENT'].region
|
|
138
|
-
config.s3['TENCENT'].bucket
|
|
135
|
+
config.s3['TENCENT'].sid ??= '';
|
|
136
|
+
config.s3['TENCENT'].skey ??= '';
|
|
137
|
+
config.s3['TENCENT'].region ??= '';
|
|
138
|
+
config.s3['TENCENT'].bucket ??= '';
|
|
139
139
|
// --- config - turnstile ---
|
|
140
140
|
config.turnstile ??= {};
|
|
141
141
|
config.turnstile['CF'] ??= {};
|
package/sys/ctr.d.ts
CHANGED
|
@@ -155,14 +155,14 @@ export declare class Ctr {
|
|
|
155
155
|
/**
|
|
156
156
|
* --- 检测提交的数据类型 ---
|
|
157
157
|
* @param input 要校验的输入项
|
|
158
|
-
* @param rule
|
|
158
|
+
* @param rule 规则, int, double, num(可字符串), array, bool, string, ascii
|
|
159
159
|
* @param rtn 返回值
|
|
160
160
|
*/
|
|
161
161
|
protected _checkInput(input: Record<string, kebab.Json>, rule: Record<string, kebab.Json[]>, rtn: kebab.Json[]): boolean;
|
|
162
162
|
/**
|
|
163
163
|
* --- 检测提交的数据类型(会检测 XSRF) ---
|
|
164
164
|
* @param input 要校验的输入项
|
|
165
|
-
* @param rule
|
|
165
|
+
* @param rule 规则, int, double, num(可字符串), array, bool, string, ascii
|
|
166
166
|
* @param rtn 返回值
|
|
167
167
|
*/
|
|
168
168
|
protected _checkXInput(input: Record<string, kebab.Json>, rule: Record<string, kebab.Json[]>, rtn: kebab.Json[]): boolean;
|
package/sys/ctr.js
CHANGED
|
@@ -218,7 +218,7 @@ export class Ctr {
|
|
|
218
218
|
/**
|
|
219
219
|
* --- 检测提交的数据类型 ---
|
|
220
220
|
* @param input 要校验的输入项
|
|
221
|
-
* @param rule
|
|
221
|
+
* @param rule 规则, int, double, num(可字符串), array, bool, string, ascii
|
|
222
222
|
* @param rtn 返回值
|
|
223
223
|
*/
|
|
224
224
|
_checkInput(input, rule, rtn) {
|
|
@@ -308,7 +308,8 @@ export class Ctr {
|
|
|
308
308
|
}
|
|
309
309
|
case 'int':
|
|
310
310
|
case 'integer': {
|
|
311
|
-
|
|
311
|
+
// --- 必须是数字型且是整数 ---
|
|
312
|
+
if (input[key] && !Number.isSafeInteger(input[key])) {
|
|
312
313
|
rtn[0] = val[lastK][0];
|
|
313
314
|
rtn[1] = val[lastK][1];
|
|
314
315
|
if (val[lastK][2]) {
|
|
@@ -320,6 +321,7 @@ export class Ctr {
|
|
|
320
321
|
}
|
|
321
322
|
case 'float':
|
|
322
323
|
case 'double': {
|
|
324
|
+
// --- 必须是数字型 ---
|
|
323
325
|
if (input[key] && (typeof input[key] !== 'number')) {
|
|
324
326
|
rtn[0] = val[lastK][0];
|
|
325
327
|
rtn[1] = val[lastK][1];
|
|
@@ -332,6 +334,7 @@ export class Ctr {
|
|
|
332
334
|
}
|
|
333
335
|
case 'num':
|
|
334
336
|
case 'number': {
|
|
337
|
+
// --- 可字符串数字 ---
|
|
335
338
|
if (input[key] && (typeof input[key] !== 'number') && !/^[0-9]+\.?[0-9]*$/.test(input[key])) {
|
|
336
339
|
rtn[0] = val[lastK][0];
|
|
337
340
|
rtn[1] = val[lastK][1];
|
|
@@ -378,6 +381,18 @@ export class Ctr {
|
|
|
378
381
|
}
|
|
379
382
|
break;
|
|
380
383
|
}
|
|
384
|
+
case 'ascii': {
|
|
385
|
+
// --- 必须是 ASCII 字符 ---
|
|
386
|
+
if (input[key] !== null && !lText.isAscii(input[key])) {
|
|
387
|
+
rtn[0] = val[lastK][0];
|
|
388
|
+
rtn[1] = val[lastK][1];
|
|
389
|
+
if (val[lastK][2]) {
|
|
390
|
+
rtn[2] = val[lastK][2];
|
|
391
|
+
}
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
381
396
|
default: {
|
|
382
397
|
let match;
|
|
383
398
|
if (input[key] !== null) {
|
|
@@ -468,7 +483,7 @@ export class Ctr {
|
|
|
468
483
|
/**
|
|
469
484
|
* --- 检测提交的数据类型(会检测 XSRF) ---
|
|
470
485
|
* @param input 要校验的输入项
|
|
471
|
-
* @param rule
|
|
486
|
+
* @param rule 规则, int, double, num(可字符串), array, bool, string, ascii
|
|
472
487
|
* @param rtn 返回值
|
|
473
488
|
*/
|
|
474
489
|
_checkXInput(input, rule, rtn) {
|
package/sys/mod.d.ts
CHANGED
package/sys/route.js
CHANGED
|
@@ -37,9 +37,9 @@ export async function run(data) {
|
|
|
37
37
|
if (!configContent) {
|
|
38
38
|
if (data.res) {
|
|
39
39
|
const content = '<h1>500 File kebab.json can not be read</h1><hr>Kebab';
|
|
40
|
-
data.res.statusCode = 500;
|
|
41
40
|
data.res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
42
41
|
data.res.setHeader('content-length', Buffer.byteLength(content));
|
|
42
|
+
lCore.writeHead(data.res, 500);
|
|
43
43
|
data.res.end(content);
|
|
44
44
|
}
|
|
45
45
|
else {
|
|
@@ -133,13 +133,13 @@ export async function run(data) {
|
|
|
133
133
|
/** --- 组合要跳转的路径 --- */
|
|
134
134
|
const apath = config.const.path + (config.const.qs ? '?' + config.const.qs : '');
|
|
135
135
|
if (config.lang.list.includes(lang)) {
|
|
136
|
-
data.res.statusCode = 302;
|
|
137
136
|
data.res.setHeader('location', config.const.urlBase + lang + '/' + apath);
|
|
137
|
+
lCore.writeHead(data.res, 302);
|
|
138
138
|
data.res.end('');
|
|
139
139
|
return true;
|
|
140
140
|
}
|
|
141
|
-
data.res.statusCode = 302;
|
|
142
141
|
data.res.setHeader('location', config.const.urlBase + config.lang.list[0] + '/' + apath);
|
|
142
|
+
lCore.writeHead(data.res, 302);
|
|
143
143
|
data.res.end('');
|
|
144
144
|
return true;
|
|
145
145
|
}
|
|
@@ -183,15 +183,15 @@ export async function run(data) {
|
|
|
183
183
|
if (pathLeft.startsWith('middle')) {
|
|
184
184
|
if (data.res) {
|
|
185
185
|
if (config.route['#404']) {
|
|
186
|
-
data.res.statusCode = 302;
|
|
187
186
|
data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
|
|
187
|
+
lCore.writeHead(data.res, 302);
|
|
188
188
|
data.res.end('');
|
|
189
189
|
return true;
|
|
190
190
|
}
|
|
191
191
|
const content = '[Error] Controller not found, path: ' + path + '.';
|
|
192
|
-
data.res.statusCode = 404;
|
|
193
192
|
data.res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
194
193
|
data.res.setHeader('content-length', Buffer.byteLength(content));
|
|
194
|
+
lCore.writeHead(data.res, 404);
|
|
195
195
|
data.res.end(content);
|
|
196
196
|
}
|
|
197
197
|
else {
|
|
@@ -413,9 +413,9 @@ export async function run(data) {
|
|
|
413
413
|
catch (e) {
|
|
414
414
|
lCore.log(middle, '(E03)' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
|
|
415
415
|
const content = '<h1>500 Server Error</h1><hr>Kebab';
|
|
416
|
-
data.res.statusCode = 500;
|
|
417
416
|
data.res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
418
417
|
data.res.setHeader('content-length', Buffer.byteLength(content));
|
|
418
|
+
lCore.writeHead(data.res, 500);
|
|
419
419
|
data.res.end(content);
|
|
420
420
|
return true;
|
|
421
421
|
}
|
|
@@ -429,9 +429,9 @@ export async function run(data) {
|
|
|
429
429
|
catch (e) {
|
|
430
430
|
lCore.log(middle, '(E05)' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
|
|
431
431
|
const content = '<h1>500 Server Error</h1><hr>Kebab';
|
|
432
|
-
data.res.statusCode = 500;
|
|
433
432
|
data.res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
434
433
|
data.res.setHeader('content-length', Buffer.byteLength(content));
|
|
434
|
+
lCore.writeHead(data.res, 500);
|
|
435
435
|
data.res.end(content);
|
|
436
436
|
return true;
|
|
437
437
|
}
|
|
@@ -443,15 +443,15 @@ export async function run(data) {
|
|
|
443
443
|
if (!await lFs.isFile(filePath)) {
|
|
444
444
|
// --- 指定的控制器不存在 ---
|
|
445
445
|
if (config.route['#404']) {
|
|
446
|
-
data.res.statusCode = 302;
|
|
447
446
|
data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
|
|
447
|
+
lCore.writeHead(data.res, 302);
|
|
448
448
|
data.res.end('');
|
|
449
449
|
return true;
|
|
450
450
|
}
|
|
451
451
|
const content = '[Error] Controller not found, path: ' + path + '.';
|
|
452
|
-
data.res.statusCode = 404;
|
|
453
452
|
data.res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
454
453
|
data.res.setHeader('content-length', Buffer.byteLength(content));
|
|
454
|
+
lCore.writeHead(data.res, 404);
|
|
455
455
|
data.res.end(content);
|
|
456
456
|
return true;
|
|
457
457
|
}
|
|
@@ -482,8 +482,8 @@ export async function run(data) {
|
|
|
482
482
|
lCore.log(cctr, '', '-visit');
|
|
483
483
|
// --- 强制 HTTPS ---
|
|
484
484
|
if (config.set.mustHttps && !config.const.https) {
|
|
485
|
-
data.res.statusCode = 302;
|
|
486
485
|
data.res.setHeader('location', data.req.url ?? '');
|
|
486
|
+
lCore.writeHead(data.res, 302);
|
|
487
487
|
data.res.end('');
|
|
488
488
|
return true;
|
|
489
489
|
}
|
|
@@ -491,15 +491,15 @@ export async function run(data) {
|
|
|
491
491
|
if (pathRight.startsWith('_') || pathRight === 'onUpgrade' || pathRight === 'onLoad' || pathRight === 'onData' || pathRight === 'onDrain' || pathRight === 'onEnd' || pathRight === 'onClose' || pathRight === 'setPrototype' || pathRight === 'getPrototype' || pathRight === 'getAuthorization') {
|
|
492
492
|
// --- _ 开头的 action 是内部方法,不允许访问 ---
|
|
493
493
|
if (config.route['#404']) {
|
|
494
|
-
data.res.statusCode = 302;
|
|
495
494
|
data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
|
|
495
|
+
lCore.writeHead(data.res, 302);
|
|
496
496
|
data.res.end('');
|
|
497
497
|
return true;
|
|
498
498
|
}
|
|
499
499
|
const content = '[Error] Action not found, path: ' + path + '.';
|
|
500
|
-
data.res.statusCode = 404;
|
|
501
500
|
data.res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
502
501
|
data.res.setHeader('content-length', Buffer.byteLength(content));
|
|
502
|
+
lCore.writeHead(data.res, 404);
|
|
503
503
|
data.res.end(content);
|
|
504
504
|
return true;
|
|
505
505
|
}
|
|
@@ -508,15 +508,15 @@ export async function run(data) {
|
|
|
508
508
|
});
|
|
509
509
|
if (cctr[pathRight] === undefined) {
|
|
510
510
|
if (config.route['#404']) {
|
|
511
|
-
data.res.statusCode = 302;
|
|
512
511
|
data.res.setHeader('location', lText.urlResolve(config.const.urlBase, config.route['#404']));
|
|
512
|
+
lCore.writeHead(data.res, 302);
|
|
513
513
|
data.res.end('');
|
|
514
514
|
return true;
|
|
515
515
|
}
|
|
516
516
|
const content = '[Error] Action not found, path: ' + path + '.';
|
|
517
|
-
data.res.statusCode = 404;
|
|
518
517
|
data.res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
519
518
|
data.res.setHeader('content-length', Buffer.byteLength(content));
|
|
519
|
+
lCore.writeHead(data.res, 404);
|
|
520
520
|
data.res.end(content);
|
|
521
521
|
return true;
|
|
522
522
|
}
|
|
@@ -541,9 +541,9 @@ export async function run(data) {
|
|
|
541
541
|
catch (e) {
|
|
542
542
|
lCore.log(cctr, '(E05)' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
|
|
543
543
|
const content = '<h1>500 Server Error</h1><hr>Kebab';
|
|
544
|
-
data.res.statusCode = 500;
|
|
545
544
|
data.res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
546
545
|
data.res.setHeader('content-length', Buffer.byteLength(content));
|
|
546
|
+
lCore.writeHead(data.res, 500);
|
|
547
547
|
data.res.end(content);
|
|
548
548
|
await waitCtr(cctr);
|
|
549
549
|
return true;
|
|
@@ -556,9 +556,9 @@ export async function run(data) {
|
|
|
556
556
|
catch (e) {
|
|
557
557
|
lCore.log(cctr, '(E04)' + lText.stringifyJson(e.stack).slice(1, -1), '-error');
|
|
558
558
|
const content = '<h1>500 Server Error</h1><hr>Kebab';
|
|
559
|
-
data.res.statusCode = 500;
|
|
560
559
|
data.res.setHeader('content-type', 'text/html; charset=utf-8');
|
|
561
560
|
data.res.setHeader('content-length', Buffer.byteLength(content));
|
|
561
|
+
lCore.writeHead(data.res, 500);
|
|
562
562
|
data.res.end(content);
|
|
563
563
|
await waitCtr(cctr);
|
|
564
564
|
return true;
|
|
@@ -580,7 +580,7 @@ export async function run(data) {
|
|
|
580
580
|
// --- 已经自行输出过 writeHead,可能自行处理了内容,如 pipe,则不再 writeHead ---
|
|
581
581
|
}
|
|
582
582
|
else {
|
|
583
|
-
data.res
|
|
583
|
+
lCore.writeHead(data.res, data.res.getHeader('location') ? 302 : httpCode);
|
|
584
584
|
}
|
|
585
585
|
if (!data.res.writableEnded) {
|
|
586
586
|
// --- 如果当前还没结束,则强制关闭连接,一切 pipe 请自行在方法中 await,否则会被中断 ---
|
|
@@ -603,7 +603,7 @@ export async function run(data) {
|
|
|
603
603
|
data.res.setHeader('content-encoding', compress.type);
|
|
604
604
|
}
|
|
605
605
|
}
|
|
606
|
-
data.res
|
|
606
|
+
lCore.writeHead(data.res, httpCode);
|
|
607
607
|
}
|
|
608
608
|
if (!data.res.writableEnded) {
|
|
609
609
|
if (compress) {
|
|
@@ -634,7 +634,7 @@ export async function run(data) {
|
|
|
634
634
|
if (compress) {
|
|
635
635
|
data.res.setHeader('content-encoding', compress.type);
|
|
636
636
|
}
|
|
637
|
-
data.res
|
|
637
|
+
lCore.writeHead(data.res, httpCode);
|
|
638
638
|
}
|
|
639
639
|
if (!data.res.writableEnded) {
|
|
640
640
|
if (compress) {
|
|
@@ -652,7 +652,7 @@ export async function run(data) {
|
|
|
652
652
|
if (rtn.length === 0) {
|
|
653
653
|
// --- 异常 ---
|
|
654
654
|
if (!data.res.headersSent) {
|
|
655
|
-
data.res
|
|
655
|
+
lCore.writeHead(data.res, 500);
|
|
656
656
|
}
|
|
657
657
|
if (!data.res.writableEnded) {
|
|
658
658
|
data.res.end('<h1>500 Internal server error</h1><hr>Kebab');
|
|
@@ -688,7 +688,7 @@ export async function run(data) {
|
|
|
688
688
|
data.res.setHeader('content-encoding', compress.type);
|
|
689
689
|
}
|
|
690
690
|
}
|
|
691
|
-
data.res
|
|
691
|
+
lCore.writeHead(data.res, httpCode);
|
|
692
692
|
}
|
|
693
693
|
if (!data.res.writableEnded) {
|
|
694
694
|
if (compress) {
|
|
@@ -713,7 +713,7 @@ export async function run(data) {
|
|
|
713
713
|
if (compress) {
|
|
714
714
|
data.res.setHeader('content-encoding', compress.type);
|
|
715
715
|
}
|
|
716
|
-
data.res
|
|
716
|
+
lCore.writeHead(data.res, httpCode);
|
|
717
717
|
}
|
|
718
718
|
if (!data.res.writableEnded) {
|
|
719
719
|
const passThrough = new stream.PassThrough();
|
|
@@ -742,7 +742,7 @@ export async function run(data) {
|
|
|
742
742
|
data.res.setHeader('content-encoding', compress.type);
|
|
743
743
|
}
|
|
744
744
|
}
|
|
745
|
-
data.res
|
|
745
|
+
lCore.writeHead(data.res, httpCode);
|
|
746
746
|
}
|
|
747
747
|
if (!data.res.writableEnded) {
|
|
748
748
|
if (compress) {
|
|
@@ -759,7 +759,7 @@ export async function run(data) {
|
|
|
759
759
|
else {
|
|
760
760
|
// --- 异常 ---
|
|
761
761
|
if (!data.res.headersSent) {
|
|
762
|
-
data.res
|
|
762
|
+
lCore.writeHead(data.res, 500);
|
|
763
763
|
}
|
|
764
764
|
if (!data.res.writableEnded) {
|
|
765
765
|
data.res.end('<h1>500 Internal server error</h1><hr>Kebab');
|
|
@@ -24,7 +24,8 @@ export default class extends sCtr.Ctr {
|
|
|
24
24
|
ctrCross2(): kebab.Json[];
|
|
25
25
|
ctrReadable(): fs.ReadStream;
|
|
26
26
|
ctrAsynctask(): any[];
|
|
27
|
-
|
|
27
|
+
ctrTimeoutLong(): Promise<any[]>;
|
|
28
|
+
ctrTimeoutShort(): Promise<any[]>;
|
|
28
29
|
modTest(): Promise<kebab.Json[] | string | boolean>;
|
|
29
30
|
modSplit(): Promise<string>;
|
|
30
31
|
modSplit1(): Promise<void>;
|
package/www/example/ctr/test.js
CHANGED
|
@@ -112,7 +112,8 @@ export default class extends sCtr.Ctr {
|
|
|
112
112
|
`<br><a href="${this._config.const.urlBase}test/ctr-cross">View "test/ctr-cross"</a>`,
|
|
113
113
|
`<br><a href="${this._config.const.urlBase}test/ctr-readable">View "test/ctr-readable"</a>`,
|
|
114
114
|
`<br><a href="${this._config.const.urlBase}test/ctr-asynctask">View "test/ctr-asynctask"</a>`,
|
|
115
|
-
`<br><a href="${this._config.const.urlBase}test/ctr-timeout">View "test/ctr-timeout"</a>`,
|
|
115
|
+
`<br><a href="${this._config.const.urlBase}test/ctr-timeout-long">View "test/ctr-timeout-long"</a>`,
|
|
116
|
+
`<br><a href="${this._config.const.urlBase}test/ctr-timeout-short">View "test/ctr-timeout-short"</a>`,
|
|
116
117
|
'<br><br><b>Middle:</b>',
|
|
117
118
|
`<br><br><a href="${this._config.const.urlBase}test/middle">View "test/middle"</a>`,
|
|
118
119
|
'<br><br><b>Model test:</b>',
|
|
@@ -509,7 +510,7 @@ Result:<pre id="result">Nothing.</pre>` + this._getEnd();
|
|
|
509
510
|
});
|
|
510
511
|
return [1];
|
|
511
512
|
}
|
|
512
|
-
async
|
|
513
|
+
async ctrTimeoutLong() {
|
|
513
514
|
this.timeout = 70_000;
|
|
514
515
|
const echo = ['1'];
|
|
515
516
|
await lCore.sleep(15_000);
|
|
@@ -520,7 +521,20 @@ Result:<pre id="result">Nothing.</pre>` + this._getEnd();
|
|
|
520
521
|
echo.push('4');
|
|
521
522
|
await lCore.sleep(15_000);
|
|
522
523
|
echo.push('5');
|
|
523
|
-
return [1, echo];
|
|
524
|
+
return [1, { 'list': echo }];
|
|
525
|
+
}
|
|
526
|
+
async ctrTimeoutShort() {
|
|
527
|
+
this.timeout = 5_000;
|
|
528
|
+
const echo = ['1'];
|
|
529
|
+
await lCore.sleep(15_000);
|
|
530
|
+
echo.push('2');
|
|
531
|
+
await lCore.sleep(15_000);
|
|
532
|
+
echo.push('3');
|
|
533
|
+
await lCore.sleep(15_000);
|
|
534
|
+
echo.push('4');
|
|
535
|
+
await lCore.sleep(15_000);
|
|
536
|
+
echo.push('5');
|
|
537
|
+
return [1, { 'list': echo }];
|
|
524
538
|
}
|
|
525
539
|
async modTest() {
|
|
526
540
|
const retur = [];
|