@maiyunnet/kebab 9.13.4 → 9.13.6
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/doc/kebab-rag.md +24 -18
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/undici/response.d.ts +1 -1
- package/package.json +1 -1
- package/sys/ctr.d.ts +2 -2
- package/sys/ctr.js +5 -2
- package/sys/route.js +106 -49
- package/www/example/ctr/test.d.ts +8 -27
- package/www/example/ctr/test.js +65 -495
package/doc/kebab-rag.md
CHANGED
|
@@ -1360,7 +1360,7 @@ index/variables/VER.md
|
|
|
1360
1360
|
|
|
1361
1361
|
# Variable: VER
|
|
1362
1362
|
|
|
1363
|
-
> `const` **VER**: `"9.13.
|
|
1363
|
+
> `const` **VER**: `"9.13.6"` = `'9.13.6'`
|
|
1364
1364
|
|
|
1365
1365
|
Defined in: [index.ts:10](https://github.com/maiyunnet/kebab/blob/master/index.ts#L10)
|
|
1366
1366
|
|
|
@@ -22804,7 +22804,7 @@ Defined in: [sys/ctr.ts:750](https://github.com/maiyunnet/kebab/blob/master/sys/
|
|
|
22804
22804
|
|
|
22805
22805
|
> `protected` **\_cross**(`opt?`): `boolean`
|
|
22806
22806
|
|
|
22807
|
-
Defined in: [sys/ctr.ts:
|
|
22807
|
+
Defined in: [sys/ctr.ts:985](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L985)
|
|
22808
22808
|
|
|
22809
22809
|
开启跨域请求
|
|
22810
22810
|
|
|
@@ -22886,7 +22886,7 @@ Defined in: [sys/ctr.ts:764](https://github.com/maiyunnet/kebab/blob/master/sys/
|
|
|
22886
22886
|
|
|
22887
22887
|
> `protected` **\_end**(): `void`
|
|
22888
22888
|
|
|
22889
|
-
Defined in: [sys/ctr.ts:
|
|
22889
|
+
Defined in: [sys/ctr.ts:1093](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L1093)
|
|
22890
22890
|
|
|
22891
22891
|
主动关闭当前 socket 连接
|
|
22892
22892
|
|
|
@@ -22928,7 +22928,7 @@ Defined in: [sys/ctr.ts:788](https://github.com/maiyunnet/kebab/blob/master/sys/
|
|
|
22928
22928
|
|
|
22929
22929
|
> `protected` **\_getLocale**(): `string`
|
|
22930
22930
|
|
|
22931
|
-
Defined in: [sys/ctr.ts:
|
|
22931
|
+
Defined in: [sys/ctr.ts:976](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L976)
|
|
22932
22932
|
|
|
22933
22933
|
获取当前语言名
|
|
22934
22934
|
|
|
@@ -22942,7 +22942,7 @@ Defined in: [sys/ctr.ts:973](https://github.com/maiyunnet/kebab/blob/master/sys/
|
|
|
22942
22942
|
|
|
22943
22943
|
> `protected` **\_getLocaleJsonString**(): `string`
|
|
22944
22944
|
|
|
22945
|
-
Defined in: [sys/ctr.ts:
|
|
22945
|
+
Defined in: [sys/ctr.ts:964](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L964)
|
|
22946
22946
|
|
|
22947
22947
|
根据当前后台语言包设置情况获取 JSON 字符串传输到前台
|
|
22948
22948
|
|
|
@@ -22994,7 +22994,7 @@ Defined in: [sys/ctr.ts:315](https://github.com/maiyunnet/kebab/blob/master/sys/
|
|
|
22994
22994
|
|
|
22995
22995
|
> `protected` **\_handleFormData**(`events?`, `limits?`): `Promise`\<`boolean`\>
|
|
22996
22996
|
|
|
22997
|
-
Defined in: [sys/ctr.ts:
|
|
22997
|
+
Defined in: [sys/ctr.ts:1102](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L1102)
|
|
22998
22998
|
|
|
22999
22999
|
获取 formdata 的信息
|
|
23000
23000
|
|
|
@@ -23042,7 +23042,7 @@ Defined in: [sys/ctr.ts:1099](https://github.com/maiyunnet/kebab/blob/master/sys
|
|
|
23042
23042
|
|
|
23043
23043
|
> **\_l**(`key`, `data?`): `string`
|
|
23044
23044
|
|
|
23045
|
-
Defined in: [sys/ctr.ts:
|
|
23045
|
+
Defined in: [sys/ctr.ts:1028](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L1028)
|
|
23046
23046
|
|
|
23047
23047
|
获取语言包值
|
|
23048
23048
|
|
|
@@ -23066,11 +23066,17 @@ Defined in: [sys/ctr.ts:1025](https://github.com/maiyunnet/kebab/blob/master/sys
|
|
|
23066
23066
|
|
|
23067
23067
|
### \_loadData()
|
|
23068
23068
|
|
|
23069
|
-
> `protected` **\_loadData
|
|
23069
|
+
> `protected` **\_loadData**\<`T`\>(`path`): `Promise`\<`false` \| `T` \| `null`\>
|
|
23070
23070
|
|
|
23071
23071
|
Defined in: [sys/ctr.ts:864](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L864)
|
|
23072
23072
|
|
|
23073
|
-
获取 data 数据
|
|
23073
|
+
获取 data JSON 数据
|
|
23074
|
+
|
|
23075
|
+
#### Type Parameters
|
|
23076
|
+
|
|
23077
|
+
##### T
|
|
23078
|
+
|
|
23079
|
+
`T`
|
|
23074
23080
|
|
|
23075
23081
|
#### Parameters
|
|
23076
23082
|
|
|
@@ -23082,7 +23088,7 @@ Defined in: [sys/ctr.ts:864](https://github.com/maiyunnet/kebab/blob/master/sys/
|
|
|
23082
23088
|
|
|
23083
23089
|
#### Returns
|
|
23084
23090
|
|
|
23085
|
-
`Promise`\<`
|
|
23091
|
+
`Promise`\<`false` \| `T` \| `null`\>
|
|
23086
23092
|
|
|
23087
23093
|
***
|
|
23088
23094
|
|
|
@@ -23090,7 +23096,7 @@ Defined in: [sys/ctr.ts:864](https://github.com/maiyunnet/kebab/blob/master/sys/
|
|
|
23090
23096
|
|
|
23091
23097
|
> `protected` **\_loadLocale**(`loc`, `pkg?`): `Promise`\<`boolean`\>
|
|
23092
23098
|
|
|
23093
|
-
Defined in: [sys/ctr.ts:
|
|
23099
|
+
Defined in: [sys/ctr.ts:915](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L915)
|
|
23094
23100
|
|
|
23095
23101
|
设定语言并加载语言包
|
|
23096
23102
|
|
|
@@ -23211,7 +23217,7 @@ Defined in: [sys/ctr.ts:332](https://github.com/maiyunnet/kebab/blob/master/sys/
|
|
|
23211
23217
|
|
|
23212
23218
|
> `protected` **\_location**(`location`): `false`
|
|
23213
23219
|
|
|
23214
|
-
Defined in: [sys/ctr.ts:
|
|
23220
|
+
Defined in: [sys/ctr.ts:885](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L885)
|
|
23215
23221
|
|
|
23216
23222
|
跳转(302临时跳转),支持相对本项目根路径的路径或绝对路径
|
|
23217
23223
|
|
|
@@ -23233,7 +23239,7 @@ Defined in: [sys/ctr.ts:882](https://github.com/maiyunnet/kebab/blob/master/sys/
|
|
|
23233
23239
|
|
|
23234
23240
|
> `protected` **\_ping**(`data?`): `boolean`
|
|
23235
23241
|
|
|
23236
|
-
Defined in: [sys/ctr.ts:
|
|
23242
|
+
Defined in: [sys/ctr.ts:1078](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L1078)
|
|
23237
23243
|
|
|
23238
23244
|
发送 socket ping
|
|
23239
23245
|
|
|
@@ -23255,7 +23261,7 @@ Defined in: [sys/ctr.ts:1075](https://github.com/maiyunnet/kebab/blob/master/sys
|
|
|
23255
23261
|
|
|
23256
23262
|
> `protected` **\_pong**(`data?`): `boolean`
|
|
23257
23263
|
|
|
23258
|
-
Defined in: [sys/ctr.ts:
|
|
23264
|
+
Defined in: [sys/ctr.ts:1086](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L1086)
|
|
23259
23265
|
|
|
23260
23266
|
发送 socket pong
|
|
23261
23267
|
|
|
@@ -23277,7 +23283,7 @@ Defined in: [sys/ctr.ts:1083](https://github.com/maiyunnet/kebab/blob/master/sys
|
|
|
23277
23283
|
|
|
23278
23284
|
> `protected` **\_startSession**(`link`, `auth?`, `opt?`): `Promise`\<`boolean`\>
|
|
23279
23285
|
|
|
23280
|
-
Defined in: [sys/ctr.ts:
|
|
23286
|
+
Defined in: [sys/ctr.ts:899](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L899)
|
|
23281
23287
|
|
|
23282
23288
|
开启 Session
|
|
23283
23289
|
|
|
@@ -23311,7 +23317,7 @@ Kv 或 Db 实例
|
|
|
23311
23317
|
|
|
23312
23318
|
> `protected` **\_writeBinary**(`data`): `boolean`
|
|
23313
23319
|
|
|
23314
|
-
Defined in: [sys/ctr.ts:
|
|
23320
|
+
Defined in: [sys/ctr.ts:1070](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L1070)
|
|
23315
23321
|
|
|
23316
23322
|
发送 socket 二进制
|
|
23317
23323
|
|
|
@@ -23333,7 +23339,7 @@ Defined in: [sys/ctr.ts:1067](https://github.com/maiyunnet/kebab/blob/master/sys
|
|
|
23333
23339
|
|
|
23334
23340
|
> `protected` **\_writeResult**(`data`): `boolean`
|
|
23335
23341
|
|
|
23336
|
-
Defined in: [sys/ctr.ts:
|
|
23342
|
+
Defined in: [sys/ctr.ts:1062](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L1062)
|
|
23337
23343
|
|
|
23338
23344
|
发送结果对象文本
|
|
23339
23345
|
|
|
@@ -23355,7 +23361,7 @@ Defined in: [sys/ctr.ts:1059](https://github.com/maiyunnet/kebab/blob/master/sys
|
|
|
23355
23361
|
|
|
23356
23362
|
> `protected` **\_writeText**(`data`): `boolean`
|
|
23357
23363
|
|
|
23358
|
-
Defined in: [sys/ctr.ts:
|
|
23364
|
+
Defined in: [sys/ctr.ts:1054](https://github.com/maiyunnet/kebab/blob/master/sys/ctr.ts#L1054)
|
|
23359
23365
|
|
|
23360
23366
|
发送 socket 文本
|
|
23361
23367
|
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* --- 本文件用来定义每个目录实体地址的常量 ---
|
|
7
7
|
*/
|
|
8
8
|
/** --- 当前系统版本号 --- */
|
|
9
|
-
export const VER = '9.13.
|
|
9
|
+
export const VER = '9.13.6';
|
|
10
10
|
// --- 服务端用的路径 ---
|
|
11
11
|
const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
|
|
12
12
|
/** --- /xxx/xxx --- */
|
package/lib/undici/response.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export declare class Response {
|
|
|
31
31
|
/**
|
|
32
32
|
* --- 获取响应读取流对象 ---
|
|
33
33
|
*/
|
|
34
|
-
getStream():
|
|
34
|
+
getStream(): (import("undici/types/readable").default & undici.Dispatcher.BodyMixin) | zlib.Gunzip | zlib.Inflate | zlib.BrotliDecompress | null;
|
|
35
35
|
/**
|
|
36
36
|
* --- 获取原生响应读取流对象 ---
|
|
37
37
|
*/
|
package/package.json
CHANGED
package/sys/ctr.d.ts
CHANGED
|
@@ -246,10 +246,10 @@ export declare class Ctr {
|
|
|
246
246
|
'token': string;
|
|
247
247
|
} | false;
|
|
248
248
|
/**
|
|
249
|
-
* --- 获取 data 数据 ---
|
|
249
|
+
* --- 获取 data JSON 数据 ---
|
|
250
250
|
* @param path 文件路径(不含扩展名)
|
|
251
251
|
*/
|
|
252
|
-
protected _loadData(path: string): Promise<
|
|
252
|
+
protected _loadData<T>(path: string): Promise<T | false | null>;
|
|
253
253
|
/**
|
|
254
254
|
* --- 跳转(302临时跳转),支持相对本项目根路径的路径或绝对路径 ---
|
|
255
255
|
* @param location 相对或绝对网址
|
package/sys/ctr.js
CHANGED
|
@@ -695,7 +695,7 @@ export class Ctr {
|
|
|
695
695
|
return this._authorization;
|
|
696
696
|
}
|
|
697
697
|
/**
|
|
698
|
-
* --- 获取 data 数据 ---
|
|
698
|
+
* --- 获取 data JSON 数据 ---
|
|
699
699
|
* @param path 文件路径(不含扩展名)
|
|
700
700
|
*/
|
|
701
701
|
async _loadData(path) {
|
|
@@ -708,6 +708,9 @@ export class Ctr {
|
|
|
708
708
|
return null;
|
|
709
709
|
}
|
|
710
710
|
const json = lText.parseJson(content);
|
|
711
|
+
if (!json) {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
711
714
|
loadedData[realPath] = json;
|
|
712
715
|
return json;
|
|
713
716
|
}
|
|
@@ -747,7 +750,7 @@ export class Ctr {
|
|
|
747
750
|
if (!localeFiles.includes(lPath)) {
|
|
748
751
|
// --- 全局缓存没有,先加载全局缓存 ---
|
|
749
752
|
const locData = await this._loadData('locale/' + lName);
|
|
750
|
-
if (locData
|
|
753
|
+
if (!locData) {
|
|
751
754
|
return false;
|
|
752
755
|
}
|
|
753
756
|
this._locale = loc;
|
package/sys/route.js
CHANGED
|
@@ -902,7 +902,7 @@ export function getPost(req) {
|
|
|
902
902
|
* @param limits 文件上传限制
|
|
903
903
|
*/
|
|
904
904
|
export function getFormData(req, events = {}, limits = {}) {
|
|
905
|
-
return new Promise(
|
|
905
|
+
return new Promise(resolve => {
|
|
906
906
|
if (req.readableEnded) {
|
|
907
907
|
resolve({ 'post': {}, 'files': {} });
|
|
908
908
|
return;
|
|
@@ -954,6 +954,38 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
954
954
|
let writeFileLength = 0;
|
|
955
955
|
/** --- 当前读取是否已经完全结束 --- */
|
|
956
956
|
let readEnd = false;
|
|
957
|
+
/** --- 是否有文件被限制拒绝(整体返回 false) --- */
|
|
958
|
+
let rejected = false;
|
|
959
|
+
/** --- 清理 rtn.files 中所有已写入的临时文件 --- */
|
|
960
|
+
function cleanupFiles() {
|
|
961
|
+
for (const key in rtn.files) {
|
|
962
|
+
let files = rtn.files[key];
|
|
963
|
+
if (!Array.isArray(files)) {
|
|
964
|
+
files = [files];
|
|
965
|
+
}
|
|
966
|
+
for (const file of files) {
|
|
967
|
+
lFs.unlink(file.path).catch(() => { });
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
/** --- 拒绝当前文件:销毁流、删临时文件、标记 rejected --- */
|
|
972
|
+
function rejectFile() {
|
|
973
|
+
rejected = true;
|
|
974
|
+
ftmpStream.destroy();
|
|
975
|
+
lFs.unlink(kebab.FTMP_CWD + ftmpName).catch(() => { });
|
|
976
|
+
ftmpName = '';
|
|
977
|
+
--writeFileLength;
|
|
978
|
+
}
|
|
979
|
+
/** --- 最终输出 --- */
|
|
980
|
+
function finalize() {
|
|
981
|
+
if (rejected) {
|
|
982
|
+
cleanupFiles();
|
|
983
|
+
resolve(false);
|
|
984
|
+
}
|
|
985
|
+
else {
|
|
986
|
+
resolve(rtn);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
957
989
|
// --- 开始读取 ---
|
|
958
990
|
req.on('data', function (chunk) {
|
|
959
991
|
buffer = Buffer.concat([buffer, chunk], buffer.length + chunk.length);
|
|
@@ -983,12 +1015,18 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
983
1015
|
++writeFileLength;
|
|
984
1016
|
state = EState.FILE;
|
|
985
1017
|
fileName = match[1];
|
|
1018
|
+
// --- 已被拒绝则不创建文件流,仅丢弃数据 ---
|
|
1019
|
+
if (rejected) {
|
|
1020
|
+
ftmpName = '';
|
|
1021
|
+
break;
|
|
1022
|
+
}
|
|
986
1023
|
// --- 检查文件扩展名限制 ---
|
|
987
1024
|
if (limits.allowedExts?.length) {
|
|
988
1025
|
const extIo = fileName.lastIndexOf('.');
|
|
989
1026
|
const ext = extIo !== -1 ? fileName.slice(extIo).toLowerCase() : '';
|
|
990
1027
|
if (!limits.allowedExts.includes(ext)) {
|
|
991
|
-
// ---
|
|
1028
|
+
// --- 扩展名不允许,拒绝整体上传 ---
|
|
1029
|
+
rejected = true;
|
|
992
1030
|
ftmpName = '';
|
|
993
1031
|
break;
|
|
994
1032
|
}
|
|
@@ -1003,6 +1041,7 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1003
1041
|
date.getUTCHours().toString().padStart(2, '0') +
|
|
1004
1042
|
date.getUTCMinutes().toString().padStart(2, '0') + '_' + lCore.random() + '.ftmp';
|
|
1005
1043
|
ftmpStream = lFs.createWriteStream(kebab.FTMP_CWD + ftmpName);
|
|
1044
|
+
ftmpStream.on('error', () => { });
|
|
1006
1045
|
ftmpSize = 0;
|
|
1007
1046
|
}
|
|
1008
1047
|
else {
|
|
@@ -1042,22 +1081,23 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1042
1081
|
// --- 没找到结束标语,将预留 boundary 长度之前的写入到文件 ---
|
|
1043
1082
|
const writeBuffer = buffer.subarray(0, -boundary.length - 4);
|
|
1044
1083
|
if (ftmpName) {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
ftmpSize += Buffer.byteLength(writeBuffer);
|
|
1084
|
+
if (writeBuffer.length > 0) {
|
|
1085
|
+
// --- 检查文件大小限制 ---
|
|
1086
|
+
if (limits.maxFileSize &&
|
|
1087
|
+
(ftmpSize + Buffer.byteLength(writeBuffer) > limits.maxFileSize)) {
|
|
1088
|
+
rejectFile();
|
|
1089
|
+
}
|
|
1090
|
+
else {
|
|
1091
|
+
ftmpStream.write(writeBuffer);
|
|
1092
|
+
ftmpSize += Buffer.byteLength(writeBuffer);
|
|
1093
|
+
}
|
|
1056
1094
|
}
|
|
1057
1095
|
}
|
|
1058
1096
|
else {
|
|
1059
1097
|
// --- 跳过该文件 ---
|
|
1060
|
-
|
|
1098
|
+
if (writeBuffer.length > 0) {
|
|
1099
|
+
events.onfiledata?.(writeBuffer);
|
|
1100
|
+
}
|
|
1061
1101
|
}
|
|
1062
1102
|
buffer = buffer.subarray(-boundary.length - 4);
|
|
1063
1103
|
return;
|
|
@@ -1065,44 +1105,51 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1065
1105
|
// --- 找到结束标语,结束标语之前的写入文件,之后的重新放回 buffer ---
|
|
1066
1106
|
const writeBuffer = buffer.subarray(0, io);
|
|
1067
1107
|
if (ftmpName) {
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
if (!readEnd) {
|
|
1073
|
-
// --- request 没读完,不管 ---
|
|
1074
|
-
return;
|
|
1075
|
-
}
|
|
1076
|
-
if (writeFileLength) {
|
|
1077
|
-
// --- req 读完了但文件还没写完,不管 ---
|
|
1078
|
-
return;
|
|
1079
|
-
}
|
|
1080
|
-
// --- 文件也写完了 ---
|
|
1081
|
-
resolve(rtn);
|
|
1082
|
-
});
|
|
1083
|
-
// --- POST 部分 ---
|
|
1084
|
-
let fname = fileName.replace(/\\/g, '/');
|
|
1085
|
-
const nlio = fname.lastIndexOf('/');
|
|
1086
|
-
if (nlio !== -1) {
|
|
1087
|
-
fname = fname.slice(nlio + 1);
|
|
1108
|
+
// --- 检查文件大小限制(最后一个分片) ---
|
|
1109
|
+
if (limits.maxFileSize &&
|
|
1110
|
+
(ftmpSize + Buffer.byteLength(writeBuffer) > limits.maxFileSize)) {
|
|
1111
|
+
rejectFile();
|
|
1088
1112
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1113
|
+
else {
|
|
1114
|
+
ftmpStream.write(writeBuffer);
|
|
1115
|
+
ftmpSize += Buffer.byteLength(writeBuffer);
|
|
1116
|
+
ftmpStream.end(() => {
|
|
1117
|
+
--writeFileLength;
|
|
1118
|
+
if (!readEnd) {
|
|
1119
|
+
// --- request 没读完,不管 ---
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
if (writeFileLength) {
|
|
1123
|
+
// --- req 读完了但文件还没写完,不管 ---
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
// --- 文件也写完了 ---
|
|
1127
|
+
finalize();
|
|
1128
|
+
});
|
|
1129
|
+
// --- POST 部分 ---
|
|
1130
|
+
let fname = fileName.replace(/\\/g, '/');
|
|
1131
|
+
const nlio = fname.lastIndexOf('/');
|
|
1132
|
+
if (nlio !== -1) {
|
|
1133
|
+
fname = fname.slice(nlio + 1);
|
|
1134
|
+
}
|
|
1135
|
+
const val = {
|
|
1136
|
+
'name': fname,
|
|
1137
|
+
'origin': fileName,
|
|
1138
|
+
'size': ftmpSize,
|
|
1139
|
+
'path': kebab.FTMP_CWD + ftmpName
|
|
1140
|
+
};
|
|
1141
|
+
if (rtn.files[name]) {
|
|
1142
|
+
if (Array.isArray(rtn.files[name])) {
|
|
1143
|
+
rtn.files[name].push(val);
|
|
1144
|
+
}
|
|
1145
|
+
else {
|
|
1146
|
+
rtn.files[name] = [rtn.files[name], val];
|
|
1147
|
+
}
|
|
1098
1148
|
}
|
|
1099
1149
|
else {
|
|
1100
|
-
rtn.files[name] =
|
|
1150
|
+
rtn.files[name] = val;
|
|
1101
1151
|
}
|
|
1102
1152
|
}
|
|
1103
|
-
else {
|
|
1104
|
-
rtn.files[name] = val;
|
|
1105
|
-
}
|
|
1106
1153
|
}
|
|
1107
1154
|
else {
|
|
1108
1155
|
// --- 跳过该文件 ---
|
|
@@ -1114,6 +1161,10 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1114
1161
|
break;
|
|
1115
1162
|
}
|
|
1116
1163
|
}
|
|
1164
|
+
// --- 被拒绝则丢弃剩余数据,等待 'end' 事件后 resolve ---
|
|
1165
|
+
if (rejected) {
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1117
1168
|
}
|
|
1118
1169
|
});
|
|
1119
1170
|
req.on('error', function (e) {
|
|
@@ -1122,17 +1173,23 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1122
1173
|
}
|
|
1123
1174
|
lCore.debug('[ROUTE][GETFORMDATA] request error before getFormData: ' + e.message);
|
|
1124
1175
|
lCore.log({}, '[ROUTE][GETFORMDATA] request error before getFormData: ' + (e.stack ?? ''), '-error');
|
|
1176
|
+
cleanupFiles();
|
|
1125
1177
|
resolve(false);
|
|
1126
1178
|
});
|
|
1127
1179
|
req.on('end', function () {
|
|
1128
1180
|
readEnd = true;
|
|
1181
|
+
// --- 被拒绝则清理文件并 resolve(false) ---
|
|
1182
|
+
if (rejected) {
|
|
1183
|
+
finalize();
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1129
1186
|
if (state === EState.FILE) {
|
|
1130
1187
|
if (ftmpName) {
|
|
1131
1188
|
ftmpStream.end(() => {
|
|
1132
1189
|
--writeFileLength;
|
|
1133
1190
|
if (!writeFileLength) {
|
|
1134
1191
|
// --- 文件也写完了 ---
|
|
1135
|
-
|
|
1192
|
+
finalize();
|
|
1136
1193
|
}
|
|
1137
1194
|
});
|
|
1138
1195
|
return;
|
|
@@ -1146,7 +1203,7 @@ export function getFormData(req, events = {}, limits = {}) {
|
|
|
1146
1203
|
return;
|
|
1147
1204
|
}
|
|
1148
1205
|
// --- 文件写完了 ----
|
|
1149
|
-
|
|
1206
|
+
finalize();
|
|
1150
1207
|
});
|
|
1151
1208
|
});
|
|
1152
1209
|
}
|
|
@@ -64,33 +64,6 @@ export default class extends sCtr.Ctr {
|
|
|
64
64
|
private _dbTable;
|
|
65
65
|
vector(): Promise<any>;
|
|
66
66
|
kv(): Promise<kebab.Json>;
|
|
67
|
-
net(): Promise<string>;
|
|
68
|
-
netPipe(): Promise<kebab.Json>;
|
|
69
|
-
netPost(): Promise<kebab.Json>;
|
|
70
|
-
netPost1(): string;
|
|
71
|
-
netPostString(): Promise<string>;
|
|
72
|
-
netPostString1(): kebab.Json[];
|
|
73
|
-
netOpen(): Promise<kebab.Json>;
|
|
74
|
-
netFormTest(): Promise<string>;
|
|
75
|
-
netUpload(): Promise<string>;
|
|
76
|
-
netUpload1(): Promise<string>;
|
|
77
|
-
netCookie(): Promise<string>;
|
|
78
|
-
netCookie1(): string;
|
|
79
|
-
netCookie2(): string;
|
|
80
|
-
netSave(): Promise<string>;
|
|
81
|
-
netFollow(): Promise<string>;
|
|
82
|
-
netFollow1(): void;
|
|
83
|
-
netFollow2(): kebab.Json;
|
|
84
|
-
netReuse(): Promise<string>;
|
|
85
|
-
netError(): Promise<string>;
|
|
86
|
-
netHosts(): Promise<string>;
|
|
87
|
-
netMproxy(): Promise<string | boolean>;
|
|
88
|
-
netMproxy1(): Promise<string | boolean>;
|
|
89
|
-
netMproxy2(): any[];
|
|
90
|
-
netFilterheaders(): string;
|
|
91
|
-
netFetch(): Promise<string | boolean>;
|
|
92
|
-
netFetch1(): any[];
|
|
93
|
-
netGetResponseJson(): Promise<string>;
|
|
94
67
|
getResponseJson1(): any[];
|
|
95
68
|
getResponseJson2(): any[];
|
|
96
69
|
undici(): Promise<string>;
|
|
@@ -192,6 +165,14 @@ export default class extends sCtr.Ctr {
|
|
|
192
165
|
* --- CORS 精细化配置测试 ---
|
|
193
166
|
*/
|
|
194
167
|
ctrCrossFine(): string;
|
|
168
|
+
/**
|
|
169
|
+
* --- 文件上传限制演示页面 ---
|
|
170
|
+
*/
|
|
171
|
+
ctrUploadLimits(): string;
|
|
172
|
+
/**
|
|
173
|
+
* --- 文件上传限制演示接口 ---
|
|
174
|
+
*/
|
|
175
|
+
ctrUploadLimits1(): Promise<kebab.Json[]>;
|
|
195
176
|
/**
|
|
196
177
|
* --- END ---
|
|
197
178
|
*/
|
package/www/example/ctr/test.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// --- 库 ---
|
|
2
2
|
import * as kebab from '#kebab/index.js';
|
|
3
3
|
import * as lCore from '#kebab/lib/core.js';
|
|
4
|
-
import * as lNet from '#kebab/lib/net.js';
|
|
5
4
|
import * as lUndici from '#kebab/lib/undici.js';
|
|
6
5
|
import * as lDb from '#kebab/lib/db.js';
|
|
7
6
|
import * as lVector from '#kebab/lib/vector.js';
|
|
@@ -126,6 +125,7 @@ export default class extends sCtr.Ctr {
|
|
|
126
125
|
`<br><a href="${this._config.const.urlBase}test/ctr-timeout-short">View "test/ctr-timeout-short"</a>`,
|
|
127
126
|
`<br><a href="${this._config.const.urlBase}test/ctr-session-token">View "test/ctr-session-token"</a>`,
|
|
128
127
|
`<br><a href="${this._config.const.urlBase}test/ctr-500">View "test/ctr-500"</a>`,
|
|
128
|
+
`<br><a href="${this._config.const.urlBase}test/ctr-upload-limits">View "test/ctr-upload-limits" (File upload limits demo)</a>`,
|
|
129
129
|
'<br><br><b>Middle:</b>',
|
|
130
130
|
`<br><br><a href="${this._config.const.urlBase}test/middle">View "test/middle"</a>`,
|
|
131
131
|
'<br><br><b>Model test:</b>',
|
|
@@ -171,25 +171,6 @@ export default class extends sCtr.Ctr {
|
|
|
171
171
|
`<br><br><a href="${this._config.const.urlBase}test/vector">View "test/vector"</a>`,
|
|
172
172
|
'<br><br><b>Kv:</b>',
|
|
173
173
|
`<br><br><a href="${this._config.const.urlBase}test/kv">View "test/kv"</a>`,
|
|
174
|
-
'<br><br><b>Net:</b>',
|
|
175
|
-
`<br><br><a href="${this._config.const.urlBase}test/net">View "test/net"</a>`,
|
|
176
|
-
`<br><a href="${this._config.const.urlBase}test/net-pipe">View "test/net-pipe"</a>`,
|
|
177
|
-
`<br><a href="${this._config.const.urlBase}test/net-post">View "test/net-post"</a>`,
|
|
178
|
-
`<br><a href="${this._config.const.urlBase}test/net-post-string">View "test/net-post-string"</a>`,
|
|
179
|
-
`<br><a href="${this._config.const.urlBase}test/net-open">View "test/net-open"</a>`,
|
|
180
|
-
`<br><a href="${this._config.const.urlBase}test/net-form-test">View "test/net-form-test"</a>`,
|
|
181
|
-
`<br><a href="${this._config.const.urlBase}test/net-upload">View "test/net-upload"</a>`,
|
|
182
|
-
`<br><a href="${this._config.const.urlBase}test/net-cookie">View "test/net-cookie"</a>`,
|
|
183
|
-
`<br><a href="${this._config.const.urlBase}test/net-save">View "test/net-save"</a>`,
|
|
184
|
-
`<br><a href="${this._config.const.urlBase}test/net-follow">View "test/net-follow"</a>`,
|
|
185
|
-
`<br><a href="${this._config.const.urlBase}test/net-reuse">View "test/net-reuse"</a>`,
|
|
186
|
-
`<br><a href="${this._config.const.urlBase}test/net-error">View "test/net-error"</a>`,
|
|
187
|
-
`<br><a href="${this._config.const.urlBase}test/net-hosts">View "test/net-hosts"</a>`,
|
|
188
|
-
`<br><a href="${this._config.const.urlBase}test/net-rproxy/dist/core.js">View "test/net-rproxy/dist/core.js"</a> <a href="${this._config.const.urlBase}test/net-rproxy/package.json">View "package.json"</a>`,
|
|
189
|
-
`<br><a href="${this._config.const.urlBase}test/net-mproxy">View "test/net-mproxy"</a>`,
|
|
190
|
-
`<br><a href="${this._config.const.urlBase}test/net-filterheaders">View "test/net-filterheaders"</a>`,
|
|
191
|
-
`<br><a href="${this._config.const.urlBase}test/net-fetch">View "test/net-fetch"</a>`,
|
|
192
|
-
`<br><a href="${this._config.const.urlBase}test/net-get-response-json">View "test/net-get-response-json"</a>`,
|
|
193
174
|
'<br><br><b>Undici:</b>',
|
|
194
175
|
`<br><br><a href="${this._config.const.urlBase}test/undici">View "test/undici"</a>`,
|
|
195
176
|
`<br><a href="${this._config.const.urlBase}test/undici-pipe">View "test/undici-pipe"</a>`,
|
|
@@ -1882,481 +1863,6 @@ echo[echo.length - 1] = echo[echo.length - 1].slice(0, -4);</pre>`);
|
|
|
1882
1863
|
'<a href="' + this._config.const.urlBase + 'test/kv?ac=other">Other</a> | ' +
|
|
1883
1864
|
'<a href="' + this._config.const.urlBase + 'test">Return</a>' + echo.join('') + '<br><br>' + this._getEnd();
|
|
1884
1865
|
}
|
|
1885
|
-
async net() {
|
|
1886
|
-
const echo = [];
|
|
1887
|
-
const res = await lNet.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/package.json');
|
|
1888
|
-
echo.push(`<pre>Net.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/package.json');</pre>
|
|
1889
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
1890
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
|
|
1891
|
-
error: ${JSON.stringify(res.error)}`);
|
|
1892
|
-
const res2 = await lNet.get(this._internalUrl + 'test?abc');
|
|
1893
|
-
echo.push(`<pre>Net.get('${this._internalUrl}test?abc');</pre>
|
|
1894
|
-
headers: <pre>${JSON.stringify(res2.headers, null, 4)}</pre>
|
|
1895
|
-
content: <pre>${(await res2.getContent())?.toString().slice(0, 500) ?? 'null'}</pre>
|
|
1896
|
-
error: ${JSON.stringify(res2.error)}`);
|
|
1897
|
-
return echo.join('') + '<br><br>' + this._getEnd();
|
|
1898
|
-
}
|
|
1899
|
-
async netPipe() {
|
|
1900
|
-
const echo = [];
|
|
1901
|
-
const res = await lNet.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/package.json');
|
|
1902
|
-
echo.push(`<pre>Net.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/package.json');</pre>
|
|
1903
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
1904
|
-
content: <pre>`, res, `</pre>
|
|
1905
|
-
error: ${JSON.stringify(res.error)}
|
|
1906
|
-
<br><br>` + this._getEnd());
|
|
1907
|
-
return echo;
|
|
1908
|
-
}
|
|
1909
|
-
async netPost() {
|
|
1910
|
-
const echo = [];
|
|
1911
|
-
const res = await lNet.post(this._internalUrl + 'test/netPost1', { 'a': '1', 'b': '2', 'c': ['1', '2', '3'] });
|
|
1912
|
-
echo.push(`<pre>lNet.post('${this._internalUrl}test/netPost1', { 'a': '1', 'b': '2', 'c': ['1', '2', '3'] });</pre>
|
|
1913
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
1914
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
|
|
1915
|
-
error: ${JSON.stringify(res.error)}`);
|
|
1916
|
-
return echo.join('') + '<br><br>' + this._getEnd();
|
|
1917
|
-
}
|
|
1918
|
-
netPost1() {
|
|
1919
|
-
return `_post:\n\n${JSON.stringify(this._post)}\n\nRequest headers:\n\n${JSON.stringify(this._headers, null, 4)}\n\nIP: ${this._req.socket.remoteAddress ?? ''}`;
|
|
1920
|
-
}
|
|
1921
|
-
async netPostString() {
|
|
1922
|
-
const echo = [];
|
|
1923
|
-
const res = await lNet.post(this._internalUrl + 'test/netPostString1', 'HeiHei');
|
|
1924
|
-
echo.push(`<pre>lNet.post('${this._internalUrl}test/netPostString1', 'HeiHei');</pre>
|
|
1925
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
1926
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
|
|
1927
|
-
error: ${JSON.stringify(res.error)}`);
|
|
1928
|
-
return echo.join('') + '<br><br>' + this._getEnd();
|
|
1929
|
-
}
|
|
1930
|
-
netPostString1() {
|
|
1931
|
-
return [1, this._input];
|
|
1932
|
-
}
|
|
1933
|
-
async netOpen() {
|
|
1934
|
-
const echo = [];
|
|
1935
|
-
const res = await lNet.open(this._internalUrl + 'test/netPost1').post().data({ 'a': '2', 'b': '0', 'c': ['0', '1', '3'] }).request();
|
|
1936
|
-
echo.push(`<pre>lNet.open('${this._internalUrl}test/netPost1').post().data({ 'a': '2', 'b': '0', 'c': ['0', '1', '3'] }).request();</pre>
|
|
1937
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
1938
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
|
|
1939
|
-
error: ${JSON.stringify(res.error)}`);
|
|
1940
|
-
return echo.join('') + this._getEnd();
|
|
1941
|
-
}
|
|
1942
|
-
async netFormTest() {
|
|
1943
|
-
if (!await this._handleFormData()) {
|
|
1944
|
-
return '';
|
|
1945
|
-
}
|
|
1946
|
-
const echo = [
|
|
1947
|
-
'<pre>',
|
|
1948
|
-
JSON.stringify(this._post, null, 4),
|
|
1949
|
-
'\n-----\n',
|
|
1950
|
-
JSON.stringify(this._files, null, 4),
|
|
1951
|
-
'</pre>'
|
|
1952
|
-
];
|
|
1953
|
-
echo.push(`<form enctype="multipart/form-data" method="post">
|
|
1954
|
-
text a: <input type="text" name="a" value="a1"> <input type="text" name="a" value="a2"><br>
|
|
1955
|
-
file b: <input type="file" name="b"><br>
|
|
1956
|
-
file c: <input type="file" name="c"><input type="file" name="c"><br>
|
|
1957
|
-
fi d[]: <input type="file" name="d[]"><input type="file" name="d[]"><br>
|
|
1958
|
-
<input type="submit" value="Upload">
|
|
1959
|
-
</form>
|
|
1960
|
-
<hr>
|
|
1961
|
-
<form method="post">
|
|
1962
|
-
name a: <input type="text" name="a" value="a&1"> <input type="text" name="a" value="a&2"><br>
|
|
1963
|
-
na b[]: <input type="text" name="b[]" value="b1"> <input type="text" name="b[]" value="b2"><br>
|
|
1964
|
-
name d: <input type="text" name="d" value="d"><br>
|
|
1965
|
-
<input type="submit" value="Default post">
|
|
1966
|
-
</form>`);
|
|
1967
|
-
return echo.join('') + this._getEnd();
|
|
1968
|
-
}
|
|
1969
|
-
async netUpload() {
|
|
1970
|
-
const echo = [];
|
|
1971
|
-
const fd = lNet.getFormData();
|
|
1972
|
-
fd.putString('a', '1');
|
|
1973
|
-
await fd.putFile('file', kebab.LIB_PATH + 'net/cacert.pem');
|
|
1974
|
-
await fd.putFile('multiple', kebab.LIB_PATH + 'net/cacert.pem');
|
|
1975
|
-
await fd.putFile('multiple', kebab.LIB_PATH + 'net/cacert.pem');
|
|
1976
|
-
const res = await lNet.post(this._internalUrl + 'test/net-upload1', fd);
|
|
1977
|
-
echo.push(`<pre>const fd = lNet.getFormData();
|
|
1978
|
-
fd.putString('a', '1');
|
|
1979
|
-
await fd.putFile('file', def.LIB_PATH + 'net/cacert.pem');
|
|
1980
|
-
await fd.putFile('multiple', def.LIB_PATH + 'net/cacert.pem');
|
|
1981
|
-
await fd.putFile('multiple', def.LIB_PATH + 'net/cacert.pem');
|
|
1982
|
-
lNet.post('${this._internalUrl}test/net-upload1', fd);</pre>
|
|
1983
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
1984
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
|
|
1985
|
-
error: ${JSON.stringify(res.error)}`);
|
|
1986
|
-
return echo.join('') + '<br><br>' + this._getEnd();
|
|
1987
|
-
}
|
|
1988
|
-
async netUpload1() {
|
|
1989
|
-
if (!await this._handleFormData()) {
|
|
1990
|
-
return '{}';
|
|
1991
|
-
}
|
|
1992
|
-
return JSON.stringify(this._post, null, 4) + '\n\n' + JSON.stringify(this._files, null, 4);
|
|
1993
|
-
}
|
|
1994
|
-
async netCookie() {
|
|
1995
|
-
const echo = [];
|
|
1996
|
-
const cookie = {};
|
|
1997
|
-
let res = await lNet.get(this._internalUrl + 'test/net-cookie1', {
|
|
1998
|
-
'cookie': cookie
|
|
1999
|
-
});
|
|
2000
|
-
echo.push(`<pre>const cookie = {};
|
|
2001
|
-
lNet.get(this._internalUrl + 'test/net-cookie1', {
|
|
2002
|
-
'cookie': cookie
|
|
2003
|
-
});</pre>
|
|
2004
|
-
headers: <pre>${lText.htmlescape(JSON.stringify(res.headers, null, 4))}</pre>
|
|
2005
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
|
|
2006
|
-
cookie: <pre>${JSON.stringify(cookie, null, 4)}</pre><hr>`);
|
|
2007
|
-
res = await lNet.get(this._internalUrl + 'test/net-cookie2', {
|
|
2008
|
-
'cookie': cookie
|
|
2009
|
-
});
|
|
2010
|
-
echo.push(`<pre>lNet.get(this._internalUrl + 'test/net-cookie2', {
|
|
2011
|
-
'cookie': cookie
|
|
2012
|
-
});</pre>
|
|
2013
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
2014
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>`);
|
|
2015
|
-
lCookie.setCookie(cookie, 'custom1', 'abc1', this._config.const.host);
|
|
2016
|
-
lCookie.setCookie(cookie, 'custom2', 'abc2', '172.17.0.1');
|
|
2017
|
-
res = await lNet.get(this._internalUrl + 'test/net-cookie2', {
|
|
2018
|
-
'cookie': cookie
|
|
2019
|
-
});
|
|
2020
|
-
echo.push(`<pre>lCookie.setCookie(cookie, 'custom1', 'abc1', ${this._config.const.host});
|
|
2021
|
-
lCookie.setCookie(cookie, 'custom2', 'abc2', '172.17.0.1');
|
|
2022
|
-
lNet.get(this._internalUrl + 'test/net-cookie2', {
|
|
2023
|
-
'cookie': cookie
|
|
2024
|
-
});</pre>
|
|
2025
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
2026
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>`);
|
|
2027
|
-
return echo.join('') + this._getEnd();
|
|
2028
|
-
}
|
|
2029
|
-
netCookie1() {
|
|
2030
|
-
lCore.setCookie(this, 'test0', 'session');
|
|
2031
|
-
lCore.setCookie(this, 'test1', 'normal', {
|
|
2032
|
-
'ttl': 10
|
|
2033
|
-
});
|
|
2034
|
-
lCore.setCookie(this, 'test2', 'baidu.com', {
|
|
2035
|
-
'ttl': 20,
|
|
2036
|
-
'path': '/',
|
|
2037
|
-
'domain': 'baidu.com'
|
|
2038
|
-
});
|
|
2039
|
-
lCore.setCookie(this, 'test3', this._config.const.hostname, {
|
|
2040
|
-
'ttl': 30,
|
|
2041
|
-
'path': '/',
|
|
2042
|
-
'domain': this._config.const.hostname
|
|
2043
|
-
});
|
|
2044
|
-
lCore.setCookie(this, 'test4', '/ok/', {
|
|
2045
|
-
'ttl': 40,
|
|
2046
|
-
'path': '/ok/'
|
|
2047
|
-
});
|
|
2048
|
-
lCore.setCookie(this, 'test5', 'secure', {
|
|
2049
|
-
'ttl': 50,
|
|
2050
|
-
'ssl': true
|
|
2051
|
-
});
|
|
2052
|
-
lCore.setCookie(this, 'test6', '0.1', {
|
|
2053
|
-
'ttl': 40,
|
|
2054
|
-
'path': '/',
|
|
2055
|
-
'domain': '0.1'
|
|
2056
|
-
});
|
|
2057
|
-
lCore.setCookie(this, 'test7', 'localhost', {
|
|
2058
|
-
'ttl': 30,
|
|
2059
|
-
'path': '/',
|
|
2060
|
-
'domain': 'localhost'
|
|
2061
|
-
});
|
|
2062
|
-
lCore.setCookie(this, 'test8', 'com', {
|
|
2063
|
-
'ttl': 20,
|
|
2064
|
-
'path': '/',
|
|
2065
|
-
'domain': 'com'
|
|
2066
|
-
});
|
|
2067
|
-
lCore.setCookie(this, 'test9', 'com.cn', {
|
|
2068
|
-
'ttl': 10,
|
|
2069
|
-
'path': '/',
|
|
2070
|
-
'domain': 'com.cn'
|
|
2071
|
-
});
|
|
2072
|
-
lCore.setCookie(this, 'test10', 'httponly', {
|
|
2073
|
-
'ttl': 60,
|
|
2074
|
-
'httponly': true
|
|
2075
|
-
});
|
|
2076
|
-
// --- 用于测试 BUG 的额外 Cookie ---
|
|
2077
|
-
this._res.setHeader('Set-Cookie', [
|
|
2078
|
-
...this._res.getHeader('Set-Cookie'),
|
|
2079
|
-
'test-del=; Max-Age=0; Path=/',
|
|
2080
|
-
'test-equals=val=with=equals; Path=/',
|
|
2081
|
-
'test-expires=val; Expires=Wed, 21 Oct 2035 07:28:00 GMT; Path=/',
|
|
2082
|
-
'test-invalid=%invalid; Path=/'
|
|
2083
|
-
]);
|
|
2084
|
-
return `lCore.setCookie(this, 'test0', 'session');
|
|
2085
|
-
lCore.setCookie(this, 'test1', 'normal', {
|
|
2086
|
-
'ttl': 10
|
|
2087
|
-
});
|
|
2088
|
-
lCore.setCookie(this, 'test2', 'baidu.com', {
|
|
2089
|
-
'ttl': 20,
|
|
2090
|
-
'path': '/',
|
|
2091
|
-
'domain': 'baidu.com'
|
|
2092
|
-
});
|
|
2093
|
-
lCore.setCookie(this, 'test3', this._config.const.hostname, {
|
|
2094
|
-
'ttl': 30,
|
|
2095
|
-
'path': '/',
|
|
2096
|
-
'domain': this._config.const.hostname
|
|
2097
|
-
});
|
|
2098
|
-
lCore.setCookie(this, 'test4', '/ok/', {
|
|
2099
|
-
'ttl': 40,
|
|
2100
|
-
'path': '/ok/'
|
|
2101
|
-
});
|
|
2102
|
-
lCore.setCookie(this, 'test5', 'secure', {
|
|
2103
|
-
'ttl': 50,
|
|
2104
|
-
'ssl': true
|
|
2105
|
-
});
|
|
2106
|
-
lCore.setCookie(this, 'test6', '0.1', {
|
|
2107
|
-
'ttl': 40,
|
|
2108
|
-
'path': '/',
|
|
2109
|
-
'domain': '0.1'
|
|
2110
|
-
});
|
|
2111
|
-
lCore.setCookie(this, 'test7', 'localhost', {
|
|
2112
|
-
'ttl': 30,
|
|
2113
|
-
'path': '/',
|
|
2114
|
-
'domain': 'localhost'
|
|
2115
|
-
});
|
|
2116
|
-
lCore.setCookie(this, 'test8', 'com', {
|
|
2117
|
-
'ttl': 20,
|
|
2118
|
-
'path': '/',
|
|
2119
|
-
'domain': 'com'
|
|
2120
|
-
});
|
|
2121
|
-
lCore.setCookie(this, 'test9', 'com.cn', {
|
|
2122
|
-
'ttl': 10,
|
|
2123
|
-
'path': '/',
|
|
2124
|
-
'domain': 'com.cn'
|
|
2125
|
-
});
|
|
2126
|
-
lCore.setCookie(this, 'test10', 'httponly', {
|
|
2127
|
-
'ttl': 60,
|
|
2128
|
-
'httponly': true
|
|
2129
|
-
});`;
|
|
2130
|
-
}
|
|
2131
|
-
netCookie2() {
|
|
2132
|
-
return 'this._cookie: \n\n' + JSON.stringify(this._cookie, null, 4);
|
|
2133
|
-
}
|
|
2134
|
-
async netSave() {
|
|
2135
|
-
const echo = [];
|
|
2136
|
-
const res = await lNet.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/package.json', {
|
|
2137
|
-
'follow': 5,
|
|
2138
|
-
'save': kebab.LOG_CWD + 'test-must-remove.json'
|
|
2139
|
-
});
|
|
2140
|
-
echo.push(`<pre>lNet.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/package.json', {
|
|
2141
|
-
'follow': 5,
|
|
2142
|
-
'save': def.LOG_PATH + 'test-must-remove.json'
|
|
2143
|
-
});</pre>
|
|
2144
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
2145
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
|
|
2146
|
-
error: ${JSON.stringify(res.error)}`);
|
|
2147
|
-
return echo.join('') + this._getEnd();
|
|
2148
|
-
}
|
|
2149
|
-
async netFollow() {
|
|
2150
|
-
const echo = [];
|
|
2151
|
-
const res = await lNet.post(this._internalUrl + 'test/net-follow1', {
|
|
2152
|
-
'a': '1',
|
|
2153
|
-
'b': '2'
|
|
2154
|
-
}, {
|
|
2155
|
-
'follow': 5
|
|
2156
|
-
});
|
|
2157
|
-
echo.push(`<pre>lNet.post(this._internalUrl + 'test/net-follow1', {
|
|
2158
|
-
'a': '1',
|
|
2159
|
-
'b': '2'
|
|
2160
|
-
}, {
|
|
2161
|
-
'follow': 5
|
|
2162
|
-
});</pre>
|
|
2163
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
2164
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
|
|
2165
|
-
error: ${JSON.stringify(res.error)}</pre>`);
|
|
2166
|
-
return echo.join('') + this._getEnd();
|
|
2167
|
-
}
|
|
2168
|
-
netFollow1() {
|
|
2169
|
-
this._location('test/net-follow2');
|
|
2170
|
-
}
|
|
2171
|
-
netFollow2() {
|
|
2172
|
-
return [1, { 'post': this._post['a'] + ',' + this._post['b'] }];
|
|
2173
|
-
}
|
|
2174
|
-
async netReuse() {
|
|
2175
|
-
const echo = [];
|
|
2176
|
-
echo.push('<strong>Reuse:</strong>');
|
|
2177
|
-
let time0 = Date.now();
|
|
2178
|
-
await lNet.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/package.json');
|
|
2179
|
-
let time1 = Date.now();
|
|
2180
|
-
echo.push("<pre>lNet.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/package.json');</pre>" + Math.round(time1 - time0).toString() + 'ms.');
|
|
2181
|
-
time0 = Date.now();
|
|
2182
|
-
await lNet.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/README.md');
|
|
2183
|
-
time1 = Date.now();
|
|
2184
|
-
echo.push("<pre>lNet.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/README.md');</pre>" + Math.round(time1 - time0).toString() + 'ms.');
|
|
2185
|
-
time0 = Date.now();
|
|
2186
|
-
await lNet.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/LICENSE');
|
|
2187
|
-
time1 = Date.now();
|
|
2188
|
-
echo.push("<pre>lNet.get('https://cdn.jsdelivr.net/npm/deskrt@2.0.10/LICENSE');</pre>" + Math.round(time1 - time0).toString() + 'ms.<hr>');
|
|
2189
|
-
return echo.join('') + this._getEnd();
|
|
2190
|
-
}
|
|
2191
|
-
async netError() {
|
|
2192
|
-
const echo = [];
|
|
2193
|
-
const res = await lNet.get('https://192.111.000.222/xxx.zzz');
|
|
2194
|
-
echo.push(`<pre>lNet.get('https://192.111.000.222/xxx.zzz');</pre>
|
|
2195
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
2196
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
|
|
2197
|
-
error: <pre>${JSON.stringify(res.error, null, 4)}</pre>`);
|
|
2198
|
-
return echo.join('') + this._getEnd();
|
|
2199
|
-
}
|
|
2200
|
-
async netHosts() {
|
|
2201
|
-
const echo = [];
|
|
2202
|
-
const res = await lNet.get('http://nodejs.org:' + this._config.const.hostport.toString() + this._config.const.urlBase + 'test', {
|
|
2203
|
-
'hosts': {
|
|
2204
|
-
'nodejs.org': '127.0.0.1'
|
|
2205
|
-
}
|
|
2206
|
-
});
|
|
2207
|
-
echo.push(`<pre>lNet.get('http://nodejs.org:${this._config.const.hostport.toString() + this._config.const.urlBase}test', {
|
|
2208
|
-
'hosts': {
|
|
2209
|
-
'nodejs.org': '127.0.0.1'
|
|
2210
|
-
}
|
|
2211
|
-
});</pre>
|
|
2212
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
2213
|
-
content: <pre>${lText.htmlescape((await res.getContent())?.toString() ?? '')}</pre>
|
|
2214
|
-
error: <pre>${JSON.stringify(res.error, null, 4)}</pre>`);
|
|
2215
|
-
return echo.join('') + this._getEnd();
|
|
2216
|
-
}
|
|
2217
|
-
async netMproxy() {
|
|
2218
|
-
const echo = [];
|
|
2219
|
-
const res = await lNet.postJson(this._internalUrl + 'test/net-mproxy2', {
|
|
2220
|
-
'abc': 'ok',
|
|
2221
|
-
}, {
|
|
2222
|
-
'mproxy': {
|
|
2223
|
-
'url': this._internalUrl + 'test/net-mproxy1',
|
|
2224
|
-
'auth': '123456',
|
|
2225
|
-
'data': { 'test': '123' },
|
|
2226
|
-
}
|
|
2227
|
-
});
|
|
2228
|
-
echo.push(`<pre>lNet.get('${this._internalUrl}test/net-mproxy2', {
|
|
2229
|
-
'mproxy': {
|
|
2230
|
-
'url': '${this._internalUrl}test/net-mproxy1',
|
|
2231
|
-
'auth': '123456',
|
|
2232
|
-
'data': { 'test': '123' },
|
|
2233
|
-
}
|
|
2234
|
-
});</pre>
|
|
2235
|
-
headers: <pre>${JSON.stringify(res.headers, null, 4)}</pre>
|
|
2236
|
-
content: <pre>${(await res.getContent())?.toString() ?? 'null'}</pre>
|
|
2237
|
-
error: ${JSON.stringify(res.error)}`);
|
|
2238
|
-
return echo.join('') + '<br><br>' + this._getEnd();
|
|
2239
|
-
}
|
|
2240
|
-
async netMproxy1() {
|
|
2241
|
-
const data = lNet.mproxyData(this);
|
|
2242
|
-
lCore.debug('Got data', data);
|
|
2243
|
-
const rtn = await lNet.mproxy(this, '123456');
|
|
2244
|
-
if (rtn > 0) {
|
|
2245
|
-
return false;
|
|
2246
|
-
}
|
|
2247
|
-
return 'Nothing(' + rtn + ')';
|
|
2248
|
-
}
|
|
2249
|
-
netMproxy2() {
|
|
2250
|
-
return [1, {
|
|
2251
|
-
'data': this._post,
|
|
2252
|
-
}];
|
|
2253
|
-
}
|
|
2254
|
-
netFilterheaders() {
|
|
2255
|
-
const echo = [];
|
|
2256
|
-
const headers = {
|
|
2257
|
-
'host': 'www.maiyun.net',
|
|
2258
|
-
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/180.0.0.0 Safari/537.36',
|
|
2259
|
-
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
|
2260
|
-
'accept-encoding': 'gzip, deflate',
|
|
2261
|
-
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
|
2262
|
-
'cache-control': 'no-cache',
|
|
2263
|
-
'cdn-loop': 'TencentEdgeOne; loops=2',
|
|
2264
|
-
'eo-connecting-ip': 'xx:xx:xx:xx:xx',
|
|
2265
|
-
'eo-log-uuid': '102780998859203995',
|
|
2266
|
-
'pragma': 'no-cache',
|
|
2267
|
-
'priority': 'u=0, i',
|
|
2268
|
-
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="180"',
|
|
2269
|
-
'sec-ch-ua-mobile': '?0',
|
|
2270
|
-
'sec-ch-ua-platform': '"Windows"',
|
|
2271
|
-
'sec-fetch-dest': 'document',
|
|
2272
|
-
'sec-fetch-mode': 'navigate',
|
|
2273
|
-
'sec-fetch-site': 'none',
|
|
2274
|
-
'sec-fetch-user': '?1',
|
|
2275
|
-
'upgrade-insecure-requests': '1',
|
|
2276
|
-
'x-forwarded-for': '127.1.2.3',
|
|
2277
|
-
'x-forwarded-host': 'www.maiyun.net',
|
|
2278
|
-
'x-forwarded-proto': 'http',
|
|
2279
|
-
'authorization': ''
|
|
2280
|
-
};
|
|
2281
|
-
echo.push(`origin:<pre>${JSON.stringify(headers, null, 4)}</pre>`);
|
|
2282
|
-
const filtered = lNet.filterHeaders(headers, undefined, h => {
|
|
2283
|
-
return !['x-', 'eo-'].some(i => h.startsWith(i));
|
|
2284
|
-
});
|
|
2285
|
-
echo.push(`const filtered = lNet.filterHeaders(headers);<pre>${JSON.stringify(filtered, null, 4)}</pre>`);
|
|
2286
|
-
return echo.join('') + this._getEnd();
|
|
2287
|
-
}
|
|
2288
|
-
async netFetch() {
|
|
2289
|
-
const echo = [];
|
|
2290
|
-
try {
|
|
2291
|
-
const res = await lNet.fetch(this._internalUrl + 'test/net-fetch1', {
|
|
2292
|
-
'method': 'POST',
|
|
2293
|
-
'headers': {
|
|
2294
|
-
'content-type': 'application/json',
|
|
2295
|
-
},
|
|
2296
|
-
'body': JSON.stringify({ 'test': '123' }),
|
|
2297
|
-
});
|
|
2298
|
-
echo.push(`<pre>lNet.fetch('${this._internalUrl}test/net-fetch1', {
|
|
2299
|
-
'method': 'POST',
|
|
2300
|
-
'headers': {
|
|
2301
|
-
'content-type': 'application/json',
|
|
2302
|
-
},
|
|
2303
|
-
'body': JSON.stringify({ 'test': '123' }),
|
|
2304
|
-
});</pre>
|
|
2305
|
-
headers: <pre>${JSON.stringify(Object.fromEntries(res.headers.entries()), null, 4)}</pre>
|
|
2306
|
-
content: <pre>${await res.text()}</pre>`);
|
|
2307
|
-
}
|
|
2308
|
-
catch (e) {
|
|
2309
|
-
echo.push(`error: <pre>${JSON.stringify(e)}</pre>`);
|
|
2310
|
-
}
|
|
2311
|
-
echo.push(`mproxy:`);
|
|
2312
|
-
try {
|
|
2313
|
-
const res = await lNet.fetch(this._internalUrl + 'test/net-fetch1', {
|
|
2314
|
-
'method': 'POST',
|
|
2315
|
-
'headers': {
|
|
2316
|
-
'content-type': 'application/json',
|
|
2317
|
-
},
|
|
2318
|
-
'body': JSON.stringify({ 'test': '456' }),
|
|
2319
|
-
'mproxy': {
|
|
2320
|
-
'url': this._internalUrl + 'test/net-mproxy1',
|
|
2321
|
-
'auth': '123456',
|
|
2322
|
-
'data': { 'test': '789' },
|
|
2323
|
-
},
|
|
2324
|
-
});
|
|
2325
|
-
echo.push(`<pre>lNet.fetch('${this._internalUrl}test/net-fetch1', {
|
|
2326
|
-
'method': 'POST',
|
|
2327
|
-
'headers': {
|
|
2328
|
-
'content-type': 'application/json',
|
|
2329
|
-
},
|
|
2330
|
-
'body': JSON.stringify({ 'test': '123' }),
|
|
2331
|
-
'mproxy': {
|
|
2332
|
-
'url': this._internalUrl + 'test/net-mproxy1',
|
|
2333
|
-
'auth': '123456',
|
|
2334
|
-
'data': { 'test': '789' },
|
|
2335
|
-
},
|
|
2336
|
-
});</pre>
|
|
2337
|
-
headers: <pre>${JSON.stringify(Object.fromEntries(res.headers.entries()), null, 4)}</pre>
|
|
2338
|
-
content: <pre>${await res.text()}</pre>`);
|
|
2339
|
-
}
|
|
2340
|
-
catch (e) {
|
|
2341
|
-
echo.push(`error: <pre>${JSON.stringify(e)}</pre>`);
|
|
2342
|
-
}
|
|
2343
|
-
return echo.join('') + '<br>' + this._getEnd();
|
|
2344
|
-
}
|
|
2345
|
-
netFetch1() {
|
|
2346
|
-
return [1, {
|
|
2347
|
-
'post': this._post,
|
|
2348
|
-
}];
|
|
2349
|
-
}
|
|
2350
|
-
async netGetResponseJson() {
|
|
2351
|
-
const echo = [];
|
|
2352
|
-
const json = await lNet.getResponseJson(this._internalUrl + 'test/get-response-json1');
|
|
2353
|
-
echo.push(`<pre>const json = await lNet.getResponseJson('${this._internalUrl}test/get-response-json1');</pre>
|
|
2354
|
-
result: <pre>${lText.htmlescape(JSON.stringify(json, null, 4))}</pre>`);
|
|
2355
|
-
const json2 = await lNet.getResponseJson(this._internalUrl + 'test/get-response-json2');
|
|
2356
|
-
echo.push(`<pre>const json2 = await lNet.getResponseJson('${this._internalUrl}test/get-response-json2');</pre>
|
|
2357
|
-
result: <pre>${lText.htmlescape(JSON.stringify(json2, null, 4))}</pre>`);
|
|
2358
|
-
return echo.join('') + '<br><br>' + this._getEnd();
|
|
2359
|
-
}
|
|
2360
1866
|
getResponseJson1() {
|
|
2361
1867
|
return [1, {
|
|
2362
1868
|
'data': 'This is test data from getResponseJson1',
|
|
@@ -4730,6 +4236,70 @@ send.addEventListener('click', async () => {
|
|
|
4730
4236
|
echo.push(`Response CORS Headers: <pre>${JSON.stringify(headers, null, 4)}</pre>`);
|
|
4731
4237
|
return echo.join('') + '<br><br>' + this._getEnd();
|
|
4732
4238
|
}
|
|
4239
|
+
/**
|
|
4240
|
+
* --- 文件上传限制演示页面 ---
|
|
4241
|
+
*/
|
|
4242
|
+
ctrUploadLimits() {
|
|
4243
|
+
const urlBase = this._config.const.urlBase;
|
|
4244
|
+
return `
|
|
4245
|
+
<b>File Upload Limits Demo</b><br><br>
|
|
4246
|
+
<b>Rules:</b>
|
|
4247
|
+
<ul>
|
|
4248
|
+
<li>Allowed extensions: <code>.jpg</code>, <code>.png</code>, <code>.pdf</code></li>
|
|
4249
|
+
<li>Max file size: <code>100 KB</code></li>
|
|
4250
|
+
<li>If any file is rejected, the entire upload is rejected (returns <code>false</code>)</li>
|
|
4251
|
+
</ul>
|
|
4252
|
+
<b>Test cases:</b>
|
|
4253
|
+
<ol>
|
|
4254
|
+
<li>Upload a <code>.jpg</code> under 100KB → should succeed</li>
|
|
4255
|
+
<li>Upload a <code>.exe</code> → should be rejected (extension)</li>
|
|
4256
|
+
<li>Upload a <code>.jpg</code> over 100KB → should be rejected (size)</li>
|
|
4257
|
+
<li>Upload 2 files: one valid + one invalid → should reject entire upload</li>
|
|
4258
|
+
</ol>
|
|
4259
|
+
<form id="uploadForm" enctype="multipart/form-data">
|
|
4260
|
+
<input type="file" id="fileInput" multiple><br><br>
|
|
4261
|
+
<input type="button" value="Upload" onclick="doUpload()">
|
|
4262
|
+
</form>
|
|
4263
|
+
Result:<pre id="result">Nothing.</pre>
|
|
4264
|
+
<script>
|
|
4265
|
+
function doUpload() {
|
|
4266
|
+
var fd = new FormData();
|
|
4267
|
+
var files = document.getElementById('fileInput').files;
|
|
4268
|
+
for (var i = 0; i < files.length; i++) {
|
|
4269
|
+
fd.append('file' + i, files[i]);
|
|
4270
|
+
}
|
|
4271
|
+
document.getElementById('result').innerText = 'Uploading...';
|
|
4272
|
+
fetch('${urlBase}test/ctr-upload-limits1', {
|
|
4273
|
+
method: 'POST',
|
|
4274
|
+
body: fd
|
|
4275
|
+
}).then(function(r) { return r.text(); }).then(function(t) {
|
|
4276
|
+
document.getElementById('result').innerText = t;
|
|
4277
|
+
}).catch(function(e) {
|
|
4278
|
+
document.getElementById('result').innerText = 'Error: ' + e.message;
|
|
4279
|
+
});
|
|
4280
|
+
}
|
|
4281
|
+
</script>` + this._getEnd();
|
|
4282
|
+
}
|
|
4283
|
+
/**
|
|
4284
|
+
* --- 文件上传限制演示接口 ---
|
|
4285
|
+
*/
|
|
4286
|
+
async ctrUploadLimits1() {
|
|
4287
|
+
const rtn = await this._handleFormData({}, {
|
|
4288
|
+
'maxFileSize': 100 * 1024,
|
|
4289
|
+
'allowedExts': ['.jpg', '.png', '.pdf'],
|
|
4290
|
+
});
|
|
4291
|
+
if (!rtn) {
|
|
4292
|
+
return [0, 'Upload rejected. File extension not allowed or size exceeds 100KB limit.'];
|
|
4293
|
+
}
|
|
4294
|
+
const fileSummary = {};
|
|
4295
|
+
for (const key in this._files) {
|
|
4296
|
+
const f = this._files[key];
|
|
4297
|
+
fileSummary[key] = Array.isArray(f)
|
|
4298
|
+
? f.map(item => ({ 'name': item.name, 'size': item.size }))
|
|
4299
|
+
: { 'name': f.name, 'size': f.size };
|
|
4300
|
+
}
|
|
4301
|
+
return [1, { 'files': fileSummary, 'post': this._post }];
|
|
4302
|
+
}
|
|
4733
4303
|
/**
|
|
4734
4304
|
* --- END ---
|
|
4735
4305
|
*/
|