feat(webbridge): implement asynchronous WebSocket connection handling
This commit is contained in:
@@ -232,50 +232,74 @@ function WebBridge.wsConnect(wsUrl, handlers, opts)
|
|||||||
local receiveTimeout = opts.receiveTimeout or 30
|
local receiveTimeout = opts.receiveTimeout or 30
|
||||||
|
|
||||||
while true do
|
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 ok and ws then
|
if not aok then
|
||||||
if handlers.onConnect then
|
if handlers.onError then handlers.onError(aerr) end
|
||||||
handlers.onConnect(ws)
|
os.sleep(reconnectDelay)
|
||||||
end
|
else
|
||||||
|
-- Wait for websocket_success or websocket_failure (filtered)
|
||||||
-- Message receive loop
|
local ws = nil
|
||||||
while true do
|
while true do
|
||||||
local rok, msg = pcall(ws.receive, receiveTimeout)
|
local event, url, param = os.pullEvent()
|
||||||
|
if event == 'websocket_success' and url == wsUrl then
|
||||||
if not rok then
|
ws = param
|
||||||
-- Connection error
|
break
|
||||||
|
elseif event == 'websocket_failure' and url == wsUrl then
|
||||||
|
if handlers.onError then handlers.onError(param) end
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if not msg then
|
if ws then
|
||||||
-- Timeout — send keepalive ping
|
if handlers.onConnect then
|
||||||
local pok = pcall(ws.send, textutils.serialiseJSON({ type = 'ping' }))
|
handlers.onConnect(ws)
|
||||||
if not pok then break end
|
end
|
||||||
else
|
|
||||||
local data = textutils.unserialiseJSON(msg)
|
-- Event-based message loop — uses os.pullEvent() with awareness
|
||||||
if data then
|
-- of websocket_message, websocket_closed, and timer events.
|
||||||
if data.type == 'pong' then
|
-- Unlike ws.receive(), this loop does NOT swallow unrelated events
|
||||||
-- Keepalive response, handled
|
-- because CC:Tweaked's parallel scheduler delivers each event to
|
||||||
elseif handlers.onMessage then
|
-- all coroutines whose filter matches (or filter is nil).
|
||||||
handlers.onMessage(ws, data)
|
local keepaliveTimer = os.startTimer(receiveTimeout)
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local event, p1, p2, p3 = os.pullEvent()
|
||||||
|
|
||||||
|
if event == 'websocket_message' and p1 == wsUrl then
|
||||||
|
-- Reset keepalive timer on each message
|
||||||
|
os.cancelTimer(keepaliveTimer)
|
||||||
|
keepaliveTimer = os.startTimer(receiveTimeout)
|
||||||
|
|
||||||
|
local data = textutils.unserialiseJSON(p2)
|
||||||
|
if data then
|
||||||
|
if data.type == 'pong' then
|
||||||
|
-- Keepalive response, handled
|
||||||
|
elseif handlers.onMessage then
|
||||||
|
handlers.onMessage(ws, data)
|
||||||
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pcall(ws.close)
|
||||||
|
|
||||||
|
if handlers.onDisconnect then
|
||||||
|
handlers.onDisconnect()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
pcall(ws.close)
|
os.sleep(reconnectDelay)
|
||||||
|
|
||||||
if handlers.onDisconnect then
|
|
||||||
handlers.onDisconnect()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if handlers.onError then
|
|
||||||
handlers.onError(ws) -- ws contains the error string on failure
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
os.sleep(reconnectDelay)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user