changeset 3:2758f5527d2b

[Seobeo] Working on simple TCP server and client logic.
author June Park <parkjune1995@gmail.com>
date Wed, 24 Sep 2025 18:49:09 -0700
parents 8a43dedbe530
children 0b3b4f5887bb
files bazel-bin bazel-testlogs dowa/d_memory.c dowa/dowa.h seobeo/BUILD seobeo/index.html seobeo/main.c seobeo/pages/index.html seobeo/s_linux_network.c seobeo/seobeo.h
diffstat 10 files changed, 245 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/bazel-bin	Wed Sep 24 13:15:32 2025 -0700
+++ b/bazel-bin	Wed Sep 24 18:49:09 2025 -0700
@@ -1,1 +1,1 @@
-/private/var/tmp/_bazel_mrjunejune/db79a4b810633db3e4d84b0e91eb071e/execroot/_main/bazel-out/darwin_arm64-dbg/bin
\ No newline at end of file
+/private/var/tmp/_bazel_mrjunejune/db79a4b810633db3e4d84b0e91eb071e/execroot/_main/bazel-out/darwin_arm64-fastbuild/bin
\ No newline at end of file
--- a/bazel-testlogs	Wed Sep 24 13:15:32 2025 -0700
+++ b/bazel-testlogs	Wed Sep 24 18:49:09 2025 -0700
@@ -1,1 +1,1 @@
-/private/var/tmp/_bazel_mrjunejune/db79a4b810633db3e4d84b0e91eb071e/execroot/_main/bazel-out/darwin_arm64-dbg/testlogs
\ No newline at end of file
+/private/var/tmp/_bazel_mrjunejune/db79a4b810633db3e4d84b0e91eb071e/execroot/_main/bazel-out/darwin_arm64-fastbuild/testlogs
\ No newline at end of file
--- a/dowa/d_memory.c	Wed Sep 24 13:15:32 2025 -0700
+++ b/dowa/d_memory.c	Wed Sep 24 18:49:09 2025 -0700
@@ -65,7 +65,7 @@
   return hash_val % p_hash_map->capacity;
 }
 
-void Dowa_HashMap_PushValue(Dowa_PHashMap p_hash_map, char *key, void *value, size_t value_size)
+void Dowa_HashMap_PushValueWithType(Dowa_PHashMap p_hash_map, char *key, void *value, size_t value_size, Dowa_ValueType type)
 {
   int idx = Dowa_HashMap_GetPosition(p_hash_map, key);
   Dowa_PHashEntry entry = p_hash_map->entries[idx];
@@ -92,10 +92,16 @@
     return;
   }
   entry->capacity = value_size;
+  entry->type = type;
   memcpy(entry->buffer, value, value_size);
   p_hash_map->current_capacity++;
 }
 
+void Dowa_HashMap_PushValue(Dowa_PHashMap p_hash_map, char *key, void *value, size_t value_size)
+{
+  Dowa_HashMap_PushValueWithType(p_hash_map, key, value, value_size, DOWA_TYPE_BUFFER);
+}
+
 void Dowa_HashMap_PopKey(Dowa_PHashMap p_hash_map, char *key)
 {
   int idx = Dowa_HashMap_GetPosition(p_hash_map, key);
@@ -113,3 +119,101 @@
   }
 }
 
