From 2fa949aa831f368e803a191e8f155ce4dd03ed34 Mon Sep 17 00:00:00 2001 From: Hisham Mahgoub Date: Tue, 25 Aug 2026 18:38:41 +0300 Subject: [PATCH] Rename WebSocketDataCallback's frame param from opcode to bits The parameter was named `opcode`, but civetweb actually passes the raw first byte of the WebSocket frame header (FIN flag + reserved bits + the real opcode packed into the low 4 bits), not the opcode alone. Renamed to `bits` to match civetweb's own naming, and updated the docs (EN + AR) and examples to match this correct understanding. --- Examples/simple_chat_room.alusus | 4 +- Examples/websocket_server.alusus | 4 +- ...\331\203\330\252.\330\243\330\263\330\263" | 4 +- ...\330\264\330\251.\330\243\330\263\330\263" | 4 +- Http.alusus | 2 +- websocket_documentation.ar.md | 46 +++++++++++++++---- websocket_documentation.en.md | 39 +++++++++++++--- 7 files changed, 77 insertions(+), 26 deletions(-) diff --git a/Examples/simple_chat_room.alusus b/Examples/simple_chat_room.alusus index 07c0e7f..555b44f 100644 --- a/Examples/simple_chat_room.alusus +++ b/Examples/simple_chat_room.alusus @@ -46,12 +46,12 @@ module WebSocketChat { func onWebSocketData( conn: ptr[Http.Connection], - opcode: Int, + bits: Int, data: CharsPtr, len: ArchWord, _: ptr[Void] ): Int { - if (opcode & 1) == 0 return 1; + if (bits & 0x0F) != 1 return 1; // Ignore non-text frames def i: Int = 0; while i < clientCount and clients(i).conn != conn { diff --git a/Examples/websocket_server.alusus b/Examples/websocket_server.alusus index 4975a7a..70e298b 100644 --- a/Examples/websocket_server.alusus +++ b/Examples/websocket_server.alusus @@ -133,12 +133,12 @@ module WebSocketExample { func onWebSocketData( connection: ptr[Http.Connection], - opcode: Int, + bits: Int, data: CharsPtr, dataLen: ArchWord, userData: ptr[Void] ): Int { - if ((opcode & 0x1) == 0) { + if ((bits & 0x0F) != 1) { // Ignore non-text frames return 1; } diff --git "a/Examples/\331\205\330\253\330\247\331\204_\330\256\330\247\330\257\331\205_\331\210\331\212\330\250_\330\263\331\210\331\203\330\252.\330\243\330\263\330\263" "b/Examples/\331\205\330\253\330\247\331\204_\330\256\330\247\330\257\331\205_\331\210\331\212\330\250_\330\263\331\210\331\203\330\252.\330\243\330\263\330\263" index 3334f4e..539fac9 100644 --- "a/Examples/\331\205\330\253\330\247\331\204_\330\256\330\247\330\257\331\205_\331\210\331\212\330\250_\330\263\331\210\331\203\330\252.\330\243\330\263\330\263" +++ "b/Examples/\331\205\330\253\330\247\331\204_\330\256\330\247\330\257\331\205_\331\210\331\212\330\250_\330\263\331\210\331\203\330\252.\330\243\330\263\330\263" @@ -130,12 +130,12 @@ دالة عند_بيانات_ويب_سوكت( اتصال: مؤشر[بـننت.اتـصال]، - رمز_العملية: صـحيح، + بتات: صـحيح، بيانات: مـؤشر_محارف، طول_البيانات: طـبيعي_متكيف، بيانات_المستخدم: مؤشر[فـراغ] ): صـحيح { - إذا (رمز_العملية & 1) == 0 ارجع 1؛ + إذا (بتات & 0x0F) != 1 ارجع 1؛ // تجاهل الإطارات غير النصية طـرفية.اطبع("تم استلام رسالة WebSocket: ")؛ طـرفية.اطبع(بيانات، طول_البيانات)؛ diff --git "a/Examples/\331\205\330\253\330\247\331\204_\330\272\330\261\331\201\330\251_\330\257\330\261\330\257\330\264\330\251.\330\243\330\263\330\263" "b/Examples/\331\205\330\253\330\247\331\204_\330\272\330\261\331\201\330\251_\330\257\330\261\330\257\330\264\330\251.\330\243\330\263\330\263" index 49202dc..923ff70 100644 --- "a/Examples/\331\205\330\253\330\247\331\204_\330\272\330\261\331\201\330\251_\330\257\330\261\330\257\330\264\330\251.\330\243\330\263\330\263" +++ "b/Examples/\331\205\330\253\330\247\331\204_\330\272\330\261\331\201\330\251_\330\257\330\261\330\257\330\264\330\251.\330\243\330\263\330\263" @@ -47,12 +47,12 @@ دالة عند_بيانات_ويب_سوكت( اتصال: مؤشر[بـننت.اتـصال]، - رمز_العملية: صـحيح، + بتات: صـحيح، بيانات: مـؤشر_محارف، طول_البيانات: طـبيعي_متكيف، _: مؤشر[فـراغ] ): صـحيح { - إذا (رمز_العملية & 1) == 0 أرجع 1؛ + إذا (بتات & 0x0F) != 1 أرجع 1؛ // تجاهل الإطارات غير النصية عرف ت: صـحيح = 0؛ بينما ت < عدد_العملاء و العملاء(ت).اتصال != اتصال { diff --git a/Http.alusus b/Http.alusus index 94c20ea..ea665b1 100644 --- a/Http.alusus +++ b/Http.alusus @@ -102,7 +102,7 @@ module Http { def WebSocketConnectCallback: alias ptr[func (connection: ptr[Connection], userData: ptr[Void]): Int]; def WebSocketReadyCallback: alias ptr[func (connection: ptr[Connection], userData: ptr[Void]): Void]; def WebSocketDataCallback: alias ptr[func ( - connection: ptr[Connection], opcode: Int, data: CharsPtr, dataLen: ArchWord, userData: ptr[Void] + connection: ptr[Connection], bits: Int, data: CharsPtr, dataLen: ArchWord, userData: ptr[Void] ): Int]; def WebSocketCloseCallback: alias ptr[func (connection: ptr[Connection], userData: ptr[Void]): Void]; diff --git a/websocket_documentation.ar.md b/websocket_documentation.ar.md index 27755a3..49e7194 100644 --- a/websocket_documentation.ar.md +++ b/websocket_documentation.ar.md @@ -55,7 +55,7 @@ def WebSocketReadyCallback: alias ptr[func (connection: ptr[Connection], userDat ### مـنادى_بيانات_ويب_سوكت (WebSocketDataCallback) ``` -عرف مـنادى_بيانات_ويب_سوكت: لقب مؤشر[دالة (اتصال: مؤشر[بـننت.اتـصال], رمز_العملية: صـحيح, بيانات: مـؤشر_محارف, طول_البيانات: صـحيح, بيانات_المستخدم: مؤشر[فـراغ]): صـحيح]; +عرف مـنادى_بيانات_ويب_سوكت: لقب مؤشر[دالة (اتصال: مؤشر[بـننت.اتـصال], بتات: صـحيح, بيانات: مـؤشر_محارف, طول_البيانات: صـحيح, بيانات_المستخدم: مؤشر[فـراغ]): صـحيح]; ```
@@ -66,12 +66,38 @@ def WebSocketDataCallback: alias ptr[func (connection: ptr[Connection], bits: In
-يتم استدعاؤها عند استلام البيانات من العميل. يشير المعامل `رمز_العملية` إلى نوع الإطار: -- `1`: إطار نصي -- `2`: إطار ثنائي -- `8`: إطار إغلاق -- `9`: إطار Ping -- `10`: إطار Pong +يتم استدعاؤها عند استلام البيانات من العميل. المعامل `بتات` هو البايت الأول من رأس إطار ويب سوكت. + +وفق **RFC 6455**، يحتوي هذا البايت على: + +- البت 7 (`0x80`): نهائي (FIN). +- البتات 4-6 (`0x70`): بتات محجوزة، عادةً `0` ما لم تُستخدم امتدادات. +- البتات 0-3 (`0x0F`): رمز العملية (opcode). + +استخرج كل جزء قبل التحقق منه: + +``` +عرف رمز_العملية: صـحيح = بتات & 0x0F؛ +عرف نهائي: ثـنائي = (بتات & 0x80) != 0؛ +``` + +
+ +```alusus +def opcode: Int = bits & 0x0F; +def fin: Bool = (bits & 0x80) != 0; +``` + +
+ +* قيم `رمز_العملية`: + - `1`: إطار نصي + - `2`: إطار ثنائي + - `8`: إطار إغلاق + - `9`: إطار Ping + - `10`: إطار Pong + +* تكون `نهائي` قيمتها `true` عندما تكون هذه هي الإطار الأخير (أو الوحيد) من الرسالة. ### مـنادى_إغلاق_ويب_سوكت (WebSocketCloseCallback) @@ -294,12 +320,12 @@ func closeWebSocket(connection: ptr[Connection]): Int; دالة عند_بيانات_ويب_سوكت( اتصال: مؤشر[بـننت.اتـصال]، - رمز_العملية: صـحيح، + بتات: صـحيح، بيانات: مـؤشر_محارف، طول_البيانات: صـحيح، بيانات_المستخدم: مؤشر[فـراغ] ): صـحيح { - إذا رمز_العملية == 1 { // إطار نصي + إذا (بتات & 0x0F) == 1 { // إطار نصي طـرفية.اطبع("تم الاستلام: ")؛ طـرفية.اطبع(بيانات، طول_البيانات)؛ طـرفية.اطبع("\ج")؛ @@ -370,7 +396,7 @@ module WebSocketExample { }; func onData(connection: ptr[Http.Connection], bits: Int, data: CharsPtr, dataLen: Int, userData: ptr[Void]): Int { - if bits == 1 { // إطار نصي + if (bits & 0x0F) == 1 { // إطار نصي Console.print("تم الاستلام: "); Console.print(data, dataLen); Console.print("\n"); diff --git a/websocket_documentation.en.md b/websocket_documentation.en.md index 86a5eed..d31402b 100644 --- a/websocket_documentation.en.md +++ b/websocket_documentation.en.md @@ -19,29 +19,48 @@ The Alusus HTTP module now includes comprehensive WebSocket support, built on to ## WebSocket Callback Types ### WebSocketConnectCallback + ```alusus def WebSocketConnectCallback: alias ptr[func (connection: ptr[Connection], userData: ptr[Void]): Int]; ``` Called when a new WebSocket connection is established. Return 0 to accept the connection, non-zero to reject. ### WebSocketReadyCallback + ```alusus def WebSocketReadyCallback: alias ptr[func (connection: ptr[Connection], userData: ptr[Void]): Void]; ``` Called when the WebSocket connection is ready for communication. ### WebSocketDataCallback + ```alusus def WebSocketDataCallback: alias ptr[func (connection: ptr[Connection], bits: Int, data: CharsPtr, dataLen: Int, userData: ptr[Void]): Int]; ``` -Called when data is received from the client. The `bits` parameter indicates the frame type: -- `1`: Text frame -- `2`: Binary frame -- `8`: Close frame -- `9`: Ping frame -- `10`: Pong frame +Called when data is received from the client. The `bits` parameter is the raw first byte of the WebSocket frame header, not the frame type by itself. Per RFC 6455, that byte packs at: + +- bit 7 (`0x80`): The FIN flag. +- bits 4-6 (`0x70`): Reserved bits, usually `0` unless extensions are used. +- bits 0-3 (`0x0F`): The opcode. + +Extract each piece before checking it: + +```alusus +def opcode: Int = bits & 0x0F; +def fin: Bool = (bits & 0x80) != 0; +``` + +* `opcode` values: + - `1`: Text frame + - `2`: Binary frame + - `8`: Close frame + - `9`: Ping frame + - `10`: Pong frame + +* `fin` is `true` when this is the final (or only) fragment of the message. ### WebSocketCloseCallback + ```alusus def WebSocketCloseCallback: alias ptr[func (connection: ptr[Connection], userData: ptr[Void]): Void]; ``` @@ -50,6 +69,7 @@ Called when a WebSocket connection is closed. ## Core Functions ### setWebSocketHandler + ```alusus func setWebSocketHandler( context: ptr[Context], @@ -73,6 +93,7 @@ Registers WebSocket handlers for a specific URI path. - `userData`: Optional user data passed to callbacks ### setWebSocketHandlerWithSubprotocols + ```alusus func setWebSocketHandlerWithSubprotocols( context: ptr[Context], @@ -90,6 +111,7 @@ Same as `setWebSocketHandler` but with support for WebSocket subprotocols. ## Message Sending Functions ### writeToWebSocket + ```alusus func writeToWebSocket(connection: ptr[Connection], opcode: Int, data: CharsPtr, dataLen: Int): Int; ``` @@ -103,18 +125,21 @@ Send raw WebSocket frame with specified opcode. - `10`: Pong frame ### writeTextToWebSocket + ```alusus func writeTextToWebSocket(connection: ptr[Connection], data: CharsPtr, dataLen: Int): Int; ``` Send text message to WebSocket client. ### writeBinaryToWebSocket + ```alusus func writeBinaryToWebSocket(connection: ptr[Connection], data: CharsPtr, dataLen: Int): Int; ``` Send binary message to WebSocket client. ### closeWebSocket + ```alusus func closeWebSocket(connection: ptr[Connection]): Int; ``` @@ -172,7 +197,7 @@ module WebSocketExample { }; func onData(connection: ptr[Http.Connection], bits: Int, data: CharsPtr, dataLen: Int, userData: ptr[Void]): Int { - if bits == 1 { // Text frame + if (bits & 0x0F) == 1 { // Text frame Console.print("Received: "); Console.print(data, dataLen); Console.print("\n");