Mercurial
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