+#include <stdio.h>
+
+void Dowa_HashMap_Print(Dowa_PHashMap map)
+{
+  for (size_t i = 0; i < map->capacity; ++i)
+  {
+    Dowa_PHashEntry e = map->entries[i];
+    if (!e) continue;
+    printf("%s: ", e->key);
+    switch (e->type)
+    {
+      case DOWA_TYPE_BUFFER:
+      {
+        unsigned char *p = e->buffer;
+        for (size_t j = 0; j < e->capacity; ++j) {
+          printf("%02x", p[j]);
+        }
+        printf("\n");
+      }
+      break;
+      case DOWA_TYPE_STRING:
+      {
+        printf("%s\n", (char*)e->buffer);
+      }
+      break;
+      case DOWA_TYPE_INT:
+      {
+        printf("%d\n", *(int32_t*)e->buffer);
+      }
+      break;
+      default:
+      {
+        printf("<unknown type>\n");
+      }
+    }
+  }
+}
+
+
+
+int Dowa_Cache_Folder(Dowa_PHashMap map, const char *folder_path)
+{
+  DIR *dir = opendir(folder_path);
+  if (!dir) {
+    perror("opendir");
+    return -1;
+  }
+
+  struct dirent *entry;
+  while ((entry = readdir(dir))) {
+    // skip "." and ".."
+    if (entry->d_name[0] == '.' &&
+       (entry->d_name[1] == '\0' ||
+      (entry->d_name[1] == '.' && entry->d_name[2] == '\0')))
+      continue;
+
+    char fullpath[PATH_MAX];
+    snprintf(fullpath, sizeof fullpath, "%s/%s", folder_path, entry->d_name);
+
+    struct stat st;
+    if (stat(fullpath, &st) < 0 || !S_ISREG(st.st_mode))
+      continue;  // skip non-files or errors
+
+    size_t size = (size_t)st.st_size;
+    FILE *f = fopen(fullpath, "rb");
+    if (!f)
+    {
+      perror("fopen");
+      continue;
+    }
+
+    void *buf = malloc(size);
+    if (!buf)
+    {
+      perror("malloc");
+      fclose(f);
+      closedir(dir);
+      return -1;
+    }
+
+    if (fread(buf, 1, size, f) != size)
+    {
+      perror("fread");
+      free(buf);
+      fclose(f);
+      continue;
+    }
+    fclose(f);
+
+    // key = filename (d_name), value = file contents
+    Dowa_HashMap_PushValueWithType(map, entry->d_name, buf, size, DOWA_TYPE_STRING);
+
+    free(buf);  // Dowa_HashMap_PushValue made its own copy
+  }
+
+  closedir(dir);
+  return 0;
+}
--- a/dowa/dowa.h	Wed Sep 24 13:15:32 2025 -0700
+++ b/dowa/dowa.h	Wed Sep 24 18:49:09 2025 -0700
@@ -5,6 +5,11 @@
 #include <string.h> // stdup
 #include <stdlib.h> // only for malloc, free, stuff
 #include <assert.h>
+#include <dirent.h>
+
+#include <sys/stat.h>
+#include <limits.h>
+
 #include "dowa_internal.h"
 
 #define HASH_KEY_NUMBER 5381 // DJD hash number
@@ -32,10 +37,17 @@
 extern void        Dowa_Arena_Free(Dowa_PArena p_arena);
 
 // --- Map --- //
