@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 CHANGED
@@ -5,7 +5,7 @@
5
5
  * --- 本文件用来定义每个目录实体地址的常量 ---
6
6
  */
7
7
  /** --- 当前系统版本号 --- */
8
- export declare const VER = "3.2.17";
8
+ export declare const VER = "3.2.19";
9
9
  /** --- 框架根目录,以 / 结尾 --- */
10
10
  export declare const ROOT_PATH: string;
11
11
  export declare const LIB_PATH: string;
package/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * --- 本文件用来定义每个目录实体地址的常量 ---
7
7
  */
8
8
  /** --- 当前系统版本号 --- */
9
- export const VER = '3.2.17';
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.statusCode = 304;
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.statusCode = rres.headers?.['http-code'] ?? 200;
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.statusCode = rres.headers?.['http-code'] ?? 200;
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
@@ -51,6 +51,7 @@ export declare const REGEXP_DOMAIN: RegExp;
51
51
  * @return bool
52
52
  */
53
53
  export declare function isDomain(domain: string): boolean;
54
+ /** --- 可打印的 ascii 字符集 --- */
54
55
  export declare const REGEXP_ASCII: RegExp;
55
56
  /**
56
57
  * --- 判断是否在 ascii 字符集内,仅可输入部分 ---
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maiyunnet/kebab",
3
- "version": "3.2.17",
3
+ "version": "3.2.19",
4
4
  "description": "Simple, easy-to-use, and fully-featured Node.js framework that is ready-to-use out of the box.",
5
5
  "type": "module",
6
6
  "keywords": [
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.statusCode = 403;
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.statusCode = 403;
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.statusCode = 403;
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 = 'auto';
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
- if (input[key] && (typeof input[key] !== 'number') && !Number.isSafeInteger(input[key])) {
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
@@ -69,6 +69,7 @@ export default class Mod {
69
69
  constructor(opt: {
70
70
  'db': lDb.Pool | lDb.Transaction;
71
71
  'ctr'?: sCtr.Ctr;
72
+ /** --- 框架会自动去重 --- */
72
73
  'index'?: string | string[];
73
74
  'alias'?: string;
74
75
  'row'?: Record<string, any>;
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.statusCode = data.res.getHeader('location') ? 302 : httpCode;
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.statusCode = httpCode;
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.statusCode = httpCode;
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.statusCode = 500;
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.statusCode = httpCode;
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.statusCode = httpCode;
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.statusCode = httpCode;
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.statusCode = 500;
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
- ctrTimeout(): Promise<any[]>;
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>;
@@ -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 ctrTimeout() {
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 = [];