diff postdog/main.c @ 152:7387eec8e7f8

[Postdog] Updated to use Seobeo_Client instead of CURL. Updated to handle websocket connection.
author June Park <parkjune1995@gmail.com>
date Sun, 11 Jan 2026 07:47:05 -0800
parents 249881ceff7b
children 790930d9bb90
line wrap: on
line diff
--- a/postdog/main.c	Sat Jan 10 13:30:28 2026 -0800
+++ b/postdog/main.c	Sun Jan 11 07:47:05 2026 -0800
@@ -3,13 +3,28 @@
 #include <string.h>
 #include <time.h>
 #include <sys/stat.h>
-#include "dowa/dowa.h"
+#include <pthread.h>
 
-#include <curl/curl.h>
+#ifdef _WIN32
+    #include <direct.h>
+    #include <io.h>
+    #define mkdir(path, mode) _mkdir(path)
+    #define access _access
+    #define F_OK 0
+#else
+    #include <sys/stat.h>
+    #include <dirent.h>
+    #include <unistd.h>
+#endif
+
+
 #include "third_party/raylib/include/raylib.h"
 #define RAYGUI_IMPLEMENTATION
 #include "third_party/raylib/include/raygui.h"
 
+#include "dowa/dowa.h"
+#include "seobeo/seobeo.h"
+
 #ifndef POSTDOG_PATHS
   #define POSTDOG_PATHS "/Users/mrjunejune/zenbu/postdog/history"
 #endif
@@ -24,19 +39,6 @@
 #define BODY_BUFFER_LENGTH 1024 * 1024 * 5
 #define RESULT_BUFFER_LENGTH 1024 * 1024 * 5
 
-
-#ifdef _WIN32
-    #include <direct.h>
-    #include <io.h>
-    #define mkdir(path, mode) _mkdir(path)
-    #define access _access
-    #define F_OK 0
-#else
-    #include <sys/stat.h>
-    #include <dirent.h>
-    #include <unistd.h>
-#endif
-
 typedef Dowa_KV(char*, char*) INPUT_HASHMAP;
 
 typedef struct {
@@ -62,7 +64,7 @@
   TAB_HEADER = 0,
   TAB_BODY,
   TAB_GET_PARAMS,
-  TAB_BAR,
+  TAB_WEBSOCKET,
   TAB_LENGTH
 } PostDog_Tab_Enum;
 
@@ -75,6 +77,8 @@
 char *url_result_text = NULL;
 char **url_body_map = NULL;
 int active_method_dropdown = 0;
+int active_input_tab = 0;
+Seobeo_WebSocket *ws = NULL;
 
 int CompareHistoryItemsByDate(const void *a, const void *b) {
   HistoryItem *itemA = (HistoryItem *)a;
@@ -246,7 +250,7 @@
       url_body_map[TAB_HEADER],
       url_body_map[TAB_BODY],
       url_body_map[TAB_GET_PARAMS],
-      url_body_map[TAB_BAR],
+      url_body_map[TAB_WEBSOCKET],
       url_result_text
   );
   char *filename = Dowa_Arena_Allocate(arena, 1024);
@@ -276,118 +280,108 @@
   Dowa_Arena_Free(arena);
 }
 
-static size_t Postdog_Curl_Callback(void *contents, size_t size, size_t nmemb, void *userp)
+int PostDog_Websocket_Send(void)
 {
-  size_t real_size = size * nmemb;
-  ResponseBuffer *buf = (ResponseBuffer *)userp;
+  if (!ws)
+    ws = Seobeo_WebSocket_Connect(url_input_text);
 
-  char *ptr = realloc(buf->data, buf->size + real_size + 1);
-  if (ptr == NULL)
+  printf("URL %s\n", url_input_text);
+  if (Seobeo_WebSocket_Send_Text(ws, url_body_map[active_input_tab]) < 0)
+    printf("Failed to send message\n");
+
+  printf("Receiving responses...\n");
+
+  int received = 0;
+  while (TRUE)
   {
-    printf("Not enough memory for response\n");
-    return 0;
+    Seobeo_WebSocket_Message *p_msg = Seobeo_WebSocket_Receive(ws);
+    if (p_msg)
+    {
+      if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
+      {
+        printf("Response %d: %.*s\n", received + 1, (int)p_msg->length, (char*)p_msg->data);
+        snprintf(url_result_text, RESULT_BUFFER_LENGTH, "%s", p_msg->data);
+        received++;
+      }
+      Seobeo_WebSocket_Message_Destroy(p_msg);
+    }
+    usleep(10000);
   }
+  printf("Received %d/%d messages\n", received, 3);
+}
 
