Fix stability test server list and timeouts

This commit is contained in:
Stefan Stidl 2026-05-16 15:22:06 +02:00
parent 8db7cdca80
commit a9197302b0
3 changed files with 60 additions and 16 deletions

View file

@ -96,6 +96,7 @@ if [[ "$MODE" == "frontend" || "$MODE" == "dual" || "$MODE" == "standalone" ]];
SERVER_LIST_URL_ESCAPED=$(printf '%s\n' "$SERVER_LIST_URL" | sed 's/[&/\\]/\\&/g; s/\$/\\$/g')
sed -i "s/var SPEEDTEST_SERVERS = \"server-list.json\";/var SPEEDTEST_SERVERS = \"$SERVER_LIST_URL_ESCAPED\";/" /var/www/html/index-modern.html
sed -i "s/var SPEEDTEST_SERVERS = \\[/var SPEEDTEST_SERVERS = \"$SERVER_LIST_URL_ESCAPED\";\\n\\t\\t\\/\\*/" /var/www/html/index-classic.html
sed -i "s/var SPEEDTEST_SERVERS = \"server-list.json\";/var SPEEDTEST_SERVERS = \"$SERVER_LIST_URL_ESCAPED\";/" /var/www/html/stability.html
fi
# The stability page reads the same local server list as the main UI when present.

View file

@ -357,7 +357,8 @@
}
// Server configuration (same pattern as index.html)
var SPEEDTEST_SERVERS = [];
// Set this to a different URL to load the server list from another location.
var SPEEDTEST_SERVERS = "server-list.json";
// State
var worker = null;
@ -394,30 +395,51 @@
// Load server list dynamically (Docker exposes server-list.json; older setups may expose servers.json)
function loadServers(callback) {
if (Array.isArray(SPEEDTEST_SERVERS)) {
callback();
return;
}
var serverListUrl = typeof SPEEDTEST_SERVERS === "string" ? SPEEDTEST_SERVERS : "";
if (!serverListUrl) {
SPEEDTEST_SERVERS = [];
callback();
return;
}
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var loadedServers = null;
try {
var servers = JSON.parse(xhr.responseText);
if (Array.isArray(servers) && servers.length > 0) {
SPEEDTEST_SERVERS = servers;
if (Array.isArray(servers)) {
loadedServers = servers;
}
} catch (e) {}
if (SPEEDTEST_SERVERS.length > 0 || xhr._fallbackTried) callback();
else {
if (loadedServers !== null) {
SPEEDTEST_SERVERS = loadedServers;
callback();
} else if (!xhr._fallbackTried) {
xhr._fallbackTried = true;
xhr.open("GET", "servers.json?r=" + Math.random());
xhr.send();
} else {
SPEEDTEST_SERVERS = [];
callback();
}
};
xhr.onerror = function () {
if (xhr._fallbackTried) callback();
else {
if (xhr._fallbackTried) {
SPEEDTEST_SERVERS = [];
callback();
} else {
xhr._fallbackTried = true;
xhr.open("GET", "servers.json?r=" + Math.random());
xhr.send();
}
};
xhr.open("GET", "server-list.json?r=" + Math.random());
xhr.open("GET", serverListUrl + (serverListUrl.match(/\?/) ? "&" : "?") + "r=" + Math.random());
try {
xhr.timeout = 2000;
xhr.ontimeout = xhr.onerror;
@ -515,8 +537,10 @@
workerSettings.mpot = true;
}
worker = new Worker("stability_worker.js?r=" + Math.random());
var currentWorker = new Worker("stability_worker.js?r=" + Math.random());
worker = currentWorker;
worker.onmessage = function (e) {
if (worker !== currentWorker) return;
var data = JSON.parse(e.data);
latestData = data;
@ -554,12 +578,13 @@
}
// request final status
if (worker) {
worker.postMessage("status");
var stoppedWorker = worker;
stoppedWorker.postMessage("status");
setTimeout(function () {
if (worker) {
try {
worker.terminate();
} catch (e) {}
try {
stoppedWorker.terminate();
} catch (e) {}
if (worker === stoppedWorker) {
worker = null;
}
}, 500);

View file

@ -26,6 +26,7 @@ let settings = {
url_ping_external: "", // external URL to ping (uses fetch no-cors, e.g. "https://www.google.com/generate_204")
duration: 60, // seconds
ping_interval: 200, // minimum ms between pings to limit sample rate
ping_timeout: 5000,
ping_allowPerformanceApi: true,
mpot: false
};
@ -202,7 +203,7 @@ function doPing() {
true
);
try {
xhr.timeout = 5000;
xhr.timeout = settings.ping_timeout;
} catch (e) {}
xhr.send();
}
@ -210,16 +211,33 @@ function doPing() {
// ping an external host using fetch with no-cors (opaque response, but timing still works)
function doPingExternal() {
const prevT = new Date().getTime();
const remainingMs = Math.max(1, settings.duration * 1000 - (prevT - startTime));
const timeoutMs = Math.min(settings.ping_timeout, remainingMs);
const url =
settings.url_ping_external + (settings.url_ping_external.indexOf("?") >= 0 ? "&" : "?") + "r=" + Math.random();
fetch(url, { mode: "no-cors", cache: "no-store" })
let timeoutId = null;
const controller = typeof AbortController !== "undefined" ? new AbortController() : null;
const fetchOptions = { mode: "no-cors", cache: "no-store" };
if (controller) fetchOptions.signal = controller.signal;
const timeout = new Promise(function (_, reject) {
timeoutId = setTimeout(function () {
if (controller) controller.abort();
reject(new Error("timeout"));
}, timeoutMs);
});
Promise.race([fetch(url, fetchOptions), timeout])
.then(function () {
if (timeoutId) clearTimeout(timeoutId);
if (aborted || testState >= 4) return;
const instspd = new Date().getTime() - prevT;
recordPing(instspd);
schedulePing(instspd);
})
.catch(function () {
if (timeoutId) clearTimeout(timeoutId);
if (aborted || testState >= 4) return;
recordLoss();
schedulePing(0);