Use gzip fallback for WebConfig clients

This commit is contained in:
Kyle Duren committed 2026-09-30 17:07:59 -04:00
1 parent 70d183a3ae
commit 22aa62e7d6
3 files changed
+2227 -7403

No files matched your search

+11 -9
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env python
#
# Pre-build script: Brotli-compress webui/index.html into a PROGMEM C header so the
# Pre-build script: compress webui/index.html into a PROGMEM C header so the
# webconfig portal can serve the page straight from flash with
# Content-Encoding: br. The generated header is tracked so ESP32 targets
# Content-Encoding: br or gzip. The generated header is tracked so ESP32 targets
# that replace extra_scripts still compile from a clean checkout; this script
# rewrites it only when the emitted firmware bytes actually change.
#
@@ -12,8 +12,8 @@
#
# Output: src/helpers/esp32/WebConfigHtml.h
# WEBCONFIG_HTML_LOADER[] - small bootstrap page (PROGMEM)
# WEBCONFIG_HTML_RAW[] - complete stripped page (PROGMEM)
# WEBCONFIG_HTML_BR[] - complete Brotli-compressed page (PROGMEM)
# WEBCONFIG_HTML_GZ[] - complete gzip-compressed page (PROGMEM)
# WEBCONFIG_HTML_ETAG - quoted strong ETag for the loader page
#
# Runnable outside SCons to inspect exactly what gets shipped:
@@ -21,6 +21,7 @@
# or served directly by the mock backend with its --minify flag.
import hashlib
import gzip
import os
import sys
@@ -92,6 +93,7 @@ def main():
src_hash = content_hash(raw)
br = compress_html(raw)
gz = gzip.compress(raw, compresslevel=9, mtime=0)
version = hashlib.sha256(br).hexdigest()[:16]
loader = ("<!doctype html><meta charset=utf-8><meta name=viewport "
"content=\"width=device-width,initial-scale=1\"><title>MeshCore "
@@ -112,24 +114,24 @@ def main():
lines = []
lines.append("// Auto-generated by scripts/generate_webconfig_html.py from %s" % SOURCE.replace(os.sep, "/"))
lines.append("// DO NOT EDIT - edit webui/index.html instead.")
lines.append("// Compressed with Google Brotli %s (quality %d, lgwin %d)." %
lines.append("// Compressed with Google Brotli %s (quality %d, lgwin %d) and gzip." %
(BROTLI_VERSION, QUALITY, LGWIN))
lines.append("%s%s" % (HASH_MARKER, src_hash))
lines.append("#pragma once")
lines.append("#include <stdint.h>")
lines.append("#include <pgmspace.h>")
lines.append("")
lines.append("const uint32_t WEBCONFIG_HTML_RAW_LEN = %d;" % len(raw))
lines.append("const uint32_t WEBCONFIG_HTML_BR_LEN = %d;" % len(br))
lines.append("const uint32_t WEBCONFIG_HTML_GZ_LEN = %d;" % len(gz))
lines.append('const char WEBCONFIG_HTML_ETAG[] = "\\"%s\\"";' % etag)
lines.append('const char WEBCONFIG_HTML_VERSION[] = "%s";' % version)
lines.append("const uint16_t WEBCONFIG_HTML_LOADER_LEN = %d;" % len(loader_bytes))
append_byte_array(lines, "WEBCONFIG_HTML_LOADER", loader_bytes)
lines.append("")
append_byte_array(lines, "WEBCONFIG_HTML_RAW", raw)
lines.append("")
append_byte_array(lines, "WEBCONFIG_HTML_BR", br)
lines.append("")
append_byte_array(lines, "WEBCONFIG_HTML_GZ", gz)
lines.append("")
rendered = "\n".join(lines)
try:
@@ -143,8 +145,8 @@ def main():
with open(OUTPUT, "w", encoding="utf-8", newline="") as f:
f.write(rendered)
status("%s -> %s (%d bytes source, %d stripped, %d Brotli)"
% (SOURCE, OUTPUT, len(src.encode("utf-8")), len(raw), len(br)))
status("%s -> %s (%d bytes source, %d stripped, %d Brotli, %d gzip)"
% (SOURCE, OUTPUT, len(src.encode("utf-8")), len(raw), len(br), len(gz)))
# This script is attached only to esp32_base. Generate unconditionally here:
File diff suppressed because it is too large. Load diff
+12 -7
View File
@@ -1647,7 +1647,8 @@ void WebConfigServer::handleRoot(AsyncWebServerRequest* req) {
req->send(res);
}
static bool webConfigClientAcceptsBrotli(AsyncWebServerRequest* req) {
static bool webConfigClientAcceptsEncoding(AsyncWebServerRequest* req,
const char* wanted) {
if (!req || !req->hasHeader("Accept-Encoding")) return false;
String encodings = req->getHeader("Accept-Encoding")->value();
encodings.toLowerCase();
@@ -1660,7 +1661,7 @@ static bool webConfigClientAcceptsBrotli(AsyncWebServerRequest* req) {
int semi = item.indexOf(';');
String coding = semi < 0 ? item : item.substring(0, semi);
coding.trim();
if (coding == "br") {
if (coding == wanted || coding == "*") {
if (semi < 0) return true;
String params = item.substring(semi + 1);
params.trim();
@@ -1686,7 +1687,7 @@ void WebConfigServer::handleUi(AsyncWebServerRequest* req) {
return;
}
AsyncWebServerResponse* res = nullptr;
if (webConfigClientAcceptsBrotli(req)) {
if (webConfigClientAcceptsEncoding(req, "br")) {
res = new WebConfigPacedProgmemResponse("text/html; charset=utf-8",
WEBCONFIG_HTML_BR,
WEBCONFIG_HTML_BR_LEN);
@@ -1694,12 +1695,16 @@ void WebConfigServer::handleUi(AsyncWebServerRequest* req) {
// precompressed bytes.
res->addHeader("Content-Encoding", "br");
res->addHeader("Cache-Control", "public, max-age=31536000, immutable");
} else {
// Never send Brotli to clients that did not advertise support for it.
} else if (webConfigClientAcceptsEncoding(req, "gzip")) {
res = new WebConfigPacedProgmemResponse("text/html; charset=utf-8",
WEBCONFIG_HTML_RAW,
WEBCONFIG_HTML_RAW_LEN);
WEBCONFIG_HTML_GZ,
WEBCONFIG_HTML_GZ_LEN);
res->addHeader("Content-Encoding", "gzip");
res->addHeader("Cache-Control", "no-cache");
} else {
req->send(406, "text/plain",
"WebConfig requires Brotli or gzip support");
return;
}
req->send(res);
}