-  buf->data = ptr;
-  memcpy(&(buf->data[buf->size]), contents, real_size);
-  buf->size += real_size;
-  buf->data[buf->size] = 0;
+void* PostDog_Websocket_Thread(void* arg)
+{
+    PostDog_Websocket_Send();
+    printf("Websocket request finished.\n");
+    return NULL;
+}
 
-  return real_size;
+void Trigger_Async_Send()
+{
+  pthread_t thread_id;
+  
+  if (pthread_create(&thread_id, NULL, PostDog_Websocket_Thread, NULL) != 0)
+  {
+    perror("Failed to create thread");
+    return;
+  }
+  pthread_detach(thread_id);
 }
 
 int PostDog_Http_Request(void)
 {
-  const char *method = PostDog_Enum_To_String(active_method_dropdown);
-  CURL *curl;
-  CURLcode res;
-  ResponseBuffer buffer = { .data = malloc(1), .size = 0 };
-
-  url_result_text[0] = '\n';
-
-  if (buffer.data == NULL)
+  Seobeo_Client_Request *req = Seobeo_Client_Request_Create(url_input_text);
+  Seobeo_Client_Response *res;
+  switch (active_method_dropdown) 
   {
-    snprintf(url_result_text, RESULT_BUFFER_LENGTH, "Error: Failed to allocate memory");
-    return -1;
+    case 0:
+    {
+      Seobeo_Client_Request_Set_Method(req, "GET");
+      break;
+    }
+    case 1: 
+    {
+      Seobeo_Client_Request_Set_Method(req, "POST");
+      break;
+    }
+    case 2:
+    {
+      Seobeo_Client_Request_Set_Method(req, "PUT");
+      break;
+    }
+    case 3:
+    {
+      Seobeo_Client_Request_Set_Method(req, "DELETE");
+      break;
+    }
   }
-  buffer.data[0] = '\0';
-
-  curl_global_init(CURL_GLOBAL_DEFAULT);
-  curl = curl_easy_init();
-
-  if (curl)
-  {
-    struct curl_slist *headerList = NULL;
 
-    // Set URL
-    curl_easy_setopt(curl, CURLOPT_URL, url_input_text);
-
-    // Set HTTP method
-    if (strcmp(method, "POST") == 0)
-    {
-      curl_easy_setopt(curl, CURLOPT_POST, 1L);
-      if (url_body_map[TAB_BODY] && strlen(url_body_map[TAB_BODY]) > 0)
-        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, url_body_map[TAB_BODY]);
-    }
-    else if (strcmp(method, "PUT") == 0)
-    {
-      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
-      if (url_body_map[TAB_BODY] && strlen(url_body_map[TAB_BODY]) > 0)
-        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, url_body_map[TAB_BODY]);
-    }
-    else if (strcmp(method, "DELETE") == 0)
-    {
-      curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
-    }
-    // Default is GET
-
-    // Parse and add headers
+  {
     if (url_body_map[TAB_HEADER] && strlen(url_body_map[TAB_HEADER]) > 0)
     {
       char *headersCopy = strdup(url_body_map[TAB_HEADER]);
       char *line = strtok(headersCopy, "\n");
-      while (line != NULL) {
+      while (line != NULL)
+      {
         while (*line == ' ' || *line == '\t') line++;
         if (strlen(line) > 0)
-          headerList = curl_slist_append(headerList, line);
+          Seobeo_Client_Request_Add_Header_Array(req, line);
         line = strtok(NULL, "\n");
       }
-      curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList);
-      free(headersCopy);
-    }
-
-    // Set write callback
-    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Postdog_Curl_Callback);
-    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&buffer);
 
-    // Follow redirects
-    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-    curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
-    res = curl_easy_perform(curl);
+    }  
+    Seobeo_Client_Request_Set_Follow_Redirects(req, TRUE, 5); // TODO: remove magic number;
+    res = Seobeo_Client_Request_Execute(req);
 
-    if (res != CURLE_OK)
-      snprintf(url_result_text, RESULT_BUFFER_LENGTH, "Error: %s\n", curl_easy_strerror(res));
+    if (res == NULL)
+      snprintf(url_result_text, RESULT_BUFFER_LENGTH, "Error: Failed to send the request\n");
     else
