Add Bounded Ordinary HTTPS Idle Cleanup

This commit is contained in:
2026-09-08 18:33:33 +02:00
parent f6263042ff
commit 82f21d6116
18 changed files with 884 additions and 13 deletions
+50 -5
View File
@@ -108,7 +108,28 @@ static void secure_wipe(void *p, size_t n) { assert(!locked); memset(p, 0, n); }
HANDLER(root_handler) HANDLER(status_handler) HANDLER(traced_ticket_handler)
HANDLER(traced_websocket_handler) HANDLER(asset_handler) HANDLER(web_cookie_auth_handler)
HANDLER(traced_admin_ticket_handler) HANDLER(traced_admin_upgrade_handler)
static void web_diagnostics_tls(void *arg) { (void)arg; assert(false); }
typedef void esp_https_server_user_cb_arg_t;
static unsigned tls_steps;
static void web_httpd_idle_tls(void *arg) { assert(arg && tls_steps == 0); ++tls_steps; }
static void web_diagnostics_tls(void *arg) { assert(arg && tls_steps == 1); ++tls_steps; }
static void tls_session_callback(esp_https_server_user_cb_arg_t *arg);
static bool idle_owned, idle_fenced;
static esp_err_t idle_prepare_error, idle_attach_error, idle_detach_error;
static unsigned idle_prepares, idle_attaches, idle_detaches, idle_stoppeds;
static esp_err_t web_httpd_idle_prepare(void) {
assert(!locked && !ssl_live && !idle_owned); ++idle_prepares; return idle_prepare_error;
}
static esp_err_t web_httpd_idle_attach(httpd_handle_t s) {
assert(!locked && s == SERVER && ssl_live && !idle_owned); ++idle_attaches;
idle_owned = idle_attach_error == ESP_OK; idle_fenced = false; return idle_attach_error;
}
static esp_err_t web_httpd_idle_detach(httpd_handle_t s) {
assert(!locked && s == SERVER && ssl_live && !auth_live); ++idle_detaches;
idle_fenced = idle_detach_error == ESP_OK; return idle_detach_error;
}
static void web_httpd_idle_stopped(httpd_handle_t s) {
assert(!locked && s == SERVER && !ssl_live && idle_fenced); ++idle_stoppeds; idle_owned = false;
}
HANDLER(serial_settings_handler)
HANDLER(web_serial_settings_handler) HANDLER(web_account_settings_handler)
HANDLER(web_account_generate_password_handler) HANDLER(web_account_keys_handler)
@@ -131,7 +152,8 @@ static esp_err_t httpd_ssl_start(httpd_handle_t *server, const httpd_ssl_config_
assert(config->httpd.max_uri_handlers == 24 && config->port_secure == 443);
assert(config->httpd.recv_wait_timeout == 1 && config->httpd.send_wait_timeout == 1);
assert(config->tls_handshake_timeout_ms == 5000);
assert(config->user_cb == web_diagnostics_tls);
assert(config->user_cb == tls_session_callback);
tls_steps = 0; config->user_cb(&tls_steps); assert(tls_steps == 2);
assert(config->servercert_len == 1 && config->servercert[0] == 1);
assert(config->prvtkey_len == 1 && config->prvtkey_pem[0] == 2);
if (ssl_start_error != ESP_OK) return ssl_start_error;
@@ -233,7 +255,7 @@ static esp_err_t web_serial_transport_detach_server(httpd_handle_t s) {
event('S'); ++serial_detaches; serial_live = false; return ESP_OK;
}
static esp_err_t httpd_ssl_stop(httpd_handle_t s) {
assert(s == SERVER && ssl_live && !auth_live); event('H'); ++ssl_stops;
assert(s == SERVER && ssl_live && !auth_live && idle_fenced); event('H'); ++ssl_stops;
if (ssl_stop_error == ESP_OK) ssl_live = false;
return ssl_stop_error;
}
@@ -253,6 +275,9 @@ static void reset(void) {
s_last_error = s_serial_transport_error = ESP_ERR_INVALID_STATE;
memset(&s_counters, 0, sizeof(s_counters));
mutex_fail = auth_live = ssl_live = admin_owned = serial_live = false;
idle_owned = idle_fenced = false;
idle_prepare_error = idle_attach_error = idle_detach_error = ESP_OK;
idle_prepares = idle_attaches = idle_detaches = idle_stoppeds = 0;
serial_init_error = admin_init_error = admin_attach_error = ESP_OK;
auth_error = ssl_start_error = ssl_stop_error = admin_detach_error = ESP_OK;
serial_inits = admin_inits = auth_starts = auth_stops = ssl_starts = ssl_stops = 0;
@@ -267,7 +292,7 @@ static void fresh_registration(void) { registration_calls = registered_count = 0
static void start(void) {
assert(web_server_start() == ESP_OK);
assert(s_server == SERVER && s_admin_transport_owned && s_serial_transport_attached);
assert(auth_live && ssl_live && admin_owned && serial_live && !s_transitioning);
assert(auth_live && ssl_live && admin_owned && serial_live && !s_transitioning && idle_owned);
}
static const httpd_uri_t *route(const char *uri) {
const httpd_uri_t *found = NULL;
@@ -468,12 +493,32 @@ int main(void) {
assert(route("/api/settings/accounts/keys")->handler == web_account_keys_handler);
assert(web_server_stop() == ESP_OK);
puts("PASS optional account keys allocation failure preserves account/generation/auth/transports; restart recovers");
puts("16 lifecycle groups passed (16 required fatal positions, 10 optional routes, plus failed unregister)");
reset(); idle_prepare_error = ESP_ERR_NO_MEM;
assert(web_server_start() == ESP_ERR_NO_MEM && !ssl_starts && !auth_live);
assert(idle_prepares == 1 && !idle_attaches && !s_server);
reset(); idle_attach_error = ESP_FAIL;
assert(web_server_start() == ESP_FAIL && !ssl_live && !s_server);
assert(idle_detaches == 1 && idle_stoppeds == 1 && !serial_attaches);
puts("PASS idle timer preparation/attachment failures gate startup and clean partial HTTPD");
reset(); start(); idle_detach_error = ESP_ERR_TIMEOUT;
assert(web_server_stop() == ESP_ERR_TIMEOUT && ssl_live && idle_owned);
assert(!ssl_stops && !admin_detaches && !serial_detaches && !idle_stoppeds);
assert(!s_transitioning && s_server == SERVER && web_server_start() == ESP_ERR_INVALID_STATE);
idle_detach_error = ESP_OK; ssl_stop_error = ESP_FAIL;
assert(web_server_stop() == ESP_FAIL && idle_owned && !idle_stoppeds);
ssl_stop_error = ESP_OK;
assert(web_server_stop() == ESP_OK && !idle_owned && idle_stoppeds == 1);
fresh_registration(); start(); assert(web_server_stop() == ESP_OK);
puts("PASS idle submit fence failure forbids SSL destruction; failed stop retains ownership until retry/restart");
puts("18 lifecycle groups passed (16 required fatal positions, 10 optional routes, plus failed unregister)");
return 0;
}
'''
unit = FAKES + header + '\n' + constants + state + '\n'.join(uri_tables)
callback = re.search(r'^static void tls_session_callback\(.*?^\}', source, re.M | re.S)
assert callback
unit += callback.group() + '\n'
unit += function('ensure_mutex')
unit += ''.join(function(name) for name in ('web_server_init', 'web_server_start', 'web_server_stop'))
unit += TESTS