Files
HaloKeymind/scripts/esp32_async_web_fix.py
T

60 lines
2.6 KiB
Python

"""Build private AsyncTCP fixes and the current vendored OTA implementation."""
import hashlib
from pathlib import Path
PINNED_ASYNCTCP_SHA256 = "27e3e22a6030cc5c9c9f32f28d0a3a36189b1ea6a5fa270777b2d2790a89c365"
ORIGINAL_BIND = " msg->err = tcp_bind(pcb, msg->bind.addr, msg->bind.port);"
REUSABLE_BIND = """ // A stopped HTTP listener may still have completed connections in
// TIME_WAIT. Permit immediate rebinding during WebConfig/OTA handoffs.
#if SO_REUSE
ip_set_option(pcb, SOF_REUSEADDR);
#endif
""" + ORIGINAL_BIND
def patched_async_tcp(source):
source = source.replace("\r\n", "\n")
if (hashlib.sha256(source.encode("ascii")).hexdigest() != PINNED_ASYNCTCP_SHA256
or source.count(ORIGINAL_BIND) != 1):
raise RuntimeError("AsyncTCP restart fix: changed pinned source; review library update")
return source.replace(ORIGINAL_BIND, REUSABLE_BIND, 1)
def replace_async_source(build_env, node):
source = Path(node.srcnode().get_abspath())
if source.name == "AsyncTCP.cpp":
content = patched_async_tcp(source.read_text(encoding="utf-8"))
include_dir = source.parent
elif source.name == "AsyncElegantOTA.cpp":
# file:// dependencies can retain an earlier copy in .pio/libdeps.
# Compile this checkout's vendored source without editing that cache.
include_dir = Path(build_env.subst("$PROJECT_DIR")) / "arch/esp32/AsyncElegantOTA/src"
content = (include_dir / source.name).read_text(encoding="ascii")
else:
return node
destination = Path(build_env.subst("$BUILD_DIR")) / "patched-esp32-web" / source.name
destination.parent.mkdir(parents=True, exist_ok=True)
if source.name == "AsyncElegantOTA.cpp":
# Quoted includes resolve beside this private source, ahead of any
# stale library include path inherited by PlatformIO's build clone.
for name in ("AsyncElegantOTA.h", "Hash.h", "elegantWebpage.h"):
header = (include_dir / name).read_bytes()
target = destination.parent / name
if not target.exists() or target.read_bytes() != header:
target.write_bytes(header)
if not destination.exists() or destination.read_text(encoding="ascii") != content:
destination.write_text(content, encoding="ascii")
build_env.PrependUnique(CPPPATH=[str(include_dir)])
return build_env.File(str(destination))
def install(build_env):
build_env.AddBuildMiddleware(replace_async_source, "*AsyncTCP*src*AsyncTCP.cpp")
build_env.AddBuildMiddleware(replace_async_source, "*AsyncElegantOTA*src*AsyncElegantOTA.cpp")
if "Import" in globals():
Import("env")
install(env)