🍕 Sync 2026-10-01 13:33:56

This commit is contained in:
github-actions[bot] committed 2026-10-01 13:33:56 +08:00
1 parent 9057fa94b1
commit 1f3a4e8fb4
24 files changed
+731 -83

No files matched your search

@@ -8,6 +8,11 @@ local M = {}
M.TIMEOUT = 2 -- 每个目标的超时(秒)
-- 批量探测时同时存在的子进程上限。
-- 每个探测占 1 个进程 + 1 个管道 fd,节点数由订阅内容决定(可上千),
-- 不限并发会把路由器的 fd/进程额度打满,之后 io.popen 静默失败。
M.MAX_PARALLEL = 16
-- shell 引号:用单引号转义(util.shq),配合 safe_host 校验,双重防命令注入。
-- 不能用 string.format("%q"):它生成双引号字符串,sh 在双引号内仍会做 $() / ``
-- 命令替换,实测可注入。
@@ -145,26 +150,39 @@ end
-- 返回结果数组 { name=, server=, port=, latency=number|nil }
function M.probe(nodes, mode)
local jobs = {}
-- 分批:一批最多 MAX_PARALLEL 个进程,读完并关闭这一批再起下一批。
--
-- 原来是「先把全部节点的 io.popen 起完,再统一读取」。并行度确实最高
-- (总耗时≈单节点超时),但代价是无界的:每个节点占 1 个进程 + 1 个管道 fd,
-- 而节点数由订阅内容决定(上千个很常见)。路由器上 fd 与进程数都是硬上限,
-- 打满之后 io.popen 直接失败 —— 表现是一大片节点探测不出来,
-- 而不是报错。分批后并发度封顶,总耗时仍是「批数 × 单节点超时」。
local out = {}
local batch = {}
local function drain()
for _, j in ipairs(batch) do
local latency
if j.f then
local raw = j.f:read("*a") or ""
j.f:close()
latency = j.parse and j.parse(raw) or nil
end
out[#out + 1] = {
name = j.name, server = j.server, port = j.port, latency = latency,
}
end
batch = {}
end
for _, n in ipairs(nodes or {}) do
local cmd, parse = build_job(mode, n.server, n.port)
local f = cmd and io.popen(cmd) or nil
jobs[#jobs + 1] = {
batch[#batch + 1] = {
f = f, parse = parse,
name = n.name or "", server = n.server or "", port = n.port or "",
}
if #batch >= M.MAX_PARALLEL then drain() end
end
local out = {}
for _, j in ipairs(jobs) do
local latency
if j.f then
local raw = j.f:read("*a") or ""
j.f:close()
latency = j.parse and j.parse(raw) or nil
end
out[#out + 1] = {
name = j.name, server = j.server, port = j.port, latency = latency,
}
end
drain()
return out
end