update 2026-10-11 00:49:02
marry-jell / merge (push) Canceled after 0s

This commit is contained in:
action committed 2026-10-11 00:49:02 +08:00
1 parent 54d5be6086
commit 7ae707aaa6
8 files changed
+112 -64

No files matched your search

@@ -381,35 +381,36 @@ function connect_status()
local e = {}
e.use_time = ""
local url = http.formvalue("url")
if not url or url == "" then
http_write_json(e)
return
end
local aliyun = string.find(url, "aliyun")
local chn_list = uci_get("@global[0]", "chn_list") or "direct"
local gfw_list = uci_get("@global[0]", "use_gfw_list") or "1"
local proxy_mode = uci_get("@global[0]", "tcp_proxy_mode") or "proxy"
local localhost_proxy = uci_get("@global[0]", "localhost_proxy") or "1"
local socks_server = (localhost_proxy == "0") and api.get_cache_var("GLOBAL_SOCKS_server") or ""
url = "-w %{http_code}:%{time_pretransfer} " .. url
local curl_args = "-w '%{http_code}:%{time_pretransfer}:%{time_starttransfer}' " .. util.shellquote(url)
if socks_server and socks_server ~= "" then
if (chn_list == "proxy" and gfw_list == "0" and proxy_mode ~= "proxy" and aliyun ~= nil) or (chn_list == "0" and gfw_list == "0" and proxy_mode == "proxy") then
-- 中国列表+阿里 or 全局
url = "-x socks5h://" .. socks_server .. " " .. url
curl_args = "-x " .. util.shellquote("socks5h://" .. socks_server) .. " " .. curl_args
elseif aliyun == nil then
-- 其他代理模式+阿里以外网站
url = "-x socks5h://" .. socks_server .. " " .. url
curl_args = "-x " .. util.shellquote("socks5h://" .. socks_server) .. " " .. curl_args
end
end
local result = luci.sys.exec('/usr/bin/curl --connect-timeout 3 --max-time 5 -o /dev/null -I -sk ' .. url)
local code = tonumber(luci.sys.exec("echo -n '" .. result .. "' | awk -F ':' '{print $1}'") or "0")
if code ~= 0 then
local use_time_str = luci.sys.exec("echo -n '" .. result .. "' | awk -F ':' '{print $2}'")
local use_time = tonumber(use_time_str)
if use_time then
if use_time_str:find("%.") then
e.use_time = string.format("%.2f", use_time * 1000)
else
e.use_time = string.format("%.2f", use_time / 1000)
end
e.ping_type = "curl"
end
local result = luci.sys.exec('curl_result=$(/usr/bin/curl --connect-timeout 3 --max-time 5 -o /dev/null -I -sk ' .. curl_args .. '); [ $? -eq 0 ] && printf "%s" "$curl_result"')
local code, pretransfer, starttransfer = result:match("^%s*(%d+):([%d%.]+):([%d%.]+)%s*$")
code, pretransfer, starttransfer = tonumber(code), tonumber(pretransfer), tonumber(starttransfer)
if code and code > 0 then e.http_code = code end
-- A completed HTTP response, including 4xx/5xx, confirms homepage connectivity.
if code and code >= 200 and code < 600 and pretransfer and pretransfer >= 0 and starttransfer and starttransfer > 0 and starttransfer >= pretransfer then
-- HTTP request latency excludes connection and TLS handshakes.
e.use_time = string.format("%.2f", (starttransfer - pretransfer) * 1000)
e.handshake_time = string.format("%.2f", pretransfer * 1000)
e.ping_type = "curl"
end
http_write_json(e)
end
@@ -438,17 +439,13 @@ function urltest_node()
local e = {}
e.index = index
local result = luci.sys.exec("/usr/share/passwall/test.sh url_test_node " .. util.shellquote(id or ""))
local code = tonumber(luci.sys.exec("echo -n '" .. result .. "' | awk -F ':' '{print $1}'") or "0")
if code ~= 0 then
local use_time_str = luci.sys.exec("echo -n '" .. result .. "' | awk -F ':' '{print $2}'")
local use_time = tonumber(use_time_str)
if use_time then
if use_time_str:find("%.") then
e.use_time = string.format("%.2f", use_time * 1000)
else
e.use_time = string.format("%.2f", use_time / 1000)
end
end
local code, pretransfer, starttransfer = result:match("^%s*(%d+):([%d%.]+):([%d%.]+)%s*$")
code, pretransfer, starttransfer = tonumber(code), tonumber(pretransfer), tonumber(starttransfer)
if code and code > 0 then e.http_code = code end
if code and code >= 200 and code < 600 and pretransfer and pretransfer >= 0 and starttransfer and starttransfer > 0 and starttransfer >= pretransfer then
-- Keep node test parsing independent from the homepage test.
e.use_time = string.format("%.2f", (starttransfer - pretransfer) * 1000)
e.handshake_time = string.format("%.2f", pretransfer * 1000)
end
http_write_json(e)
end
+1 -1
View File
@@ -169,7 +169,7 @@ function del_cache_var(key)
end
function set_cache_var(key, val)
sys.call(string.format('. /usr/share/passwall/utils.sh ; set_cache_var "%s" "%s"', key, val))
sys.call(string.format('. /usr/share/passwall/utils.sh ; set_cache_var %s %s', util.shellquote(tostring(key)), util.shellquote(tostring(val))))
end
function get_cache_var(key)
@@ -139,7 +139,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
<div class="pure-u-1-4 check" onclick="check_connect('aliyun', 'https://www.aliyun.com')" title="<%:TLS handshake test, latency for reference only%>">
<div class="pure-u-1-4 check" onclick="check_connect('aliyun', 'https://www.aliyun.com')" title="<%:HTTP request latency, for reference only%>">
<div class="block pure-g">
<div class="pure-u-1-3">
<div class="img-con">
@@ -151,7 +151,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
<div class="pure-u-1-4 check" onclick="check_connect('google', 'https://www.google.com/generate_204')" title="<%:TLS handshake test, latency for reference only%>">
<div class="pure-u-1-4 check" onclick="check_connect('google', 'https://www.google.com/generate_204')" title="<%:HTTP request latency, for reference only%>">
<div class="block pure-g">
<div class="pure-u-1-3">
<div class="img-con">
@@ -163,7 +163,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
<div class="pure-u-1-4 check" onclick="check_connect('github', 'https://github.com')" title="<%:TLS handshake test, latency for reference only%>">
<div class="pure-u-1-4 check" onclick="check_connect('github', 'https://github.com')" title="<%:HTTP request latency, for reference only%>">
<div class="block pure-g">
<div class="pure-u-1-3">
<div class="img-con">
@@ -175,7 +175,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
<div class="pure-u-1-4 check" onclick="check_connect('youtube', 'https://www.youtube.com')" title="<%:TLS handshake test, latency for reference only%>">
<div class="pure-u-1-4 check" onclick="check_connect('youtube', 'https://www.youtube.com/generate_204')" title="<%:HTTP request latency, for reference only%>">
<div class="block pure-g">
<div class="pure-u-1-3">
<div class="img-con">
@@ -246,6 +246,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
if (s) {
var div = s.parentNode.parentNode.parentNode.parentNode;
div.removeAttribute('onclick');
div.title = "<%:HTTP request latency, for reference only%>";
s.innerHTML = '<%:Check...%>';
var sendDate = (new Date()).getTime();
XHR.get('<%=api.url("connect_status")%>', {
@@ -263,6 +264,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
s.className="red";
}
s.innerHTML = use_time + " ms";
div.title += "\n<%:Initial handshake time%>: " + rv.handshake_time + " ms";
} else if (rv.status) {
s.className="green";
s.innerHTML = "<%:Working...%>";
@@ -710,6 +710,7 @@ table td, .table .td {
function urltest_node(cbi_id, dom) {
if (dom.dataset.busy == "1" || !cbi_id) return;
dom.dataset.busy = "1";
dom.title = "<%:HTTP request latency, for reference only%>";
dom.style.color = "";
dom.innerText = "<%:Check...%>";
XHR.get('<%=api.url("urltest_node")%>', {
@@ -720,7 +721,7 @@ table td, .table .td {
let text = "<%:Error%>";
if (x && x.status == 200) {
if (result.use_time == null || result.use_time.trim() == "") {
text = "<%:Timeout%>";
text = result.http_code >= 400 ? "HTTP " + result.http_code : "<%:Timeout%>";
} else {
let use_time = parseInt(result.use_time);
if (use_time < 1000)
@@ -728,6 +729,7 @@ table td, .table .td {
else if (use_time < 2000)
color = "#fb9a05";
text = use_time + " ms";
dom.title += "\n<%:Initial handshake time%>: " + result.handshake_time + " ms";
}
}
dom.innerText = text;
@@ -1417,7 +1419,7 @@ table td, .table .td {
innerHTML = innerHTML.split("{{tcping}}").join('<span class="tcping_value" cbiid="{{id}}"><a href="javascript:void(0)" style="color:inherit">---</a></span>');
}
if (o["address"] && _port) {
innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:urltest_node(\'{{id}}\', this)" title="<%:TLS handshake test, latency for reference only%>"><%:Test%></a></span>');
innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:urltest_node(\'{{id}}\', this)" title="<%:HTTP request latency, for reference only%>"><%:Test%></a></span>');
} else {
innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping" cbiid="{{id}}">---</span>');
}
+5 -2
View File
@@ -22,8 +22,11 @@ msgstr "连接失败"
msgid "Touch Check"
msgstr "点我检测"
msgid "TLS handshake test, latency for reference only"
msgstr "TLS握手测试,延时仅供参考"
msgid "HTTP request latency, for reference only"
msgstr "HTTP 请求延迟,仅供参考"
msgid "Initial handshake time"
msgstr "首次握手耗时"
msgid "Kernel Unsupported"
msgstr "内核不支持"
@@ -21,7 +21,7 @@ local haproxy_dns = "127.0.0.1"
local cpu_thread = sys.exec('echo -n $(cat /proc/cpuinfo | grep "processor" | wc -l)') or "1"
local health_check_type = api.uci_get_c("@global_haproxy[0]", "health_check_type") or "tcp"
local health_check_inter = api.uci_get_c("@global_haproxy[0]", "health_check_inter") or "20"
local health_check_inter = api.uci_get_c("@global_haproxy[0]", "health_check_inter") or "60"
local balancingStrategy = api.uci_get_c("@global_haproxy[0]", "balancingStrategy") or "roundrobin"
local console_port = api.uci_get_c("@global_haproxy[0]", "console_port") or "1188"
local bind_local = api.uci_get_c("@global_haproxy[0]", "bind_local") or "0"
@@ -44,7 +44,7 @@ global
nbthread {{nbthread}}
external-check
insecure-fork-wanted
spread-checks 50
spread-checks 20
tune.max-checks-per-thread 5
defaults
@@ -99,7 +99,7 @@ api.uci_foreach_c("haproxy_config", function(t)
local lbss = t.lbss
local listen_port = tonumber(t.haproxy_port) or 0
local server_node = api.uci_get_c(lbss)
local hop = (health_check_type == "script_logic") and (server_node.hysteria_hop or server_node.hysteria2_hop) or nil
local hop = (health_check_type == "script_logic") and server_node and (server_node.hysteria_hop or server_node.hysteria2_hop) or nil
hop = hop and hop:gsub(":", "-") or nil
if server_node and server_node.address and (server_node.port or hop) then
server_remark = server_node.address .. ":" .. (server_node.port or hop)
@@ -153,9 +153,12 @@ api.uci_foreach_c("haproxy_config", function(t)
end
end)
local inter = tonumber((health_check_inter or "20"):match("^%d+"))
inter = (inter and inter >= 20) and inter or 20
local inter_arg = "inter " .. inter .. "s fastinter " .. inter / 2 .. "s downinter " .. inter * 1.5 .. "s"
local inter = tonumber(health_check_inter:match("^%d+")) or 60
inter = math.max(inter, 20)
-- 限制过渡状态的确认间隔,避免正常检测间隔较长时摘除或恢复过慢。
local fastinter = math.max(10, math.min(15, math.floor(inter / 2)))
local downinter = math.ceil(inter * 1.5)
local inter_arg = string.format("inter %ds fastinter %ds downinter %ds", inter, fastinter, downinter)
local sortTable = {}
for i in pairs(listens) do
@@ -165,7 +168,28 @@ for i in pairs(listens) do
end
table.sort(sortTable, function(a,b) return (a < b) end)
for i, port in pairs(sortTable) do
local function server_endpoint(o)
local address = o.server_address:gsub("^%[(.*)%]$", "%1")
if address:find(":", 1, true) then address = "[" .. address .. "]" end
return address .. ":" .. o.server_port
end
local check_counts = {}
if health_check_type == "script_logic" then
for _, port in ipairs(sortTable) do
for _, o in ipairs(listens[port]) do
-- 域名可能解析到不同 IP,仅对相同的实际代理端点共享检测。
if api.is_ip(o.server_address) then
local endpoint = server_endpoint(o)
check_counts[endpoint] = (check_counts[endpoint] or 0) + 1
end
end
end
end
local shared_checks = {}
local shared_check_names = {}
for i, port in ipairs(sortTable) do
log(" + 入口 %s:%s" % {bind_address, port})
f_out:write("\n" .. string.format([[
@@ -184,7 +208,7 @@ listen %s
local count_M, count_B = 1, 1
for i, o in ipairs(listens[port]) do
local remark = o.server_remark or ""
local remark = (o.server_remark or ""):gsub("[%[%]]", "")
-- 防止重名导致无法运行
if tostring(o.backup) ~= "1" then
remark = "M" .. count_M .. "-" .. remark
@@ -193,8 +217,18 @@ listen %s
remark = "B" .. count_B .. "-" .. remark
count_B = count_B + 1
end
local server = o.server_address .. ":" .. o.server_port
local server_conf = "server {{remark}} {{server}} weight {{weight}} {{resolvers}} check {{inter_arg}} rise 2 fall 2 {{backup}}"
local server = server_endpoint(o)
local check_arg = "check " .. inter_arg .. " rise 2 fall 2"
if (check_counts[server] or 0) > 1 then
local name = shared_check_names[server]
if not name then
name = "check_" .. (#shared_checks + 1)
shared_check_names[server] = name
table.insert(shared_checks, {name = name, server = server})
end
check_arg = "track " .. appname .. "_health_checks/" .. name
end
local server_conf = "server {{remark}} {{server}} weight {{weight}} {{resolvers}} {{check_arg}} {{backup}}"
server_conf = server_conf:gsub("{{remark}}", remark)
server_conf = server_conf:gsub("{{server}}", server)
server_conf = server_conf:gsub("{{weight}}", o.lbweight)
@@ -203,7 +237,7 @@ listen %s
resolvers = ""
end
server_conf = server_conf:gsub("{{resolvers}}", resolvers)
server_conf = server_conf:gsub("{{inter_arg}}", inter_arg)
server_conf = server_conf:gsub("{{check_arg}}", check_arg)
server_conf = server_conf:gsub("{{backup}}", tostring(o.backup) == "1" and "backup" or "")
f_out:write(" " .. api.trim(server_conf) .. "\n")
@@ -216,6 +250,20 @@ listen %s
end
end
-- 独立检测 backend,避免入口服务器的手动维护状态传播到其他入口。
if #shared_checks > 0 then
f_out:write(string.format([[
backend %s_health_checks
mode tcp
option external-check
external-check command "/usr/share/%s/haproxy_check.sh"
]], appname, appname))
for _, check in ipairs(shared_checks) do
f_out:write(string.format("\tserver %s %s check %s rise 2 fall 2\n", check.name, check.server, inter_arg))
end
end
--控制台配置
local console_user = api.uci_get_c("@global_haproxy[0]", "console_user")
local console_password = api.uci_get_c("@global_haproxy[0]", "console_password")
@@ -234,8 +282,7 @@ f_out:close()
--passwall内置健康检查URL
if health_check_type == "script_logic" then
local probeUrl = api.uci_get_c("@global_haproxy[0]", "health_probe_url") or "https://www.google.com/generate_204"
local f_url = io.open(haproxy_path .. "/Probe_URL", "w")
f_url:write(probeUrl)
f_url:close()
local probeUrl = api.trim(api.uci_get_c("@global_haproxy[0]", "health_probe_url") or "")
if probeUrl == "" then probeUrl = "https://www.google.com/generate_204" end
api.set_cache_var("HAPROXY_PROBE_URL", probeUrl)
end
@@ -1,7 +1,7 @@
#!/bin/sh
export PATH=/usr/sbin:/usr/bin:/sbin:/bin:/root/bin
CONFIG=passwall
. /usr/share/passwall/utils.sh
listen_address=$1
listen_port=$2
@@ -13,19 +13,16 @@ busybox pgrep -af "${CONFIG}/" | grep -E 'app\.sh.*(start|stop)|nftables\.sh|ipt
exit 0
}
probe_file="/tmp/etc/passwall/haproxy/Probe_URL"
probeUrl="https://www.google.com/generate_204"
if [ -f "$probe_file" ]; then
firstLine=$(head -n 1 "$probe_file" | tr -d ' \t\n')
[ -n "$firstLine" ] && probeUrl="$firstLine"
fi
probeUrl=$(get_cache_var "HAPROXY_PROBE_URL")
probeUrl="${probeUrl:-https://www.google.com/generate_204}"
extra_params="-x socks5h://${server_address}:${server_port}"
if /usr/bin/curl --help all | grep -q "\-\-retry-all-errors"; then
extra_params="${extra_params} --retry-all-errors"
fi
case "$server_address" in
\[*\]) ;;
*:*) server_address="[$server_address]" ;;
esac
status=$(/usr/bin/curl -I -o /dev/null -skL ${extra_params} --connect-timeout 3 --retry 1 --max-time 6 -w "%{http_code}" "${probeUrl}")
# 每轮只探测一次,连续成功/失败由 HAProxy 的 rise/fall 判定。
status=$(/usr/bin/curl -q -I -o /dev/null -sk --noproxy "" -x "socks5h://${server_address}:${server_port}" --connect-timeout 3 --max-time 6 -w "%{http_code}" "$probeUrl") || exit 1
case "$status" in
200|204)
@@ -73,7 +73,7 @@ url_test_node() {
fi
sleep 2s
probeUrl=$(config_n_get @global_other[0] url_test_url https://www.google.com/generate_204)
result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -skL -w "%{http_code}:%{time_pretransfer}" "$@" -x "$curlx" "${probeUrl}")
result=$(curl --connect-timeout 3 --max-time 5 -o /dev/null -I -sk -w "%{http_code}:%{time_pretransfer}:%{time_starttransfer}" "$@" -x "$curlx" "${probeUrl}") || result=0
# 结束 SS 插件进程
if [ "$_type" != "socks" ]; then
pid_file="${TMP_PATH}/${test_flag}_plugin.pid"