diff seobeo/docs/web_socket_server.md @ 121:7b1719fa918c

[Seobeo] Added web socket server.
author June Park <parkjune1995@gmail.com>
date Thu, 08 Jan 2026 06:45:10 -0800
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/seobeo/docs/web_socket_server.md	Thu Jan 08 06:45:10 2026 -0800
@@ -0,0 +1,391 @@
+# Seobeo WebSocket Server - Usage Guide
+
+A clean, easy-to-use WebSocket server library that integrates seamlessly with the existing Seobeo HTTP server.
+
+## Features
+
+1. **Automatic Upgrade Handling**: Automatically detects and upgrades HTTP connections to WebSocket
+2. **Route-Based Handlers**: Register WebSocket handlers for specific paths (like HTTP routing)
+3. **Text and Binary Messages**: Send/receive both text and binary data
+4. **Broadcast Support**: Send messages to all connected clients
+5. **Connection Management**: Automatic connection tracking and cleanup
+6. **Integrated with HTTP Server**: Works alongside existing HTTP routes and static file serving
+7. **RFC 6455 Compliant**: Full WebSocket protocol support
+
+## API Overview
+
+### Initialization and Registration
+
+```c
+// Initialize WebSocket server system
+void Seobeo_WebSocket_Server_Init();
+
+// Register a WebSocket handler for a specific path
+void Seobeo_WebSocket_Server_Register(const char *path,
+    Seobeo_WebSocket_Server_Handler handler, void *p_user_data);
+```
+
+### Handler Function Type
+
+```c
+typedef void (*Seobeo_WebSocket_Server_Handler)(
+  Seobeo_WebSocket_Server_Connection *p_conn,
+  Seobeo_WebSocket_Message *p_msg,
+  void *p_user_data
+);
+```
+
+### Sending Messages
+
+```c
+// Send to specific client
+int32 Seobeo_WebSocket_Server_Send_Text(Seobeo_WebSocket_Server_Connection *p_conn,
+    const char *text);
+int32 Seobeo_WebSocket_Server_Send_Binary(Seobeo_WebSocket_Server_Connection *p_conn,
+    const uint8 *data, size_t length);
+
+// Broadcast to all clients
+void Seobeo_WebSocket_Server_Broadcast_Text(const char *text);
+void Seobeo_WebSocket_Server_Broadcast_Binary(const uint8 *data, size_t length);
+```
+
+### Connection Management
+
+```c
+// Close specific connection
+void Seobeo_WebSocket_Server_Connection_Close(Seobeo_WebSocket_Server_Connection *p_conn,
+    uint16 code, const char *reason);
+```
+
+## Examples
+
+### 1. Simple Echo Server
+
+```c
+#include "seobeo/seobeo.h"
+
+void Echo_Handler(Seobeo_WebSocket_Server_Connection *p_conn,
+    Seobeo_WebSocket_Message *p_msg, void *p_user_data)
+{
+  if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
+  {
+    printf("Received: %.*s\n", (int)p_msg->length, (char*)p_msg->data);
+    Seobeo_WebSocket_Server_Send_Text(p_conn, (char*)p_msg->data);
+  }
+}
+
+int main()
+{
+  // Initialize WebSocket routing
+  Seobeo_WebSocket_Server_Init();
+
+  // Register echo handler
+  Seobeo_WebSocket_Server_Register("/echo", Echo_Handler, NULL);
+
+  // Start HTTP server (automatically handles WebSocket upgrades)
+  Seobeo_Web_Server_Start(NULL, "8080", SEOBEO_MODE_FORK, 0);
+
+  return 0;
+}
+```
+
+### 2. Chat Room Server
+
+```c
+void Chat_Handler(Seobeo_WebSocket_Server_Connection *p_conn,
+    Seobeo_WebSocket_Message *p_msg, void *p_user_data)
+{
+  if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
+  {
+    char message[2048];
+    snprintf(message, sizeof(message), "[%s]: %.*s",
+        p_conn->client_id, (int)p_msg->length, (char*)p_msg->data);
+
+    printf("Broadcasting: %s\n", message);
+    Seobeo_WebSocket_Server_Broadcast_Text(message);
+  }
+}
+
+int main()
+{
+  Seobeo_WebSocket_Server_Init();
+  Seobeo_WebSocket_Server_Register("/chat", Chat_Handler, NULL);
+
+  printf("Chat server started on ws://localhost:8080/chat\n");
+  Seobeo_Web_Server_Start(NULL, "8080", SEOBEO_MODE_FORK, 0);
+
+  return 0;
+}
+```
+
+### 3. Binary Data Broadcasting
+
+```c
+void Binary_Handler(Seobeo_WebSocket_Server_Connection *p_conn,
+    Seobeo_WebSocket_Message *p_msg, void *p_user_data)
+{
+  if (p_msg->opcode == SEOBEO_WS_OPCODE_BINARY)
+  {
+    printf("Received %zu bytes, broadcasting...\n", p_msg->length);
+    Seobeo_WebSocket_Server_Broadcast_Binary(p_msg->data, p_msg->length);
+  }
+}
+
+int main()
+{
+  Seobeo_WebSocket_Server_Init();
+  Seobeo_WebSocket_Server_Register("/binary", Binary_Handler, NULL);
+
+  Seobeo_Web_Server_Start(NULL, "8080", SEOBEO_MODE_FORK, 0);
+  return 0;
+}
+```
+
+### 4. Multiple Endpoints
+
+```c
+void Echo_Handler(Seobeo_WebSocket_Server_Connection *p_conn,
+    Seobeo_WebSocket_Message *p_msg, void *p_user_data)
+{
+  if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
+    Seobeo_WebSocket_Server_Send_Text(p_conn, (char*)p_msg->data);
+}
+
+void Chat_Handler(Seobeo_WebSocket_Server_Connection *p_conn,
+    Seobeo_WebSocket_Message *p_msg, void *p_user_data)
+{
+  if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
+  {
+    char msg[2048];
+    snprintf(msg, sizeof(msg), "[%s]: %.*s",
+        p_conn->client_id, (int)p_msg->length, (char*)p_msg->data);
+    Seobeo_WebSocket_Server_Broadcast_Text(msg);
+  }
+}
+
+int main()
+{
+  Seobeo_WebSocket_Server_Init();
+
+  // Register multiple WebSocket endpoints
+  Seobeo_WebSocket_Server_Register("/echo", Echo_Handler, NULL);
+  Seobeo_WebSocket_Server_Register("/chat", Chat_Handler, NULL);
+
+  printf("Server started with:\n");
+  printf("  ws://localhost:8080/echo - Echo server\n");
+  printf("  ws://localhost:8080/chat - Chat room\n");
+
+  Seobeo_Web_Server_Start(NULL, "8080", SEOBEO_MODE_FORK, 0);
+  return 0;
+}
+```
+
+### 5. Custom User Data
+
+```c
+typedef struct {
+  int message_count;
+  char name[64];
+} ChatRoomData;
+
+void Chat_Handler_With_Data(Seobeo_WebSocket_Server_Connection *p_conn,
+    Seobeo_WebSocket_Message *p_msg, void *p_user_data)
+{
+  ChatRoomData *p_data = (ChatRoomData*)p_user_data;
+
+  if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
+  {
+    p_data->message_count++;
+
+    char message[2048];
+    snprintf(message, sizeof(message), "[%s #%d]: %.*s",
+        p_data->name, p_data->message_count,
+        (int)p_msg->length, (char*)p_msg->data);
+
+    Seobeo_WebSocket_Server_Broadcast_Text(message);
+  }
+}
+
+int main()
+{
+  ChatRoomData room_data = {0};
+  strcpy(room_data.name, "Main Room");
+
+  Seobeo_WebSocket_Server_Init();
+  Seobeo_WebSocket_Server_Register("/chat", Chat_Handler_With_Data, &room_data);
+
+  Seobeo_Web_Server_Start(NULL, "8080", SEOBEO_MODE_FORK, 0);
+  return 0;
+}
+```
+
+### 6. Mixed HTTP and WebSocket Server
+
+```c
+// HTTP route handler
+Seobeo_Request_Entry* Get_Status(Seobeo_Request_Entry *req, Dowa_Arena *arena)
+{
+  Seobeo_Request_Entry *resp = NULL;
+  Dowa_HashMap_Push_Arena(resp, "status", "200", arena);
+  Dowa_HashMap_Push_Arena(resp, "content-type", "application/json", arena);
+  Dowa_HashMap_Push_Arena(resp, "body", "{\"status\": \"online\"}", arena);
+  return resp;
+}
+
+// WebSocket handler
+void WS_Handler(Seobeo_WebSocket_Server_Connection *p_conn,
+    Seobeo_WebSocket_Message *p_msg, void *p_user_data)
+{
+  if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT)
+    Seobeo_WebSocket_Server_Send_Text(p_conn, (char*)p_msg->data);
+}
+
+int main()
+{
+  // Initialize both HTTP and WebSocket routing
+  Seobeo_Router_Init();
+  Seobeo_WebSocket_Server_Init();
+
+  // Register HTTP routes
+  Seobeo_Router_Register("GET", "/api/status", Get_Status);
+
+  // Register WebSocket routes
+  Seobeo_WebSocket_Server_Register("/ws", WS_Handler, NULL);
+
+  printf("Server started:\n");
+  printf("  HTTP: http://localhost:8080/api/status\n");
+  printf("  WebSocket: ws://localhost:8080/ws\n");
+
+  Seobeo_Web_Server_Start("./public", "8080", SEOBEO_MODE_FORK, 0);
+  return 0;
+}
+```
+
+## Building
+
+### Build the server example:
+```bash
+bazel build //seobeo:websocket_server_example
+```
+
+### Run the server:
+```bash
+bazel-bin/seobeo/websocket_server_example
+```
+
+### Test with a client:
+You can test the server using the WebSocket client API or any WebSocket client tool:
+```bash
+# Using websocat (install: cargo install websocat)
+websocat ws://localhost:8080/echo
+
+# Using wscat (install: npm install -g wscat)
+wscat -c ws://localhost:8080/chat
+```
+
+## Connection Structure
+
+```c
+struct Seobeo_WebSocket_Server_Connection_Struct {
+  Seobeo_Handle *p_handle;      // Underlying socket handle
+  char          *client_id;     // Unique client identifier
+  boolean        is_active;     // Connection status
+
+  // Fragment handling (internal)
+  uint8  *fragment_buffer;
+  size_t  fragment_length;
+  size_t  fragment_capacity;
+  Seobeo_WebSocket_Opcode fragment_opcode;
+
+  Seobeo_WebSocket_Server_Connection *next;  // Linked list
+};
+```
+
+## Protocol Details
+
+The server implementation follows RFC 6455:
+
+1. **Handshake**: Responds to HTTP Upgrade requests with proper Sec-WebSocket-Accept key
+2. **Frame Format**: Server sends unmasked frames (RFC requirement)
+3. **Frame Receiving**: Server receives and unmasks client frames
+4. **Fragmentation**: Handles fragmented messages automatically
+5. **Control Frames**: Proper handling of ping, pong, and close frames
+6. **Broadcast**: Efficiently sends to all active connections
+
+## Key Differences from Client
+
+| Feature | Client | Server |
+|---------|--------|--------|
+| Frame Masking | Masks outgoing frames | Does NOT mask outgoing frames |
+| Connection | Initiates connection | Accepts connections |
+| Upgrade | Sends upgrade request | Responds to upgrade request |
+| Multiple Connections | Single connection | Manages multiple connections |
+| Broadcast | N/A | Can broadcast to all clients |
+
+## Coding Standards
+
+The implementation follows your specified coding standards:
+- Naming: `Seobeo_WebSocket_Server_Init`, `Seobeo_WebSocket_Server_Send_Text`
+- Two spaces for indentation
+- New line before `{` unless it's a struct
+- Single statement: no need for `{}`
+
+## Integration with Existing Server
+
+The WebSocket server integrates seamlessly:
+
+1. **Automatic Detection**: The HTTP server automatically checks for WebSocket upgrade requests
+2. **No Conflicts**: WebSocket and HTTP routes coexist peacefully
+3. **Same Port**: WebSocket and HTTP share the same server port
+4. **Unified Server**: Use `Seobeo_Web_Server_Start()` for everything
+
+## Testing the Server
+
+### JavaScript Client (Browser):
+```javascript
+const ws = new WebSocket('ws://localhost:8080/echo');
+
+ws.onopen = () => {
+  console.log('Connected!');
+  ws.send('Hello, Server!');
+};
+
+ws.onmessage = (event) => {
+  console.log('Received:', event.data);
+};
+```
+
+### Python Client:
+```python
+import websocket
+
+ws = websocket.create_connection("ws://localhost:8080/chat")
+ws.send("Hello from Python!")
+print(ws.recv())
+ws.close()
+```
+
+## Performance Considerations
+
+1. **Fork Mode**: Each connection runs in a separate process (SEOBEO_MODE_FORK)
+2. **Edge Mode**: Multi-threaded connections (SEOBEO_MODE_EDGE)
+3. **Broadcast**: Iterates through all connections (O(n) complexity)
+4. **Fragmentation**: Large messages are automatically fragmented (1MB chunks)
+
+## Complete Feature Comparison
+
+| Feature | Implemented |
+|---------|-------------|
+| ✅ WebSocket upgrade handshake | Yes |
+| ✅ Text messages | Yes |
+| ✅ Binary messages | Yes |
+| ✅ Message fragmentation | Yes |
+| ✅ Ping/Pong | Yes |
+| ✅ Close handshake | Yes |
+| ✅ Multiple endpoints | Yes |
+| ✅ Broadcast | Yes |
+| ✅ Connection tracking | Yes |
+| ✅ Integration with HTTP server | Yes |
+| ✅ Custom user data per handler | Yes |
+
+The Seobeo WebSocket Server provides a complete, production-ready WebSocket implementation that works seamlessly with the existing HTTP server!