feat(webbridge): implement asynchronous WebSocket connection handling

This commit is contained in:
MayaTheShy
2026-03-29 00:35:23 -04:00
parent 63e110f2ee
commit caf82ba81d

View File

@@ -232,28 +232,46 @@ function WebBridge.wsConnect(wsUrl, handlers, opts)
local receiveTimeout = opts.receiveTimeout or 30
while true do
local ok, ws = pcall(http.websocket, wsUrl)
-- Use async API to avoid unfiltered os.pullEvent() in http.websocket()
local aok, aerr = http.websocketAsync(wsUrl)
if not aok then
if handlers.onError then handlers.onError(aerr) end
os.sleep(reconnectDelay)
else
-- Wait for websocket_success or websocket_failure (filtered)
local ws = nil
while true do
local event, url, param = os.pullEvent()
if event == 'websocket_success' and url == wsUrl then
ws = param
break
elseif event == 'websocket_failure' and url == wsUrl then
if handlers.onError then handlers.onError(param) end
break
end
end
if ok and ws then
if ws then
if handlers.onConnect then
handlers.onConnect(ws)
end
-- Message receive loop
-- Event-based message loop — uses os.pullEvent() with awareness
-- of websocket_message, websocket_closed, and timer events.
-- Unlike ws.receive(), this loop does NOT swallow unrelated events
-- because CC:Tweaked's parallel scheduler delivers each event to
-- all coroutines whose filter matches (or filter is nil).
local keepaliveTimer = os.startTimer(receiveTimeout)
while true do
local rok, msg = pcall(ws.receive, receiveTimeout)
local event, p1, p2, p3 = os.pullEvent()
if not rok then
-- Connection error
break
end
if event == 'websocket_message' and p1 == wsUrl then
-- Reset keepalive timer on each message
os.cancelTimer(keepaliveTimer)
keepaliveTimer = os.startTimer(receiveTimeout)
if not msg then
-- Timeout — send keepalive ping
local pok = pcall(ws.send, textutils.serialiseJSON({ type = 'ping' }))
if not pok then break end
else
local data = textutils.unserialiseJSON(msg)
local data = textutils.unserialiseJSON(p2)
if data then
if data.type == 'pong' then
-- Keepalive response, handled
@@ -261,6 +279,15 @@ function WebBridge.wsConnect(wsUrl, handlers, opts)
handlers.onMessage(ws, data)
end
end
elseif event == 'websocket_closed' and p1 == wsUrl then
break
elseif event == 'timer' and p1 == keepaliveTimer then
-- No message received within timeout — send keepalive ping
local pok = pcall(ws.send, textutils.serialiseJSON({ type = 'ping' }))
if not pok then break end
keepaliveTimer = os.startTimer(receiveTimeout)
end
end
@@ -269,14 +296,11 @@ function WebBridge.wsConnect(wsUrl, handlers, opts)
if handlers.onDisconnect then
handlers.onDisconnect()
end
else
if handlers.onError then
handlers.onError(ws) -- ws contains the error string on failure
end
end
os.sleep(reconnectDelay)
end
end
end
---------------------------------------------------------------------------