+typedef enum {
+  DOWA_TYPE_BUFFER,
+  DOWA_TYPE_STRING,
+  DOWA_TYPE_INT,
+} Dowa_ValueType;
+
 typedef struct {
-  char   *key;
-  void   *buffer; 
-  size_t capacity;
+  char           *key;
+  void           *buffer; 
+  size_t         capacity;
+  Dowa_ValueType type;
 } Dowa_HashEntry, *Dowa_PHashEntry;
 
 typedef struct {
@@ -47,7 +59,13 @@
 extern Dowa_PHashMap Dowa_HashMap_Create(size_t capacity);
 extern int32         Dowa_HashMap_GetPosition(Dowa_PHashMap p_hash_map, char *key);
 extern void          Dowa_HashMap_PushValue(Dowa_PHashMap p_hash_map, char *key, void *value, size_t value_size);
+extern void          Dowa_HashMap_PushValueWithType(Dowa_PHashMap p_hash_map, char *key, void *value, size_t value_size, Dowa_ValueType type);
 extern void          Dowa_HashMap_PopKey(Dowa_PHashMap p_hash_map, char *key);
 
+// --- Maybe Useful --- //
+extern void Dowa_HashMap_Print(Dowa_PHashMap map);
+// 0 for success, -1 for failure.
+extern int  Dowa_Cache_Folder(Dowa_PHashMap map, const char *folder_path);
+
 
 #endif
--- a/seobeo/BUILD	Wed Sep 24 13:15:32 2025 -0700
+++ b/seobeo/BUILD	Wed Sep 24 18:49:09 2025 -0700
@@ -13,7 +13,7 @@
   srcs = ["main.c"],
   deps = [":seobeo_non_window"],
   data = [
-    "index.html"
+    "pages/index.html"
   ],
   target_compatible_with = [
       "@platforms//os:osx",
--- a/seobeo/index.html	Wed Sep 24 13:15:32 2025 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-<HTML>
-  <head>
-  </head>
-  <body>
-    <h1> June </h1>
-  </body>
-</HTML>
--- a/seobeo/main.c	Wed Sep 24 13:15:32 2025 -0700
+++ b/seobeo/main.c	Wed Sep 24 18:49:09 2025 -0700
@@ -4,6 +4,8 @@
 
 #include "seobeo/seobeo.h"
 
+Dowa_PHashMap cache;
+
 void SigchildHandler(int s)
 {
   (void)s; // quiet unused variable warning
@@ -16,63 +18,76 @@
   errno = saved_errno;
 }
 
-
-void *GetInternetaddr(struct sockaddr *sa)
-{
-  if (sa->sa_family == AF_INET) 
-  {
-    return &(((struct sockaddr_in*)sa)->sin_addr);
-  }
-
-  return &(((struct sockaddr_in6*)sa)->sin6_addr);
-}
-
 void HandleClientRequest(int client_fd)
 {
-  FILE *file = fopen("seobeo/index.html", "rb");
-  if (!file) {
-    perror("fopen");
+  size_t idx = Dowa_HashMap_GetPosition(cache, "index.html");
+  Dowa_PHashEntry entry = cache->entries[idx];
+  if (!entry) {
+    // 404 response if missing
+    const char *not_found =
+      "HTTP/1.1 404 Not Found\r\n"
+      "Content-Length: 0\r\n"
+      "Connection: close\r\n"
+      "\r\n";
+    send(client_fd, not_found, strlen(not_found), 0);
+    close(client_fd);
     return;
   }
 
-  fseek(file, 0, SEEK_END);
-  size_t size = ftell(file);
-  fseek(file, 0, SEEK_SET);
-
-  char *data = malloc(size);
-  fread(data, 1, size, file);
-  fclose(file);
-
-  char *header = malloc(100);
-  sprintf(
-    header,
+  // 3) Prepare header with the correct content‐length
+  size_t body_size = entry->capacity;
+  const char *template =
     "HTTP/1.1 200 OK\r\n"
     "Content-Type: text/html\r\n"
     "Content-Length: %zu\r\n"
     "Connection: close\r\n"
-    "\r\n",
-    size
-  );
+    "\r\n";
+
+  // Compute how large the header is and allocate just enough space
+  int header_len = snprintf(NULL, 0, template, body_size);
+  char *header = malloc(header_len + 1);
+  if (!header) {
+    perror("malloc");
+    close(client_fd);
+    return;
+  }
+  snprintf(header, header_len + 1, template, body_size);
+
+  // 4) Send header
+  send(client_fd, header, header_len, 0);
+  free(header);
 
-  send(client_fd, header, strlen(header), 0);
+  // 5) Stream the body in a loop until everything is sent
   ssize_t total_sent = 0;
-  while (total_sent < size)
+  const char *body = entry->buffer;
+  while ((size_t)total_sent < body_size)
   {
-    ssize_t sent = send(client_fd, data + total_sent, size - total_sent, 0);
-    if (sent <= 0) break;
+    ssize_t sent = send(
+      client_fd,
+      body + total_sent,
+      body_size - total_sent,
+      0
+    );
+    if (sent <= 0)
+    {
+      // error or connection closed by client
+      break;
+    }
     total_sent += sent;
   }
 
+  // 6) Tear down
   close(client_fd);
 }
 
 int main(void)
 {
-  int32 client_fd;
-  struct sockaddr_storage client_addr; 
-  socklen_t sin_size;
-
-  char client_inet_addr[INET6_ADDRSTRLEN];
+  cache = Dowa_HashMap_Create(1024);
+  if (Dowa_Cache_Folder(cache, "seobeo/pages") != 0)
+  {
+    perror("Dowa_Cache_Folder");
+    return -1;
+  }
 
   struct sigaction sa;
 
@@ -85,37 +100,7 @@
   }
   
   int sock_fd = Seobeo_CreateSocket(1, "6969", 10);
-
-  printf("server: waiting for connections...\n");
-
-  while (1) 
-  {
-    sin_size = sizeof(client_addr);
-    client_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
-
-    if (client_fd == -1)
-    {
-      perror("accept");
-      break;
-    }
-
-    inet_ntop(
-        client_addr.ss_family,
-        GetInternetaddr((struct sockaddr *)&client_addr),
-        client_inet_addr, sizeof client_inet_addr);
-
-    printf("server: got connection from %s\n", client_inet_addr);
-
-    // Create a child process
-    if (!fork())
-    {
-      close(sock_fd);
-      HandleClientRequest(client_fd);
-      exit(0);
-    }
-
-    close(client_fd);
-  }
+  Seobeo_ListenClient(sock_fd, &HandleClientRequest);
 
   return 0;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/seobeo/pages/index.html	Wed Sep 24 18:49:09 2025 -0700
@@ -0,0 +1,7 @@
+<HTML>
+  <head>
+  </head>
+  <body>
+    <h1> June </h1>
+  </body>
+</HTML>
--- a/seobeo/s_linux_network.c	Wed Sep 24 13:15:32 2025 -0700
+++ b/seobeo/s_linux_network.c	Wed Sep 24 18:49:09 2025 -0700
@@ -98,3 +98,51 @@
   return sock_fd;
 }
 
+
+void Seobeo_ListenClient(int sock_fd, void (*handle_client)(int))
+{
+  int32 client_fd;
+  struct sockaddr_storage client_addr; 
+  socklen_t sin_size;
+
+  char client_inet_addr[INET6_ADDRSTRLEN];
+
+  while (1) 
+  {
+    sin_size = sizeof(client_addr);
+    client_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);
+
+    if (client_fd == -1)
+    {
+      perror("accept");
+      break;
+    }
+
+    inet_ntop(
+        client_addr.ss_family,
+        Seobeo_GetIP4OrIP6((struct sockaddr *)&client_addr),
+        client_inet_addr, sizeof client_inet_addr);
+
+    printf("server: got connection from %s\n", client_inet_addr);
+
+    // Create a child process
+    if (!fork())
+    {
+      close(sock_fd);
+      handle_client(client_fd);
+      exit(0);
+    }
+
+    close(client_fd);
+  }
+}
+
+void *Seobeo_GetIP4OrIP6(struct sockaddr *sa)
+{
+  if (sa->sa_family == AF_INET) 
+  {
+    return &(((struct sockaddr_in*)sa)->sin_addr);
+  }
+
+  return &(((struct sockaddr_in6*)sa)->sin6_addr);
+}
--- a/seobeo/seobeo.h	Wed Sep 24 13:15:32 2025 -0700
+++ b/seobeo/seobeo.h	Wed Sep 24 18:49:09 2025 -0700
@@ -22,6 +22,13 @@
 #include "dowa/dowa.h"
 
 
-extern int Seobeo_CreateSocket(int32 stream, char* port, int32 backlog);
+// --- Internal? --- //
+extern int  Seobeo_CreateSocket(int32 stream, char* port, int32 backlog);
+extern void Seobeo_ListenClient(int sock_fd, void (*handle_client)(int));
+extern void *Seobeo_GetIP4OrIP6(struct sockaddr *sa);
+
+// --- TCP --- //
+// extern int Seobeo_TCP_SocketCreate(char* port, int32 backlog)
+
 
 #endif