diff seobeo/os/s_linux_edge.c @ 186:8cf4ec5e2191 hg-web

Fixed merge conflict.
author MrJuneJune <me@mrjunejune.com>
date Fri, 23 Jan 2026 22:38:59 -0800
parents a8976a008a9d
children
line wrap: on
line diff
--- a/seobeo/os/s_linux_edge.c	Wed Jan 21 19:32:08 2026 -0800
+++ b/seobeo/os/s_linux_edge.c	Fri Jan 23 22:38:59 2026 -0800
@@ -1,6 +1,27 @@
 #include <sys/epoll.h>
+#include <netinet/tcp.h>
 #include "seobeo/seobeo.h"
 
+// TCP keep-alive settings
+#define KEEP_ALIVE_IDLE_SEC   30  // Start probes after 30s idle
+#define KEEP_ALIVE_INTERVAL   5   // Probe every 5 seconds
+#define KEEP_ALIVE_COUNT      3   // Close after 3 failed probes
+
+// Configure TCP keep-alive on socket (kernel handles timeout)
+static void configure_keep_alive(int socket)
+{
+  int enable = 1;
+  setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable));
+
+  int idle = KEEP_ALIVE_IDLE_SEC;
+  setsockopt(socket, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle));
+
+  int interval = KEEP_ALIVE_INTERVAL;
+  setsockopt(socket, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
+
+  int count = KEEP_ALIVE_COUNT;
+  setsockopt(socket, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));
+}
 
 void *Seobeo_Web_Edge_Worker(void *vargs)
 {
@@ -8,57 +29,93 @@
   const int max_events = 64;
   struct epoll_event events[max_events];
 
-  // Each thread creates its own epoll to avoid race conditions
   int epfd = epoll_create1(0);
-  if (epfd < 0) {
+  if (epfd < 0)
+  {
     perror("epoll_create1");
     return NULL;
   }
 
-  // Add server socket to this thread's epoll
   struct epoll_event ev = {
     .events = EPOLLIN | EPOLLET,
     .data.ptr = args->srv
   };
-  if (epoll_ctl(epfd, EPOLL_CTL_ADD, args->srv->socket, &ev) < 0) {
+  if (epoll_ctl(epfd, EPOLL_CTL_ADD, args->srv->socket, &ev) < 0)
+  {
     perror("epoll_ctl ADD server");
     close(epfd);
     return NULL;
   }
 
-  while (1) {
-    int n = epoll_wait(epfd, events, max_events, -1);
-    if (n < 0) {
-      if (errno == EINTR) continue;
+  while (1)
+  {
+    int n = epoll_wait(epfd, events, max_events, -1);  // Block indefinitely, kernel handles timeouts
+    if (n < 0)
+    {
+      if (errno == EINTR)
+        continue;
       perror("epoll_wait");
       continue;
     }
 
-    for (int i = 0; i < n; i++) {
-      Seobeo_Handle *phandle = events[i].data.ptr;
+    for (int i = 0; i < n; i++)
+    {
+      Seobeo_Handle *p_handle = events[i].data.ptr;
 
-      if (phandle == args->srv) {
-        // Accept all pending connections (edge-triggered mode)
-        while (1) {
+      // Server socket - accept new connections
+      if (p_handle == args->srv)
+      {
+        while (1)
+        {
           Seobeo_Handle *p_cli_handle = Seobeo_Stream_Handle_Server_Accept(args->srv);
           if (!p_cli_handle) break;
 
+          // Let kernel handle keep-alive timeout
+          configure_keep_alive(p_cli_handle->socket);
+
           struct epoll_event client_ev = {
             .events = EPOLLIN | EPOLLET,
             .data.ptr = p_cli_handle
           };
+
           if (epoll_ctl(epfd, EPOLL_CTL_ADD, p_cli_handle->socket, &client_ev) < 0)
           {
             perror("epoll_ctl ADD client");
             Seobeo_Handle_Destroy(p_cli_handle);
           }
         }
-      } else {
-        // Remove from epoll first
-        epoll_ctl(epfd, EPOLL_CTL_DEL, phandle->socket, NULL);
+      }
+      // Client socket - handle request
+      else
+      {
+        Seobeo_Handle *p_client_handle = p_handle;
+
+        // Connection error or hangup - clean up
+        if (events[i].events & (EPOLLERR | EPOLLHUP))
+        {
+          epoll_ctl(epfd, EPOLL_CTL_DEL, p_client_handle->socket, NULL);
+          Seobeo_Handle_Destroy(p_client_handle);
+          continue;
+        }
 
-        // Handle request (this function destroys the handle internally)
-        Seobeo_Web_HandleClientRequest(phandle, args->cache);
+        // Handle requests (loop for pipelined requests)
+        boolean keep_alive = TRUE;
+        while (keep_alive)
+        {
+          keep_alive = Seobeo_Web_ClientHandle_Request(p_client_handle, args->cache, TRUE);
+
+          // No more data in buffer, wait for next epoll event
+          if (keep_alive && p_client_handle->read_buffer_len == 0)
+            break;
+        }
+
+        // Client wants to close
+        if (!keep_alive)
+        {
+          epoll_ctl(epfd, EPOLL_CTL_DEL, p_client_handle->socket, NULL);
+          Seobeo_Handle_Destroy(p_client_handle);
+        }
+        // Connection stays open, kernel will timeout if idle
       }
     }
   }