Просмотр исходного кода

Web: replace unreliable post-OTA device poll with a countdown timer

After a successful OTA upload the web panel polled the device by
fetching "/" and waiting for a 200 OK response.  This approach
is fundamentally broken when the new firmware contains a freshly
generated self-signed certificate (e.g. from a clean CI build):

- The browser rejects the TLS handshake with a certificate error
  (MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT in Firefox; other browsers
  report different error names but behave identically).  The fetch()
  call throws a generic TypeError — the same type thrown for any other
  network failure such as a refused or timed-out connection.  Browsers
  intentionally provide no way to distinguish a cert rejection from a
  plain connectivity failure, so the catch block cannot tell whether
  the device is up-but-cert-changed or simply not yet online.

- Switching to an explicit http:// URL to sidestep TLS is not an
  option: the web panel is served over HTTPS, and browsers block
  active mixed content (fetch/XHR to HTTP) unconditionally from an
  HTTPS origin.

Because no network-based probe can reliably detect device readiness
under these constraints without changing how the certificate is
generated, replace the polling loop with a simple 10-second countdown
timer that unconditionally redirects to the login page.  The device
is typically back online within ~7 seconds, so the 10-second wait
provides a reasonable safety margin.
Valentin V. Bartenev 2 месяцев назад
Родитель
Сommit
59f6df50c5
1 измененных файлов с 8 добавлено и 16 удалено
  1. 8 16
      src/helpers/web/WebPanelServer.cpp

+ 8 - 16
src/helpers/web/WebPanelServer.cpp

@@ -2641,22 +2641,14 @@ const char kWebPanelAppHtml[] PROGMEM = R"HTML(
         }
         if (xhr.status === 200) {
           barEl.style.width = "100%";
-          msgEl.textContent = "Done. Device is rebooting";
-          let wait = 4;
-          const iv = setInterval(async () => {
-            msgEl.textContent += '.';
-            if (msgEl.textContent.length > 85) { clearInterval(iv); msgEl.textContent = "Device did not come back online. =("; return; }
-            if (--wait) { return; }
-            wait = 1000; // prevent concurrent fetch
-            try {
-              if ((await fetch("/", { cache: "no-store" })).ok) {
-                clearInterval(iv);
-                msgEl.textContent = "Device is back online. Redirecting to login...";
-                setTimeout(redirectToLogin, 1500);
-                return;
-              }
-            } catch (_) {}
-            wait = 4;
+          let secs = 10;
+          const iv = setInterval(() => {
+            if (--secs <= 0) {
+              clearInterval(iv);
+              redirectToLogin();
+              return;
+            }
+            msgEl.textContent = `Done. Device is rebooting. Redirecting in ${secs}s...`;
           }, 1000);
         } else {
           msgEl.textContent = "Failed: " + (xhr.responseText || String(xhr.status));