-    {
-      long response_code;
-      curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
-
-      if (buffer.size > RESULT_BUFFER_LENGTH)
-        printf("TODO: Realloc\n");
-
-      snprintf(url_result_text, RESULT_BUFFER_LENGTH, "HTTP Status: %ld\n\n%s",
-               response_code, buffer.data ? buffer.data : "");
-    }
-
-    if (headerList) curl_slist_free_all(headerList);
-    curl_easy_cleanup(curl);
+      snprintf(url_result_text, RESULT_BUFFER_LENGTH, "HTTP Status: %d\n\n%s",
+               res->status_code, res->body ? res->body : "");
   }
-  else
-    snprintf(url_result_text, RESULT_BUFFER_LENGTH, "Error: Failed to initialize curl");
-
-  free(buffer.data);
-  curl_global_cleanup();
-
+  Seobeo_Client_Request_Destroy(req);
+  Seobeo_Client_Response_Destroy(res);
   PostDog_Request_SaveFile();
   return 0;
 }
@@ -492,7 +486,7 @@
       case 3:  // Headers (TAB_HEADER)
       case 4:  // Body (TAB_BODY)
       case 5:  // Get Params (TAB_GET_PARAMS)
-      case 6:  // Bar (TAB_BAR)
+      case 6:  // Bar (TAB_WEBSOCKET)
       {
         int map_index = i - 3;  // 3->0, 4->1, 5->2, 6->3
         snprintf(url_body_map[map_index], strlen(values[i]) + 1, "%s", values[i]);
@@ -633,7 +627,7 @@
 
   // Initialize global UI state
   url_input_text = (char *)malloc(sizeof(char) * URL_TEXT_BUFFER_LENGTH);
-  snprintf(url_input_text, URL_TEXT_BUFFER_LENGTH, "https://httpbin.org/get");
+  snprintf(url_input_text, URL_TEXT_BUFFER_LENGTH, "wss://mrjunejune.com/echo");
 
   Dowa_Array_Push(url_body_map, (char *)malloc(sizeof(char) * HEADER_BUFFER_LENGTH));
   Dowa_Array_Push(url_body_map, (char *)malloc(sizeof(char) * BODY_BUFFER_LENGTH));
@@ -662,7 +656,6 @@
 
   // General styling.
   float padding = 10; // TODO make it % based?
-  int active_input_tab = 0;
   int prev_input_tab = 0;
 
   // Scroll offsets
@@ -785,7 +778,8 @@
     float mouse_wheel = GetMouseWheelMove();
 
     // Reset input body scroll when tab changes
-    if (prev_input_tab != active_input_tab) {
+    if (prev_input_tab != active_input_tab)
+    {
       input_body_scroll_offset = 0;
       prev_input_tab = active_input_tab;
     }
@@ -905,8 +899,18 @@
         Rectangle scrolled_input = AddPadding(input_body, padding * 2);
         scrolled_input.y += input_body_scroll_offset;
         scrolled_input.height = MAX_SCROLL_HEIGHT;
-        if (JUNE_GuiTextBox(scrolled_input, url_body_map[active_input_tab], DEFAULT_TEXT_BUFFER_LENGTH, input_body_bool))
-          input_body_bool = !input_body_bool;
+        if (active_input_tab != TAB_WEBSOCKET)
+        {
+          if (JUNE_GuiTextBox(scrolled_input, url_body_map[active_input_tab], DEFAULT_TEXT_BUFFER_LENGTH, input_body_bool))
+            input_body_bool = !input_body_bool;
+        }
+        else
+        {
+          boolean temp = true;
+          if (GuiTextInputBox(input_body, "send message", "connected", 
+              "send", url_body_map[active_input_tab], BODY_BUFFER_LENGTH, &temp) == 1)
+            Trigger_Async_Send();
+        }
       EndScissorMode();
 
       if (input_body_scroll_offset < 0) {
@@ -937,7 +941,8 @@
         GuiTextBoxMulti(scrolled_result, url_result_text, RESULT_BUFFER_LENGTH, false);
       EndScissorMode();
 
-      if (result_body_scroll_offset < 0) {
+      if (result_body_scroll_offset < 0)
+      {
         float scrollbar_height = 10;
         float scrollbar_y = result_body.y - (result_body_scroll_offset / MAX_SCROLL_HEIGHT) * (result_body.height - scrollbar_height);
         Rectangle scrollbar = {