Mercurial
diff seobeo/seobeo.h @ 120:cbbf78b17cfa
[Seobeo][Websocket] Created Web socket client logic.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Thu, 08 Jan 2026 03:19:59 -0800 |
| parents | c39582f937e5 |
| children | 7b1719fa918c |
line wrap: on
line diff
--- a/seobeo/seobeo.h Wed Jan 07 16:05:57 2026 -0800 +++ b/seobeo/seobeo.h Thu Jan 08 03:19:59 2026 -0800 @@ -88,6 +88,221 @@ extern void Seobeo_Client_Request_Destroy(Seobeo_Client_Request *p_req); /* Destroy response and free all resources. */ extern void Seobeo_Client_Response_Destroy(Seobeo_Client_Response *p_resp); + +/** + * WebSocket Client API + * ------ + * + * # Overview + * + * A clean, easy-to-use WebSocket client library following RFC 6455. It will auto handle over 64 bits long data into a continous stream. + * + * ## Examples + * + * ### 1. Simple Text Echo + * + * ```c + * // Connect to WebSocket server + * Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://echo.websocket.org"); + * if (!p_ws) + * { + * printf("Failed to connect\n"); + * return; + * } + * + * // Send text message + * const char *message = "Hello, WebSocket!"; + * Seobeo_WebSocket_Send_Text(p_ws, message); + * + * // Receive echo response + * Seobeo_WebSocket_Message *p_msg = Seobeo_WebSocket_Receive(p_ws); + * if (p_msg && p_msg->opcode == SEOBEO_WS_OPCODE_TEXT) + * { + * printf("Received: %.*s\n", (int)p_msg->length, (char*)p_msg->data); + * Seobeo_WebSocket_Message_Destroy(p_msg); + * } + * + * // Close connection + * Seobeo_WebSocket_Close(p_ws, 1000, "Normal closure"); + * Seobeo_WebSocket_Destroy(p_ws); + * ``` + * + * ### 2. Binary Data Transfer + * + * ```c + * Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://example.com/data"); + * + * // Send binary data + * uint8 data[] = {0x01, 0x02, 0x03, 0xAA, 0xBB, 0xCC}; + * Seobeo_WebSocket_Send_Binary(p_ws, data, sizeof(data)); + * + * // Receive binary response + * Seobeo_WebSocket_Message *p_msg = Seobeo_WebSocket_Receive(p_ws); + * if (p_msg && p_msg->opcode == SEOBEO_WS_OPCODE_BINARY) + * { + * printf("Received %zu bytes\n", p_msg->length); + * // Process binary data... + * Seobeo_WebSocket_Message_Destroy(p_msg); + * } + * + * Seobeo_WebSocket_Destroy(p_ws); + * ``` + * + * ### 3. Chat Application + * + * ```c + * Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://chat.example.com"); + * + * // Send chat message + * Seobeo_WebSocket_Send_Text(p_ws, "Hello everyone!"); + * + * // Continuous receive loop + * while (1) + * { + * Seobeo_WebSocket_Message *p_msg = Seobeo_WebSocket_Receive(p_ws); + * if (p_msg) + * { + * if (p_msg->opcode == SEOBEO_WS_OPCODE_TEXT) + * { + * printf("Chat: %.*s\n", (int)p_msg->length, (char*)p_msg->data); + * } + * Seobeo_WebSocket_Message_Destroy(p_msg); + * } + * + * usleep(10000); // 10ms sleep to avoid busy waiting + * } + * + * Seobeo_WebSocket_Destroy(p_ws); + * ``` + * + * ### 4. Ping/Pong Keep-Alive + * + * ```c + * Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://api.example.com"); + * + * // Send ping to keep connection alive + * Seobeo_WebSocket_Send_Ping(p_ws, "keep-alive"); + * + * // Server will automatically receive pong responses + * // (pong responses are handled internally) + * + * Seobeo_WebSocket_Destroy(p_ws); + * ``` + * + * ### 5. Handling Different Message Types + * + * ```c + * Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://example.com"); + * + * while (1) + * { + * Seobeo_WebSocket_Message *p_msg = Seobeo_WebSocket_Receive(p_ws); + * if (p_msg) + * { + * switch (p_msg->opcode) + * { + * case SEOBEO_WS_OPCODE_TEXT: + * printf("Text: %.*s\n", (int)p_msg->length, (char*)p_msg->data); + * break; + * + * case SEOBEO_WS_OPCODE_BINARY: + * printf("Binary: %zu bytes\n", p_msg->length); + * break; + * + * default: + * printf("Unknown opcode: 0x%X\n", p_msg->opcode); + * break; + * } + * + * Seobeo_WebSocket_Message_Destroy(p_msg); + * } + * + * usleep(10000); + * } + * + * Seobeo_WebSocket_Destroy(p_ws); + * ``` + * + * ### 6. Graceful Shutdown + * + * ```c + * Seobeo_WebSocket *p_ws = Seobeo_WebSocket_Connect("wss://example.com"); + * + * // Do work... + * + * // Close with custom status code and reason + * Seobeo_WebSocket_Close(p_ws, 1000, "Client shutting down"); + * Seobeo_WebSocket_Destroy(p_ws); + * ``` + * + * ## WebSocket Close Codes + * + * Common close status codes (RFC 6455): + * + * - **1000**: Normal closure + * - **1001**: Going away (e.g., server shutdown, browser navigation) + * - **1002**: Protocol error + * - **1003**: Unsupported data type + * - **1006**: Abnormal closure (no close frame received) + * - **1007**: Invalid frame payload data + * - **1008**: Policy violation + * - **1009**: Message too big + * - **1010**: Mandatory extension missing + * - **1011**: Internal server error + * + * ## Building + * + * ### Build the library: + * ```bash + * bazel build //seobeo:seobeo_client + * ``` + * + * ### Build and run the WebSocket test: + * ```bash + * bazel test //seobeo:seobeo_websocket_test + * ``` + * + * ## Message Structure + * + * ```c + * typedef struct { + * Seobeo_WebSocket_Opcode opcode; // Message type + * uint8 *data; // Message payload + * size_t length; // Payload length + * boolean is_final; // Final fragment flag + * } Seobeo_WebSocket_Message; + * ``` + * + * ## Protocol Details + * + * The implementation follows RFC 6455 + * + * 1. **Handshake**: HTTP Upgrade request with `Sec-WebSocket-Key` + * 2. **Frame Format**: Proper FIN, opcode, mask, and payload length handling + * 3. **Masking**: All client-to-server frames are masked (required by RFC) + * 4. **Fragmentation**: Handles fragmented messages across multiple frames + * 5. **Control Frames**: Proper handling of ping, pong, and close frames + */ + +/* Connect to WebSocket server with given URL (ws:// or wss://). */ +extern Seobeo_WebSocket *Seobeo_WebSocket_Connect(const char *url); +/* Send text message over WebSocket. */ +extern int32 Seobeo_WebSocket_Send_Text(Seobeo_WebSocket *p_ws, const char *text); +/* Send binary message over WebSocket. */ +extern int32 Seobeo_WebSocket_Send_Binary(Seobeo_WebSocket *p_ws, const uint8 *data, size_t length); +/* Send ping frame (for keep-alive). */ +extern int32 Seobeo_WebSocket_Send_Ping(Seobeo_WebSocket *p_ws, const char *payload); +/* Send pong frame (usually in response to ping). */ +extern int32 Seobeo_WebSocket_Send_Pong(Seobeo_WebSocket *p_ws, const char *payload); +/* Receive a message from WebSocket. Returns NULL if no message available or on error. */ +extern Seobeo_WebSocket_Message *Seobeo_WebSocket_Receive(Seobeo_WebSocket *p_ws); +/* Destroy received message. */ +extern void Seobeo_WebSocket_Message_Destroy(Seobeo_WebSocket_Message *p_msg); +/* Close WebSocket connection with status code and optional reason. */ +extern int32 Seobeo_WebSocket_Close(Seobeo_WebSocket *p_ws, uint16 code, const char *reason); +/* Destroy WebSocket and free all resources. */ +extern void Seobeo_WebSocket_Destroy(Seobeo_WebSocket *p_ws); + /* Initialize the router system (called automatically by Seobeo_Web_Server_Start) */ extern void Seobeo_Router_Init(); /* Register an API route handler. Call before starting server. */