mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
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:
4 files changed
+88
-1
No files matched your search
+1
-1
@@ -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;
|
||||
|
||||
@@ -96,6 +96,21 @@ typedef unsigned short uip_stats_t;
|
||||
*/
|
||||
#define UIP_CONF_MAX_CONNECTIONS 1
|
||||
|
||||
/**
|
||||
* This httpd closes after every response, so an ESTABLISHED connection
|
||||
* that stays idle is a peer that died or never sent its request. With a
|
||||
* single connection slot it would hold the web UI until the next power
|
||||
* cycle, so it is aged out instead.
|
||||
*
|
||||
* uip_periodic() runs from idle() once per system tick; interrupt
|
||||
* wake-ups only add sweeps, so the timeout can fire early but never
|
||||
* late.
|
||||
*
|
||||
* \hideinitializer
|
||||
*/
|
||||
#define UIP_CONF_IDLE_PERIODS 200
|
||||
#define UIP_CONF_IDLE_TIMEOUT 30
|
||||
|
||||
/**
|
||||
* Maximum number of listening TCP ports. TODO: increase this!
|
||||
*
|
||||
|
||||
@@ -171,6 +171,11 @@ __xdata struct uip_udp_conn * __xdata uip_udp_conn;
|
||||
__xdata struct uip_udp_conn uip_udp_conns[UIP_UDP_CONNS];
|
||||
#endif /* UIP_UDP */
|
||||
|
||||
#if UIP_IDLE_TIMEOUT
|
||||
__xdata static u8_t uip_idle_prescale;
|
||||
__xdata static u8_t uip_idle_age;
|
||||
#endif /* UIP_IDLE_TIMEOUT */
|
||||
|
||||
__xdata static u16_t ipid; /* Ths ipid variable is an increasing
|
||||
number that is used for the IP ID
|
||||
field. */
|
||||
@@ -702,6 +707,15 @@ uip_process(u8_t flag) __banked
|
||||
/* Check if we were invoked because of the perodic timer fireing. */
|
||||
} else if(flag == UIP_TIMER) {
|
||||
// print_string("T1\n");
|
||||
#if UIP_IDLE_TIMEOUT
|
||||
if(uip_connr == uip_conns) {
|
||||
uip_idle_age = 0;
|
||||
if(++uip_idle_prescale >= UIP_IDLE_PERIODS) {
|
||||
uip_idle_prescale = 0;
|
||||
uip_idle_age = 1;
|
||||
}
|
||||
}
|
||||
#endif /* UIP_IDLE_TIMEOUT */
|
||||
#if UIP_REASSEMBLY
|
||||
if(uip_reasstmr != 0) {
|
||||
--uip_reasstmr;
|
||||
@@ -733,6 +747,21 @@ uip_process(u8_t flag) __banked
|
||||
/* If the connection has outstanding data, we increase the
|
||||
connection's timer and see if it has reached the RTO value
|
||||
in which case we retransmit. */
|
||||
#if UIP_IDLE_TIMEOUT
|
||||
if(!uip_outstanding(uip_connr)) {
|
||||
/* The retransmission timer is unused while nothing is in flight,
|
||||
so idle time is counted in it instead. */
|
||||
if(uip_idle_age &&
|
||||
(uip_connr->tcpstateflags & UIP_TS_MASK) == UIP_ESTABLISHED &&
|
||||
++(uip_connr->timer) >= UIP_IDLE_TIMEOUT) {
|
||||
uip_connr->tcpstateflags = UIP_CLOSED;
|
||||
uip_flags = UIP_TIMEDOUT;
|
||||
UIP_APPCALL();
|
||||
BUF->flags = TCP_RST | TCP_ACK;
|
||||
goto tcp_send_nodata;
|
||||
}
|
||||
}
|
||||
#endif /* UIP_IDLE_TIMEOUT */
|
||||
if(uip_outstanding(uip_connr)) {
|
||||
if(uip_connr->timer-- == 0) {
|
||||
if(uip_connr->nrtx == UIP_MAXRTX ||
|
||||
@@ -1550,6 +1579,11 @@ uip_process(u8_t flag) __banked
|
||||
#endif /* UIP_ACTIVE_OPEN */
|
||||
|
||||
case UIP_ESTABLISHED:
|
||||
#if UIP_IDLE_TIMEOUT
|
||||
if(!uip_outstanding(uip_connr)) {
|
||||
uip_connr->timer = 0;
|
||||
}
|
||||
#endif /* UIP_IDLE_TIMEOUT */
|
||||
/* In the ESTABLISHED state, we call upon the application to feed
|
||||
data into the uip_buf. If the UIP_ACKDATA flag is set, the
|
||||
application should put new data into the buffer, otherwise we are
|
||||
@@ -1576,6 +1610,9 @@ uip_process(u8_t flag) __banked
|
||||
uip_connr->len = 1;
|
||||
uip_connr->tcpstateflags = UIP_LAST_ACK;
|
||||
uip_connr->nrtx = 0;
|
||||
#if UIP_IDLE_TIMEOUT
|
||||
uip_connr->timer = uip_connr->rto;
|
||||
#endif /* UIP_IDLE_TIMEOUT */
|
||||
tcp_send_finack:
|
||||
BUF->flags = TCP_FIN | TCP_ACK;
|
||||
goto tcp_send_nodata;
|
||||
@@ -1665,6 +1702,9 @@ uip_process(u8_t flag) __banked
|
||||
uip_connr->len = 1;
|
||||
uip_connr->tcpstateflags = UIP_FIN_WAIT_1;
|
||||
uip_connr->nrtx = 0;
|
||||
#if UIP_IDLE_TIMEOUT
|
||||
uip_connr->timer = uip_connr->rto;
|
||||
#endif /* UIP_IDLE_TIMEOUT */
|
||||
BUF->flags = TCP_FIN | TCP_ACK;
|
||||
goto tcp_send_nodata;
|
||||
}
|
||||
@@ -1693,6 +1733,9 @@ uip_process(u8_t flag) __banked
|
||||
/* Remember how much data we send out now so that we know
|
||||
when everything has been acknowledged. */
|
||||
uip_connr->len = uip_slen;
|
||||
#if UIP_IDLE_TIMEOUT
|
||||
uip_connr->timer = uip_connr->rto;
|
||||
#endif /* UIP_IDLE_TIMEOUT */
|
||||
} else {
|
||||
|
||||
/* If the application already had unacknowledged data, we
|
||||
|
||||
@@ -324,6 +324,35 @@
|
||||
*/
|
||||
#define UIP_TIME_WAIT_TIMEOUT 120
|
||||
|
||||
/**
|
||||
* The number of UIP_TIMER sweeps that make one idle-aging period,
|
||||
* i.e. how many times uip_periodic() runs per second in this project.
|
||||
* Counted in an 8-bit variable, so 255 is the maximum.
|
||||
*
|
||||
* \hideinitializer
|
||||
*/
|
||||
#ifndef UIP_CONF_IDLE_PERIODS
|
||||
#define UIP_IDLE_PERIODS 2
|
||||
#else /* UIP_CONF_IDLE_PERIODS */
|
||||
#define UIP_IDLE_PERIODS UIP_CONF_IDLE_PERIODS
|
||||
#endif /* UIP_CONF_IDLE_PERIODS */
|
||||
|
||||
/**
|
||||
* The number of idle-aging periods after which an ESTABLISHED
|
||||
* connection with a silent peer is aborted, or 0 to keep such
|
||||
* connections forever (the classic uIP behaviour). TCP itself has no
|
||||
* idle timeout, so a peer that vanished without FIN or RST would
|
||||
* otherwise occupy its connection slot until a reboot. Counted in the
|
||||
* connection's 8-bit retransmission timer, so 255 is the maximum.
|
||||
*
|
||||
* \hideinitializer
|
||||
*/
|
||||
#ifndef UIP_CONF_IDLE_TIMEOUT
|
||||
#define UIP_IDLE_TIMEOUT 0
|
||||
#else /* UIP_CONF_IDLE_TIMEOUT */
|
||||
#define UIP_IDLE_TIMEOUT UIP_CONF_IDLE_TIMEOUT
|
||||
#endif /* UIP_CONF_IDLE_TIMEOUT */
|
||||
|
||||
|
||||
/** @} */
|
||||
/*------------------------------------------------------------------------------*/
|
||||
|
||||
Reference in new issue
Block a user