@maiyunnet/kebab 9.14.0 → 9.14.2

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/sys/route.js CHANGED
@@ -935,6 +935,18 @@ export function getFormData(req, events = {}, limits = {}) {
935
935
  };
936
936
  /** --- 获取的 boundary 文本 --- */
937
937
  const boundary = ct.slice(clio + 9);
938
+ // --- 超时护盾:防止网络静默断开时 Promise 永不 resolve ---
939
+ /** --- 超时定时器 --- */
940
+ let timer;
941
+ /** --- 是否已经结束(防止重复 resolve) --- */
942
+ let finished = false;
943
+ /** --- 清理定时器 --- */
944
+ function clearTimer() {
945
+ if (timer) {
946
+ clearTimeout(timer);
947
+ timer = undefined;
948
+ }
949
+ }
938
950
  // 一下变量是相对于 data 事件的全局变量 ---
939
951
  /** --- 当前临时读入的还未处理的 buffer --- */
940
952
  let buffer = Buffer.from('');
@@ -978,6 +990,23 @@ export function getFormData(req, events = {}, limits = {}) {
978
990
  }
979
991
  }
980
992
  }
993
+ // --- 启动超时定时器(在所有变量声明之后,确保回调闭包可引用) ---
994
+ const timeoutMs = limits.timeout ?? 300_000;
995
+ if (timeoutMs > 0) {
996
+ timer = setTimeout(() => {
997
+ if (finished) {
998
+ return;
999
+ }
1000
+ finished = true;
1001
+ if ((state === EState.FILE) && ftmpName && ftmpStream) {
1002
+ ftmpStream.destroy();
1003
+ }
1004
+ lCore.debug('[ROUTE][GETFORMDATA] formdata request timeout');
1005
+ lCore.log({}, '[ROUTE][GETFORMDATA] formdata request timeout after ' + timeoutMs + 'ms', '-error');
1006
+ cleanupFiles();
1007
+ resolve(false);
1008
+ }, timeoutMs);
1009
+ }
981
1010
  /** --- 拒绝当前文件:销毁流、删临时文件、标记 rejected --- */
