Parcourir la source

Fix stability test server list and timeouts

Stefan Stidl il y a 2 mois
Parent
commit
7a995280a2
3 fichiers modifiés avec 79 ajouts et 18 suppressions
  1. 1 0
      docker/entrypoint.sh
  2. 58 16
      stability.html
  3. 20 2
      stability_worker.js

+ 1 - 0
docker/entrypoint.sh

@@ -102,6 +102,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
 
   # Replace title placeholders if TITLE is set

+ 58 - 16
stability.html

@@ -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;
@@ -385,22 +386,60 @@
       var CHART_PADDING_TOP = 15;
       var CHART_PADDING_BOTTOM = 30;
 
-      // Load server list dynamically (for Docker frontend/dual modes where servers.json exists)
+      function joinServerUrl(server, path) {
+        if (!server) return path;
+        if (!path) return server;
+        if (server.charAt(server.length - 1) === "/" || path.charAt(0) === "/") return server + path;
+        return server + "/" + path;
+      }
+
+      // 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) {}
-          callback();
+
+          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 () {
-          callback();
+          if (xhr._fallbackTried) {
+            SPEEDTEST_SERVERS = [];
+            callback();
+          } else {
+            xhr._fallbackTried = true;
+            xhr.open("GET", "servers.json?r=" + Math.random());
+            xhr.send();
+          }
         };
-        xhr.open("GET", "servers.json?r=" + Math.random());
+        xhr.open("GET", serverListUrl + (serverListUrl.match(/\?/) ? "&" : "?") + "r=" + Math.random());
         try {
           xhr.timeout = 2000;
           xhr.ontimeout = xhr.onerror;
@@ -442,8 +481,8 @@
       }
 
       function pingServer(server, callback) {
-        var url =
-          server.server + server.pingURL + (server.pingURL.match(/\?/) ? "&" : "?") + "cors=true&r=" + Math.random();
+        var baseUrl = joinServerUrl(server.server, server.pingURL);
+        var url = baseUrl + (baseUrl.match(/\?/) ? "&" : "?") + "cors=true&r=" + Math.random();
         var xhr = new XMLHttpRequest();
         var t = new Date().getTime();
         xhr.onload = function () {
@@ -494,12 +533,14 @@
         };
 
         if (!externalTarget && selectedServer) {
-          workerSettings.url_ping = selectedServer.server + selectedServer.pingURL;
+          workerSettings.url_ping = joinServerUrl(selectedServer.server, selectedServer.pingURL);
           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;
 
@@ -537,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);

+ 20 - 2
stability_worker.js

@@ -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);