fix: re-add WebSocket continuation-frame support for fragmented messages
Prosody 13.0.6 removed continuation-frame (fragmented-message) support from mod_websocket — validate_frame hard-rejects any frame with FIN=false (close code 1003). VNCtalk clients fragment large WebSocket messages (e.g. vCard sets with avatars >~64 KB), so the connection is silently reset. The rejection fires on the partial-frame validation path before handle_frame is reached, and websocket_close() does not log, so nothing appears in prosody debug logs. This patch removes the blanket FIN=false rejection from validate_frame and restores the dataBuffer fragment-accumulation logic from 0.11.6 in handle_frame: text frames (0x1) with FIN=false start a buffer, continuation frames (0x0) append, and concatenated data is returned only when FIN=true arrives. Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
This commit is contained in:
@@ -21,6 +21,7 @@ To change a patch: extract the pristine tarball, apply all patches, edit, and re
|
||||
| `muc.lib.patch` | `plugins/muc/muc.lib.lua` | (a) `broadcast()` also routes to offline *remote* affiliated members; (b) suppress the unavailable self-presence in `publicise_occupant_status`; (c) let owner/admin/member post to a room without being a present occupant (bypasses the `muc-occupant-groupchat` event and its not-in-room rejection); (d) fire `muc-config-sub-mitted` on config changes — **consumed by an external service**. |
|
||||
| `hidden.lib.patch` | `plugins/muc/hidden.lib.lua` | Hide the "publicly searchable" room config option for everyone when public rooms are restricted (upstream exempts admins). |
|
||||
| `mod_muc_unique.patch` | `plugins/mod_muc_unique.lua` | Rework muc#unique handler; answer `item-not-found` for bare-JID requests. |
|
||||
| `mod_websocket.patch` | `plugins/mod_websocket.lua` | Re-add WebSocket continuation-frame (fragmented-message) support that was removed upstream in the 0.12/13.0 rewrite. VNCtalk clients fragment large WebSocket messages (e.g. vCard sets with avatars >~64 KB); 13.0.6's `validate_frame` hard-rejected any frame with `FIN=false` (close code 1003) **silently** — no log was emitted because the rejection fired on the partial-frame path before `handle_frame` was reached, and `websocket_close()` itself does not log. This patch removes the blanket `not frame.FIN` rejection from `validate_frame` and restores the `dataBuffer` fragment-accumulation logic from 0.11.6 inside `handle_frame`: text frames (opcode 0x1) with `FIN=false` start a buffer, continuation frames (opcode 0x0) append to it, and the concatenated data is returned only when a frame with `FIN=true` arrives. |
|
||||
|
||||
## Patches removed at the 0.12.6 upgrade (M1)
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
--- a/plugins/mod_websocket.lua 2026-07-15 09:48:22.424559760 +0200
|
||||
+++ b/plugins/mod_websocket.lua 2026-07-15 09:49:05.585990882 +0200
|
||||
@@ -199,9 +199,6 @@
|
||||
end
|
||||
|
||||
-- Other (XMPP-specific) validity checks
|
||||
- if not frame.FIN then
|
||||
- return false, 1003, "Continuation frames are not supported, RFC 7395 3.3.3";
|
||||
- end
|
||||
if opcode == 0x01 and frame.data and frame.data:byte(1, 1) ~= 60 then
|
||||
return false, 1007, "Invalid payload start character, RFC 7395 3.3.3";
|
||||
end
|
||||
@@ -249,6 +246,7 @@
|
||||
end
|
||||
end
|
||||
|
||||
+ local dataBuffer;
|
||||
local function handle_frame(frame)
|
||||
module:log("debug", "Websocket received frame: opcode=%0x, %i bytes", frame.opcode, #frame.data);
|
||||
|
||||
@@ -266,12 +264,31 @@
|
||||
return "";
|
||||
elseif opcode == 0xA then -- Pong frame, MAY be sent unsolicited, eg as keepalive
|
||||
return "";
|
||||
- elseif opcode ~= 0x1 then -- Not text frame (which is all we support)
|
||||
+ end
|
||||
+
|
||||
+ -- Text (0x1) and continuation (0x0) frames: accumulate fragments
|
||||
+ if opcode == 0x0 and not dataBuffer then
|
||||
+ return false, 1002, "Unexpected continuation frame";
|
||||
+ end
|
||||
+ if opcode == 0x1 and dataBuffer then
|
||||
+ return false, 1002, "Continuation frame expected";
|
||||
+ end
|
||||
+
|
||||
+ if opcode == 0x0 then -- Continuation frame
|
||||
+ dataBuffer[#dataBuffer+1] = frame.data;
|
||||
+ elseif opcode == 0x1 then -- Text frame
|
||||
+ dataBuffer = { frame.data };
|
||||
+ else
|
||||
log("warn", "Received frame with unsupported opcode %i", opcode);
|
||||
return "";
|
||||
end
|
||||
|
||||
- return frame.data;
|
||||
+ if frame.FIN then
|
||||
+ local data = t_concat(dataBuffer, "");
|
||||
+ dataBuffer = nil;
|
||||
+ return data;
|
||||
+ end
|
||||
+ return "";
|
||||
end
|
||||
|
||||
conn:setlistener(c2s_listener);
|
||||
Reference in New Issue
Block a user