fix: Gesamt-Timeout für HTTP-Anfragen

I2: SO_RCVTIMEO=5s war ein Timeout pro recv(), nicht für die gesamte
Anfrage. Ein Client, der 1 Byte alle 4,9 s schickt, konnte den einzigen
HTTP-Server-Task damit stundenlang belegen (Spec §7.2 will 5 s pro Header).

serve() rechnet jetzt eine absolute Deadline (nl_app_now_ms() + 5000 ms)
und setzt SO_RCVTIMEO vor jedem recv() auf die verbleibende Zeit (mindestens
1 ms); ist die Deadline überschritten, wird die Verbindung sofort verworfen.

Nicht host-testbar (nl_http_server.c bindet SDK-Sockets ein); über den
Firmware-Build verifiziert.
This commit is contained in:
Michael Sauer
2026-09-11 23:51:11 +02:00
parent 3250b89986
commit 5f45b9e996
+18 -2
View File
@@ -33,19 +33,35 @@ static void simple_error(int status, const char *msg)
strcpy(s_resp.body, msg);
}
#define NL_HTTP_REQUEST_TIMEOUT_MS 5000u
static void serve(int fd)
{
struct timeval tv;
nl_http_request_t req;
nl_http_parse_t pr = NL_HTTP_INCOMPLETE;
size_t len = 0, out;
uint32_t deadline = nl_app_now_ms() + NL_HTTP_REQUEST_TIMEOUT_MS;
tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
/* I2: 5 s is a deadline for the whole header+body read, not a per-recv() timeout -
* otherwise a client trickling in 1 byte per <5 s occupies the single server task forever. */
while (pr == NL_HTTP_INCOMPLETE && len < sizeof s_buf) {
int n = recv(fd, s_buf + len, sizeof s_buf - len, 0);
uint32_t now = nl_app_now_ms();
uint32_t remaining_ms;
int n;
if ((int32_t)(deadline - now) <= 0)
return; /* overall deadline passed: drop silently */
remaining_ms = deadline - now;
tv.tv_sec = remaining_ms / 1000u;
tv.tv_usec = (remaining_ms % 1000u) * 1000u;
if (tv.tv_sec == 0 && tv.tv_usec == 0)
tv.tv_usec = 1000; /* at least 1 ms */
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv);
n = recv(fd, s_buf + len, sizeof s_buf - len, 0);
if (n <= 0)
return; /* timeout or closed: drop silently */
len += (size_t)n;