@maiyunnet/kebab 9.18.1 → 9.18.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/doc/kebab-rag.md +286 -142
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/lib/undici/request.d.ts +10 -0
- package/lib/undici/request.js +16 -0
- package/lib/undici.d.ts +16 -0
- package/lib/undici.js +33 -1
- package/package.json +1 -1
- package/sys/child.js +1 -1
- package/sys/ctr.d.ts +7 -0
- package/sys/ctr.js +28 -1
- package/sys/route.d.ts +2 -1
- package/sys/route.js +48 -9
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* --- 本文件用来定义每个目录实体地址的常量 ---
|
|
7
7
|
*/
|
|
8
8
|
/** --- 当前系统版本号 --- */
|
|
9
|
-
export const VER = '9.18.
|
|
9
|
+
export const VER = '9.18.2';
|
|
10
10
|
// --- 服务端用的路径 ---
|
|
11
11
|
const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
|
|
12
12
|
/** --- /xxx/xxx --- */
|
package/lib/undici/request.d.ts
CHANGED
|
@@ -47,6 +47,16 @@ export declare class Request {
|
|
|
47
47
|
* @param timeout 秒
|
|
48
48
|
*/
|
|
49
49
|
timeout(timeout: number): this;
|
|
50
|
+
/**
|
|
51
|
+
* --- 设置空闲连接允许复用的最长时间 ---
|
|
52
|
+
* @param timeout 秒数;非正数或无效值按未设置处理;默认无服务端提示时为 4 秒,有提示时采用提示值减 2 秒且最多 600 秒
|
|
53
|
+
*/
|
|
54
|
+
keepAliveTimeout(timeout: number): this;
|
|
55
|
+
/**
|
|
56
|
+
* --- 设置连接建立后允许承接新请求的最长时间 ---
|
|
57
|
+
* @param timeout 秒数;非正数或无效值按未设置处理;达到后不再复用但不会中断在途请求,默认不限制
|
|
58
|
+
*/
|
|
59
|
+
reuseTimeout(timeout: number): this;
|
|
50
60
|
/**
|
|
51
61
|
* --- 设置网络异常后的重试次数 ---
|
|
52
62
|
* @param retry 重试次数,默认为 1;非幂等请求需由调用方保证安全
|
package/lib/undici/request.js
CHANGED
|
@@ -59,6 +59,22 @@ export class Request {
|
|
|
59
59
|
this._opt['timeout'] = timeout;
|
|
60
60
|
return this;
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* --- 设置空闲连接允许复用的最长时间 ---
|
|
64
|
+
* @param timeout 秒数;非正数或无效值按未设置处理;默认无服务端提示时为 4 秒,有提示时采用提示值减 2 秒且最多 600 秒
|
|
65
|
+
*/
|
|
66
|
+
keepAliveTimeout(timeout) {
|
|
67
|
+
this._opt['keepAliveTimeout'] = timeout;
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* --- 设置连接建立后允许承接新请求的最长时间 ---
|
|
72
|
+
* @param timeout 秒数;非正数或无效值按未设置处理;达到后不再复用但不会中断在途请求,默认不限制
|
|
73
|
+
*/
|
|
74
|
+
reuseTimeout(timeout) {
|
|
75
|
+
this._opt['reuseTimeout'] = timeout;
|
|
76
|
+
return this;
|
|
77
|
+
}
|
|
62
78
|
/**
|
|
63
79
|
* --- 设置网络异常后的重试次数 ---
|
|
64
80
|
* @param retry 重试次数,默认为 1;非幂等请求需由调用方保证安全
|
package/lib/undici.d.ts
CHANGED
|
@@ -74,6 +74,10 @@ export declare function fetch(input: string | URL | Request, init?: RequestInit
|
|
|
74
74
|
'hosts'?: Record<string, string> | string;
|
|
75
75
|
/** --- 网络异常后的重试次数,默认 0;流式请求体不可重试,非幂等请求需由调用方保证安全 --- */
|
|
76
76
|
'retry'?: number;
|
|
77
|
+
/** --- 空闲连接允许复用的最长秒数;非正数或无效值按未设置处理;默认无服务端提示时为 4 秒,有提示时采用提示值减 2 秒且最多 600 秒 --- */
|
|
78
|
+
'keepAliveTimeout'?: number;
|
|
79
|
+
/** --- 连接建立后允许承接新请求的最长秒数;非正数或无效值按未设置处理;达到后不再复用但不会中断在途请求,默认不限制 --- */
|
|
80
|
+
'reuseTimeout'?: number;
|
|
77
81
|
}): Promise<Response>;
|
|
78
82
|
/**
|
|
79
83
|
* --- 发起一个请求 ---
|
|
@@ -152,6 +156,10 @@ export interface IRequestOptions {
|
|
|
152
156
|
};
|
|
153
157
|
/** --- 连接是否保持长连接(即是否允许复用),默认为 true --- */
|
|
154
158
|
'keep'?: boolean;
|
|
159
|
+
/** --- 空闲连接允许复用的最长秒数;非正数或无效值按未设置处理;默认无服务端提示时为 4 秒,有提示时采用提示值减 2 秒且最多 600 秒;自定义 Agent 由其自身配置 --- */
|
|
160
|
+
'keepAliveTimeout'?: number;
|
|
161
|
+
/** --- 连接建立后允许承接新请求的最长秒数;非正数或无效值按未设置处理;达到后不再复用但不会中断在途请求,默认不限制;自定义 Agent 由其自身配置 --- */
|
|
162
|
+
'reuseTimeout'?: number;
|
|
155
163
|
/** --- 复用池名/Agent,默认为 default --- */
|
|
156
164
|
'reuse'?: string | undici.ProxyAgent | undici.Agent;
|
|
157
165
|
/** --- cookie 托管对象 --- */
|
|
@@ -170,6 +178,10 @@ export interface IMproxyOptions {
|
|
|
170
178
|
'hosts'?: Record<string, string> | string;
|
|
171
179
|
'local'?: string;
|
|
172
180
|
'headers'?: THttpHeaders;
|
|
181
|
+
/** --- 空闲连接允许复用的最长秒数;非正数或无效值按未设置处理;默认无服务端提示时为 4 秒,有提示时采用提示值减 2 秒且最多 600 秒 --- */
|
|
182
|
+
'keepAliveTimeout'?: number;
|
|
183
|
+
/** --- 连接建立后允许承接新请求的最长秒数;非正数或无效值按未设置处理;达到后不再复用但不会中断在途请求,默认不限制 --- */
|
|
184
|
+
'reuseTimeout'?: number;
|
|
173
185
|
/** --- 过滤 header,返回 true 则留下 --- */
|
|
174
186
|
filter?: (h: string) => boolean;
|
|
175
187
|
/** --- 默认为 default --- */
|
|
@@ -184,6 +196,10 @@ export interface IRproxyOptions {
|
|
|
184
196
|
'hosts'?: Record<string, string> | string;
|
|
185
197
|
'local'?: string;
|
|
186
198
|
'headers'?: THttpHeaders;
|
|
199
|
+
/** --- 空闲连接允许复用的最长秒数;非正数或无效值按未设置处理;默认无服务端提示时为 4 秒,有提示时采用提示值减 2 秒且最多 600 秒 --- */
|
|
200
|
+
'keepAliveTimeout'?: number;
|
|
201
|
+
/** --- 连接建立后允许承接新请求的最长秒数;非正数或无效值按未设置处理;达到后不再复用但不会中断在途请求,默认不限制 --- */
|
|
202
|
+
'reuseTimeout'?: number;
|
|
187
203
|
/** --- 过滤 header,返回 true 则留下 --- */
|
|
188
204
|
filter?: (h: string) => boolean;
|
|
189
205
|
/** --- 正向 mproxy 代理,url 如 https://xxx/abc --- */
|
package/lib/undici.js
CHANGED
|
@@ -45,14 +45,31 @@ function normalizeRetry(retry) {
|
|
|
45
45
|
return (typeof retry === 'number') && Number.isFinite(retry) ?
|
|
46
46
|
Math.max(0, Math.floor(retry)) : 0;
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* --- 将 Agent 的秒数配置转换为毫秒 ---
|
|
50
|
+
* @param timeout 秒数
|
|
51
|
+
* @returns 未设置或数值无效时返回 undefined,否则返回毫秒数
|
|
52
|
+
*/
|
|
53
|
+
function normalizeAgentTimeout(timeout) {
|
|
54
|
+
if (timeout === undefined) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const milliseconds = timeout * 1_000;
|
|
58
|
+
if (!Number.isFinite(milliseconds) || (milliseconds <= 0)) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
return milliseconds;
|
|
62
|
+
}
|
|
48
63
|
/** --- 获取或创建 undici.agent 对象 --- */
|
|
49
64
|
function getAgent(opt = {}) {
|
|
50
65
|
let k = opt.reuse ?? 'default';
|
|
51
66
|
if (typeof k !== 'string') {
|
|
52
67
|
return k;
|
|
53
68
|
}
|
|
69
|
+
const keepAliveTimeout = normalizeAgentTimeout(opt.keepAliveTimeout);
|
|
70
|
+
const reuseTimeout = normalizeAgentTimeout(opt.reuseTimeout);
|
|
54
71
|
if (k === 'default') {
|
|
55
|
-
// --- hosts/local/keep
|
|
72
|
+
// --- hosts/local/keep/连接时长均会影响 agent 行为,需生成独立的 key ---
|
|
56
73
|
const features = [];
|
|
57
74
|
if (opt.hosts) {
|
|
58
75
|
features.push(`h:${lCrypto.hashHmac('md5', lText.stringifyJson(opt.hosts))}`);
|
|
@@ -63,6 +80,12 @@ function getAgent(opt = {}) {
|
|
|
63
80
|
if (opt.keep === false) {
|
|
64
81
|
features.push('k:0');
|
|
65
82
|
}
|
|
83
|
+
if (keepAliveTimeout !== undefined) {
|
|
84
|
+
features.push(`ka:${keepAliveTimeout}`);
|
|
85
|
+
}
|
|
86
|
+
if (reuseTimeout !== undefined) {
|
|
87
|
+
features.push(`rt:${reuseTimeout}`);
|
|
88
|
+
}
|
|
66
89
|
if (features.length > 0) {
|
|
67
90
|
k = features.join('|');
|
|
68
91
|
}
|
|
@@ -74,6 +97,13 @@ function getAgent(opt = {}) {
|
|
|
74
97
|
lookup: buildDnsLookup(opt.hosts),
|
|
75
98
|
},
|
|
76
99
|
'pipelining': opt.keep === false ? 0 : 1,
|
|
100
|
+
...(keepAliveTimeout === undefined ? {} : {
|
|
101
|
+
'keepAliveTimeout': keepAliveTimeout,
|
|
102
|
+
'keepAliveMaxTimeout': keepAliveTimeout,
|
|
103
|
+
}),
|
|
104
|
+
...(reuseTimeout === undefined ? {} : {
|
|
105
|
+
'clientTtl': reuseTimeout,
|
|
106
|
+
}),
|
|
77
107
|
}));
|
|
78
108
|
}
|
|
79
109
|
return agents.get(k);
|
|
@@ -293,6 +323,8 @@ export async function fetch(input, init = {}) {
|
|
|
293
323
|
'hosts': init.hosts,
|
|
294
324
|
'mproxy': init.mproxy,
|
|
295
325
|
'retry': init.retry,
|
|
326
|
+
'keepAliveTimeout': init.keepAliveTimeout,
|
|
327
|
+
'reuseTimeout': init.reuseTimeout,
|
|
296
328
|
'signal': init.signal ?? undefined,
|
|
297
329
|
'follow': init.redirect === 'follow' ? 10 : 0,
|
|
298
330
|
};
|
package/package.json
CHANGED
package/sys/child.js
CHANGED
package/sys/ctr.d.ts
CHANGED
|
@@ -58,6 +58,13 @@ export type TValibotResult<TSchema extends v.GenericSchema, TIssue extends v.Bas
|
|
|
58
58
|
readonly issues: [TIssue, ...TIssue[]];
|
|
59
59
|
readonly response: kebab.Json[];
|
|
60
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* --- 判断当前 HTTP 请求是否仍可响应 ---
|
|
63
|
+
* @param req 请求对象
|
|
64
|
+
* @param res 响应对象
|
|
65
|
+
* @returns 客户端对应的请求流仍可响应时返回 true
|
|
66
|
+
*/
|
|
67
|
+
export declare function isHttpRequestAvailable(req: http2.Http2ServerRequest | http.IncomingMessage, res: http2.Http2ServerResponse | http.ServerResponse): boolean;
|
|
61
68
|
export declare class Ctr {
|
|
62
69
|
/** --- 路由参数序列数组 --- */
|
|
63
70
|
protected _param: string[];
|
package/sys/ctr.js
CHANGED
|
@@ -19,6 +19,27 @@ export function clearLocaleData() {
|
|
|
19
19
|
localeFiles = [];
|
|
20
20
|
localeData = {};
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* --- 判断当前 HTTP 请求是否仍可响应 ---
|
|
24
|
+
* @param req 请求对象
|
|
25
|
+
* @param res 响应对象
|
|
26
|
+
* @returns 客户端对应的请求流仍可响应时返回 true
|
|
27
|
+
*/
|
|
28
|
+
export function isHttpRequestAvailable(req, res) {
|
|
29
|
+
if (req.destroyed && !req.complete) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (('stream' in req) && (req.stream.closed || req.stream.destroyed)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (req.socket.destroyed || !req.socket.writable) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
if (res.destroyed || res.closed || res.writableEnded || !res.writable) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
22
43
|
export class Ctr {
|
|
23
44
|
/** --- 路由参数序列数组 --- */
|
|
24
45
|
_param = [];
|
|
@@ -69,7 +90,13 @@ export class Ctr {
|
|
|
69
90
|
}
|
|
70
91
|
/** --- 当前用户连接是否还在连接中 --- */
|
|
71
92
|
get _isAvail() {
|
|
72
|
-
|
|
93
|
+
if (this._socket) {
|
|
94
|
+
return this._socket.writable;
|
|
95
|
+
}
|
|
96
|
+
if (!this._res) {
|
|
97
|
+
return !this._req.socket.destroyed && this._req.socket.writable;
|
|
98
|
+
}
|
|
99
|
+
return isHttpRequestAvailable(this._req, this._res);
|
|
73
100
|
}
|
|
74
101
|
/** --- timeout 的 timer --- */
|
|
75
102
|
_timer;
|
package/sys/route.d.ts
CHANGED
|
@@ -51,12 +51,13 @@ export declare function waitCtr(cctr: sCtr.Ctr): Promise<void>;
|
|
|
51
51
|
/**
|
|
52
52
|
* --- 获取 post 对象(通常已自动获取),如果是文件上传(formdata)的情况则不获取 ---
|
|
53
53
|
* @param req 请求对象
|
|
54
|
+
* @returns 解析后的 POST 数据,请求传输中断时返回 false
|
|
54
55
|
*/
|
|
55
56
|
export declare function getPost(req: http2.Http2ServerRequest | http.IncomingMessage): Promise<{
|
|
56
57
|
'input': string;
|
|
57
58
|
'raw': Record<string, any>;
|
|
58
59
|
'post': Record<string, any>;
|
|
59
|
-
}>;
|
|
60
|
+
} | false>;
|
|
60
61
|
/**
|
|
61
62
|
* --- 获取 formdata 的 post ---
|
|
62
63
|
* @param req 请求头
|
package/sys/route.js
CHANGED
|
@@ -453,6 +453,9 @@ export async function run(data) {
|
|
|
453
453
|
}
|
|
454
454
|
if (reqStartRtn === 1) {
|
|
455
455
|
const rawPost = await getPost(data.req);
|
|
456
|
+
if (!rawPost) {
|
|
457
|
+
return true;
|
|
458
|
+
}
|
|
456
459
|
// --- 原始 POST ---
|
|
457
460
|
middle.setPrototype('_rawPost', rawPost.raw);
|
|
458
461
|
// --- 原始 input ---
|
|
@@ -844,6 +847,7 @@ export async function waitCtr(cctr) {
|
|
|
844
847
|
/**
|
|
845
848
|
* --- 获取 post 对象(通常已自动获取),如果是文件上传(formdata)的情况则不获取 ---
|
|
846
849
|
* @param req 请求对象
|
|
850
|
+
* @returns 解析后的 POST 数据,请求传输中断时返回 false
|
|
847
851
|
*/
|
|
848
852
|
export function getPost(req) {
|
|
849
853
|
return new Promise(function (resolve) {
|
|
@@ -861,7 +865,28 @@ export function getPost(req) {
|
|
|
861
865
|
const maxPostSize = 50 * 1024 * 1024;
|
|
862
866
|
let buffer = Buffer.from('');
|
|
863
867
|
let overflow = false;
|
|
864
|
-
|
|
868
|
+
let finished = false;
|
|
869
|
+
let onData;
|
|
870
|
+
let onEnd;
|
|
871
|
+
let onError;
|
|
872
|
+
let onAborted;
|
|
873
|
+
let onClose;
|
|
874
|
+
const cleanup = () => {
|
|
875
|
+
req.off('data', onData);
|
|
876
|
+
req.off('end', onEnd);
|
|
877
|
+
req.off('error', onError);
|
|
878
|
+
req.off('aborted', onAborted);
|
|
879
|
+
req.off('close', onClose);
|
|
880
|
+
};
|
|
881
|
+
const finish = (result) => {
|
|
882
|
+
if (finished) {
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
finished = true;
|
|
886
|
+
cleanup();
|
|
887
|
+
resolve(result);
|
|
888
|
+
};
|
|
889
|
+
onData = (chunk) => {
|
|
865
890
|
if (overflow) {
|
|
866
891
|
return;
|
|
867
892
|
}
|
|
@@ -871,10 +896,10 @@ export function getPost(req) {
|
|
|
871
896
|
return;
|
|
872
897
|
}
|
|
873
898
|
buffer = Buffer.concat([buffer, chunk], buffer.length + chunk.length);
|
|
874
|
-
}
|
|
875
|
-
|
|
899
|
+
};
|
|
900
|
+
onEnd = () => {
|
|
876
901
|
if (overflow) {
|
|
877
|
-
|
|
902
|
+
finish({
|
|
878
903
|
'input': '',
|
|
879
904
|
'raw': {},
|
|
880
905
|
'post': {},
|
|
@@ -883,7 +908,7 @@ export function getPost(req) {
|
|
|
883
908
|
}
|
|
884
909
|
const s = buffer.toString();
|
|
885
910
|
if (!s) {
|
|
886
|
-
|
|
911
|
+
finish({
|
|
887
912
|
'input': '',
|
|
888
913
|
'raw': {},
|
|
889
914
|
'post': {},
|
|
@@ -894,14 +919,14 @@ export function getPost(req) {
|
|
|
894
919
|
if (ct.includes('json')) {
|
|
895
920
|
try {
|
|
896
921
|
const raw = lText.parseJson(s);
|
|
897
|
-
|
|
922
|
+
finish({
|
|
898
923
|
'input': s,
|
|
899
924
|
'raw': raw,
|
|
900
925
|
'post': lText.trimJson(raw),
|
|
901
926
|
});
|
|
902
927
|
}
|
|
903
928
|
catch {
|
|
904
|
-
|
|
929
|
+
finish({
|
|
905
930
|
'input': '',
|
|
906
931
|
'raw': {},
|
|
907
932
|
'post': {},
|
|
@@ -910,12 +935,26 @@ export function getPost(req) {
|
|
|
910
935
|
return;
|
|
911
936
|
}
|
|
912
937
|
const raw = lText.queryParse(s);
|
|
913
|
-
|
|
938
|
+
finish({
|
|
914
939
|
'input': s,
|
|
915
940
|
'raw': raw,
|
|
916
941
|
'post': lText.trimJson(raw),
|
|
917
942
|
});
|
|
918
|
-
}
|
|
943
|
+
};
|
|
944
|
+
onError = () => {
|
|
945
|
+
finish(false);
|
|
946
|
+
};
|
|
947
|
+
onAborted = () => {
|
|
948
|
+
finish(false);
|
|
949
|
+
};
|
|
950
|
+
onClose = () => {
|
|
951
|
+
finish(false);
|
|
952
|
+
};
|
|
953
|
+
req.on('data', onData);
|
|
954
|
+
req.on('end', onEnd);
|
|
955
|
+
req.on('error', onError);
|
|
956
|
+
req.on('aborted', onAborted);
|
|
957
|
+
req.on('close', onClose);
|
|
919
958
|
});
|
|
920
959
|
}
|
|
921
960
|
/**
|