#include #include "wm_include.h" #include "wm_sockets.h" #include "net/nl_http.h" #include "net/nl_http_server.h" #include "sys/nl_app.h" #define NL_HTTP_PRIO 42 static u32 s_stack[1536]; /* 6 KB: cJSON + API handler */ static char s_buf[NL_HTTP_MAX_REQUEST]; static char s_out[NL_API_BODY_MAX + 160]; static nl_api_response_t s_resp; static const nl_api_ctx_t *s_ctx; static void send_all(int fd, const char *p, size_t n) { while (n) { int w = send(fd, p, n, 0); if (w <= 0) return; p += w; n -= (size_t)w; } } static void simple_error(int status, const char *msg) { s_resp.status = status; s_resp.after = NL_API_AFTER_NONE; 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_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) { 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; pr = nl_http_parse(s_buf, len, &req); } if (pr == NL_HTTP_COMPLETE) nl_api_handle(s_ctx, &req, &s_resp); else if (pr == NL_HTTP_BAD_REQUEST) simple_error(400, "{\"error\":\"bad request\"}"); else simple_error(413, "{\"error\":\"request too large\"}"); out = nl_http_response(s_out, sizeof s_out, s_resp.status, s_resp.body); if (out) send_all(fd, s_out, out); } static void server_task(void *arg) { (void)arg; for (;;) { struct sockaddr_in addr; int ls = socket(AF_INET, SOCK_STREAM, 0); memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_port = htons(NL_HTTP_PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY); if (ls < 0 || bind(ls, (struct sockaddr *)&addr, sizeof addr) != 0 || listen(ls, 4) != 0) { if (ls >= 0) closesocket(ls); tls_os_time_delay(HZ); continue; } for (;;) { struct sockaddr_in peer; socklen_t plen = sizeof peer; int fd = accept(ls, (struct sockaddr *)&peer, &plen); if (fd < 0) { tls_os_time_delay(HZ / 10); continue; } s_resp.after = NL_API_AFTER_NONE; serve(fd); closesocket(fd); if (s_resp.after == NL_API_AFTER_REBOOT) { tls_os_time_delay(HZ / 2); /* let the TCP stack flush the response */ nl_app_reboot(); } } } } void nl_http_server_start(const nl_api_ctx_t *ctx) { s_ctx = ctx; tls_os_task_create(NULL, "nl_http", server_task, NULL, (u8 *)s_stack, sizeof(s_stack), NL_HTTP_PRIO, 0); }