httpd: reap idle TCP connections (#390)

* uip: time out an ESTABLISHED connection whose peer went silent

uIP has no keepalive and no idle timeout for an ESTABLISHED connection, so
a peer that vanishes without FIN or RST holds it open forever. A crashed
client does it, and so does a SYN caught while the switch is still
booting. With UIP_CONF_MAX_CONNECTIONS = 1 that wedges the single slot:
the web UI stops answering, port 80 no longer even SYN-ACKs, and only a
power cycle recovers it. Every other TCP state already has an exit path
(UIP_MAXRTX, UIP_TIME_WAIT_TIMEOUT); idle ESTABLISHED was the one hole.

Close it inside uIP rather than in httpd, so any future TCP service is
covered too. The retransmission timer is unused while nothing is in
flight, so the idle count lives in it: a 1 Hz strobe derived from the
uip_periodic() rate ages it, any arriving segment clears it, and past the
limit the connection is aborted through the same path as UIP_MAXRTX. Two
new options in uipopt.h, default off; this project enables 30 s.

httpd also joins uip_timedout() to its cleanup: it only handled closed
and aborted, so both this timeout and the existing UIP_MAXRTX one would
have left the transfer state stuck on a dead connection.

* uip: keep the tuning values out of the prose

The comment named the timeout and the tick rate, which go stale the
moment either constant is edited. It now says why an idle connection
is aged out and leaves the numbers to the defines under it.
This commit is contained in:
d00f authored and GitHub committed 2026-09-04 07:11:49 +02:00
1 parent e26693826e
commit a7692022f1
4 files changed
+88 -1

No files matched your search

+1 -1
View File
@@ -842,7 +842,7 @@ void httpd_appcall(void)
} else if (uip_closed()) {
dbg_string("Connection closed\n");
s->tstate = TSTATE_CLOSED;
} else if (uip_aborted()) {
} else if (uip_aborted() || uip_timedout()) {
dbg_string("Connection aborted\n");
uip_close();
s->tstate = TSTATE_CLOSED;