From 70f59b64f546e6205158778bb555ca0fe39c98d4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 4 Oct 2026 07:12:05 +0000 Subject: [PATCH] httpd: four login sessions instead of one Logging in from a second browser ended the first one's session at its next request: two browsers open on the switch logged each other out every few seconds. Up to four sessions now, each with its own cookie and expiry; a fifth login replaces the least recently used. The expiry check is a leaf function so its 32-bit scratch stays out of internal RAM (stack still 159 bytes with HEALTH). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Tx2W7xVecMoKbBCkD2i8PG (cherry picked from commit 03330f4077ef8872d861d951c8e44ece3c573935) --- httpd/httpd.c | 64 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/httpd/httpd.c b/httpd/httpd.c index 688513f8..93828d34 100644 --- a/httpd/httpd.c +++ b/httpd/httpd.c @@ -75,10 +75,15 @@ __xdata uint8_t postbody_endpoint; __xdata uint16_t postbody_start; __xdata char passwd[21]; -__xdata char session_id[SESSION_ID_LENGTH + 1]; +/* Up to SESSIONS logins at once, each with its own cookie and its own + * expiry. With a single one, logging in from a second browser (or a phone) + * ended the first browser's session at its next request. */ +#define SESSIONS 4 +__xdata char session_id[SESSIONS][SESSION_ID_LENGTH + 1]; +__xdata uint32_t last_session_use[SESSIONS]; +__xdata uint8_t session_slot; __xdata uint8_t authenticated; __xdata uint32_t now; -__xdata uint32_t last_session_use; #define TSTATE_NONE 0 #define TSTATE_TX 1 @@ -109,6 +114,33 @@ void httpd_init(void) __banked // handling one, and nothing has set it yet at init time. for (uint8_t i = 0; i < UIP_CONNS; i++) uip_conns[i].appstate.tstate = TSTATE_CLOSED; + for (session_slot = 0; session_slot < SESSIONS; session_slot++) + session_id[session_slot][0] = NUL; +} + +/* Leaf functions, so the compiler keeps their 32-bit scratch in the overlay + * rather than in internal RAM. */ +static uint8_t session_stale(void) +{ + if (now - last_session_use[session_slot] > session_timeout) + return 1; + return 0; +} + +/* For a new login: a free or expired slot, else the least recently used. */ +static void session_pick(void) +{ + __xdata uint8_t i; + __xdata uint8_t oldest = 0; + + for (i = 0; i < SESSIONS; i++) { + session_slot = i; + if (!session_id[i][0] || session_stale()) + return; + if (last_session_use[i] < last_session_use[oldest]) + oldest = i; + } + session_slot = oldest; } @@ -343,15 +375,17 @@ __xdata uint8_t *scan_header(__xdata uint8_t * __xdata p) read_reg_timer(&now); - if (session && session_id[0]) { - if (now - last_session_use > session_timeout) { - dbg_string("Session expired\n"); - } else { - if (is_word_x(session, session_id)) { + if (session) { + for (session_slot = 0; session_slot < SESSIONS; session_slot++) { + if (!session_id[session_slot][0]) + continue; + if (session_stale()) { + dbg_string("Session expired\n"); + session_id[session_slot][0] = NUL; + } else if (is_word_x(session, session_id[session_slot])) { authenticated = 1; - last_session_use = now; - } else { - dbg_string("Invalid session cookie!\n"); + last_session_use[session_slot] = now; + break; } } } @@ -675,13 +709,15 @@ static void run_login_body(__xdata uint8_t *body) { if (strstart(body, "pwd=") && is_url_word_x(body + 4, passwd)) { dbg_string("Password accepted!\n"); - read_reg_timer(&last_session_use); - gen_random_hex_chars(session_id, SESSION_ID_LENGTH); - session_id[SESSION_ID_LENGTH] = NUL; + read_reg_timer(&now); + session_pick(); + last_session_use[session_slot] = now; + gen_random_hex_chars(session_id[session_slot], SESSION_ID_LENGTH); + session_id[session_slot][SESSION_ID_LENGTH] = NUL; slen = strtox(outbuf, "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: index.html\r\n" \ "Set-Cookie: session="); for (uint8_t i = 0; i < SESSION_ID_LENGTH; i++) - outbuf[slen++] = session_id[i]; + outbuf[slen++] = session_id[session_slot][i]; slen += strtox(outbuf + slen, "; SameSite=Strict\r\n\r\n"); } else { dbg_string("Password invalid!\n");