982
1011
  function rejectFile() {
983
1012
  rejected = true;
@@ -988,6 +1017,11 @@ export function getFormData(req, events = {}, limits = {}) {
988
1017
  }
989
1018
  /** --- 最终输出 --- */
990
1019
  function finalize() {
1020
+ if (finished) {
1021
+ return;
1022
+ }
1023
+ finished = true;
1024
+ clearTimer();
991
1025
  if (rejected) {
992
1026
  cleanupFiles();
993
1027
  resolve(false);
@@ -1064,6 +1098,12 @@ export function getFormData(req, events = {}, limits = {}) {
1064
1098
  // --- POST 模式 ---
1065
1099
  const io = buffer.indexOf('\r\n--' + boundary);
1066
1100
  if (io === -1) {
1101
+ // --- 检查字段大小限制,防止畸形数据导致 buffer 无限增长 ---
1102
+ const maxField = limits.maxFieldSize ?? 1_048_576;
1103
+ if (buffer.length > maxField + boundary.length + 4) {
1104
+ rejected = true;
1105
+ return;
1106
+ }
1067
1107
  return;
1068
1108
  }
1069
1109
  // --- 找到结束标语,写入 POST ---
@@ -1178,6 +1218,11 @@ export function getFormData(req, events = {}, limits = {}) {
1178
1218
  }
1179
1219
  });
1180
1220
  req.on('error', function (e) {
1221
+ if (finished) {
1222
+ return;
1223
+ }
1224
+ finished = true;
1225
+ clearTimer();
1181
1226
  if ((state === EState.FILE) && ftmpName) {
1182
1227
  ftmpStream.destroy();
1183
1228
  }
@@ -1186,8 +1231,26 @@ export function getFormData(req, events = {}, limits = {}) {
1186
1231
  cleanupFiles();
1187
1232
  resolve(false);
1188
1233
  });
1234
+ // --- 监听连接关闭(HTTP/2 RST_STREAM、代理断连、或用户主动中断上传等场景) ---
1235
+ req.on('close', function () {
1236
+ if (finished) {
1237
+ return;
1238
+ }
1239
+ // --- 若数据未读完且连接已关闭,视为传输中断(多数是用户主动取消,无需记录错误) ---
1240
+ if (!readEnd && writeFileLength > 0) {
1241
+ finished = true;
1242
+ clearTimer();
1243
+ if ((state === EState.FILE) && ftmpName && ftmpStream) {
1244
+ ftmpStream.destroy();
1245
+ }
1246
+ lCore.debug('[ROUTE][GETFORMDATA] connection closed before formdata complete');
1247
+ cleanupFiles();
1248
+ resolve(false);
1249
+ }
1250
+ });
1189
1251
  req.on('end', function () {
1190
1252
  readEnd = true;
1253
+ clearTimer();
1191
1254
  // --- 被拒绝则清理文件并 resolve(false) ---
1192
1255
  if (rejected) {
1193
1256
  finalize();
@@ -47,6 +47,8 @@ export default class extends sCtr.Ctr {
47
47
  coreChecktype(): string;
48
48
  coreCheckschema(): string;
49
49
  coreMuid(): string;
50
+ coreIplimit(): string;
51
+ coreRealiplimit(): string;
50
52
  coreGetlog(): Promise<string>;
51
53
  coreLs(): Promise<string>;
52
54
  coreUpdatecode(): Promise<string>;
@@ -153,6 +153,8 @@ export default class extends sCtr.Ctr {
153
153
  `<br><a href="${this._config.const.urlBase}test/core-checktype">View "test/core-checktype"</a>`,
154
154
  `<br><a href="${this._config.const.urlBase}test/core-checkschema">View "test/core-checkschema"</a>`,
155
155
  `<br><a href="${this._config.const.urlBase}test/core-muid">View "test/core-muid"</a>`,
156
+ `<br><a href="${this._config.const.urlBase}test/core-iplimit">View "test/core-iplimit"</a>`,
157
+ `<br><a href="${this._config.const.urlBase}test/core-realiplimit">View "test/core-realiplimit"</a>`,
156
158
  `<br><a href="${this._config.const.urlBase}test/core-getlog">View "test/core-getlog"</a>`,
157
159
  `<br><a href="${this._config.const.urlBase}test/core-ls">View "test/core-ls"</a>`,
158
160
  `<br><a href="${this._config.const.urlBase}test/core-reload">View "test/core-reload"</a>`,
@@ -213,6 +215,7 @@ export default class extends sCtr.Ctr {
213
215
  `<br><a href="${this._config.const.urlBase}test/sql?type=having">View "test/sql?type=having"</a> <a href="${this._config.const.urlBase}test/sql?type=having&s=pgsql">pgsql</a>`,
214
216
  `<br><a href="${this._config.const.urlBase}test/sql?type=by">View "test/sql?type=by"</a> <a href="${this._config.const.urlBase}test/sql?type=by&s=pgsql">pgsql</a>`,
215
217
  `<br><a href="${this._config.const.urlBase}test/sql?type=field">View "test/sql?type=field"</a> <a href="${this._config.const.urlBase}test/sql?type=field&s=pgsql">pgsql</a>`,
218
+ `<br><a href="${this._config.const.urlBase}test/sql?type=hint">View "test/sql?type=hint"</a>`,
216
219
  '<br><br><b>Consistent:</b>',
217
220
  `<br><br><a href="${this._config.const.urlBase}test/consistent-hash">View "test/consistent-hash"</a>`,
218
221
  `<br><a href="${this._config.const.urlBase}test/consistent-distributed">View "test/consistent-distributed"</a>`,
@@ -1367,6 +1370,24 @@ for (let i = 0; i < 30000; ++i) {
1367
1370
  }
1368
1371
  return echo.join('') + this._getEnd();
1369
1372
  }
1373
+ coreIplimit() {
1374
+ const v6 = '2001:db8:85a3:8d3:1319:8a2e:370:7348';
1375
+ return '<pre>lCore.ipLimit(\'192.168.1.100\');</pre>' + lCore.ipLimit('192.168.1.100') +
1376
+ '<pre>lCore.ipLimit(\'2001:db8:85a3:8d3:1319:8a2e:370:7348\');</pre>' + lCore.ipLimit(v6) +
1377
+ '<pre>lCore.ipLimit(\'2001:db8:85a3:8d3:1319:8a2e:370:7348\', 48);</pre>' + lCore.ipLimit(v6, 48) +
1378
+ '<pre>lCore.ipLimit(\'2001:db8:85a3:8d3:1319:8a2e:370:7348\', 56);</pre>' + lCore.ipLimit(v6, 56) +
1379
+ '<pre>lCore.ipLimit(\'2001:db8:85a3:8d3:1319:8a2e:370:7348\', 128);</pre>' + lCore.ipLimit(v6, 128) +
1380
+ '<pre>lCore.ipLimit(\'::1\');</pre>' + lCore.ipLimit('::1') +
1381
+ '<pre>lCore.ipLimit(\'\');</pre>' + JSON.stringify(lCore.ipLimit('')) +
1382
+ '<br><br>' + this._getEnd();
1383
+ }
1384
+ coreRealiplimit() {
1385
+ return '<pre>lCore.realIP(this);</pre>' + lCore.realIP(this) +
1386
+ '<pre>lCore.realIPLimit(this);</pre>' + lCore.realIPLimit(this) +
1387
+ '<pre>lCore.realIPLimit(this, \'\', 48);</pre>' + lCore.realIPLimit(this, '', 48) +
1388
+ '<pre>lCore.realIPLimit(this, \'\', 56);</pre>' + lCore.realIPLimit(this, '', 56) +
1389
+ '<br><br>' + this._getEnd();
1390
+ }
1370
1391
  async coreGetlog() {
1371
1392
  const path = lTime.format(null, 'Y/m/d/H');
1372
1393
  const log = await lCore.getLog({
@@ -3064,6 +3085,21 @@ Result:<pre id="result">Nothing.</pre>`);
3064
3085
  echo.push(`<pre>sql.field('*');</pre>` + sql.field('*'));
3065
3086
  break;
3066
3087
  }
3088
+ case 'hint': {
3089
+ let s = sql.hint('INDEX(`supply_date_0_0` `idx_supply_date_query`)').select('*', 'supply_date_0_0').getSql();
3090
+ let sd = sql.getData();
3091
+ echo.push(`<pre>sql.hint('INDEX(\`supply_date_0_0\` \`idx_supply_date_query\`)').select('*', 'supply_date_0_0');</pre>
3092
+ <b>getSql() :</b> ${s}<br>
3093
+ <b>getData():</b> <pre>${JSON.stringify(sd, undefined, 4)}</pre>
3094
+ <b>format() :</b> ${sql.format(s, sd)}<hr>`);
3095
+ s = sql.hint('INDEX(`supply_date_0_0` `idx_supply_date_query`) NO_INDEX(`supply_date_0_0` `idx_old`)').select('*', 'supply_date_0_0').getSql();
3096
+ sd = sql.getData();
3097
+ echo.push(`<pre>sql.hint('INDEX(\`supply_date_0_0\` \`idx_supply_date_query\`) NO_INDEX(\`supply_date_0_0\` \`idx_old\`)').select('*', 'supply_date_0_0');</pre>
3098
+ <b>getSql() :</b> ${s}<br>
3099
+ <b>getData():</b> <pre>${JSON.stringify(sd, undefined, 4)}</pre>
3100
+ <b>format() :</b> ${sql.format(s, sd)}`);
3101
+ break;
3102
+ }
3067
3103
  }
3068
3104
  return echo.join('') + '<br><br>' + this._getEnd();
3069
3105
  }