controlpanel/lua/socket.tl

94 lines
2.5 KiB
Plaintext

local enum State
"reset"
"error"
"ok"
"viewer_connected"
end
local BAD_STATES <const> : {State: boolean} = {
["reset"] = true,
["error"] = true,
}
local type StateCallback = function(new_state: State)
local record Socket
state: State
is_bad_state: function(self: Socket): boolean
_set_state: function(self: Socket, state: State)
on_state_change: function(self: Socket, cb: StateCallback)
send: function(self: Socket, message: string)
reconnect: function(self: Socket)
close: function(self: Socket)
signal_viewer_connect: function(self: Socket, connected: boolean)
_endpoint: string
_headers: {string: string}
_callback: StateCallback
_ws: http.Websocket
end
local impl: table = {}
impl.is_bad_state = function(self: Socket): boolean
return BAD_STATES[self.state] ~= nil
end
impl._set_state = function(self: Socket, state: State)
self.state = state
self._callback(state)
end
impl.on_state_change = function(self: Socket, cb: StateCallback)
self._callback = cb
end
impl.send = function(self: Socket, message: string)
-- "message" needs to be valid JSON
-- otherwise the server will not accept it
if self:is_bad_state() then return end
local r = { pcall(self._ws.send, message) }
if r[1] == false then
if (r[2] as string):sub(-11) == "closed file" then
self:_set_state("reset")
elseif (r[2] as string):sub(-9) == "too large" then
-- TODO handle
-- the connection stays open though
end
end
end
impl.reconnect = function(self: Socket)
local r = http.websocket(self._endpoint, self._headers)
if r ~= false then
self._ws = r as http.Websocket
self:_set_state("ok")
else
self:_set_state("error")
end
end
impl.close = function(self: Socket)
if self:is_bad_state() then return end
self._ws.close()
end
impl.signal_viewer_connect = function(self: Socket, connected: boolean)
if self:is_bad_state() then return end --how?
local new_state: State = connected and "viewer_connected" or "ok"
self:_set_state(new_state)
end
local function new(endpoint: string, headers: {string: string}): Socket
return setmetatable({
state = "reset",
_endpoint = endpoint,
_headers = headers,
_callback = function(_: State) end,
_ws = nil,
}, { __index = impl })
end
return { new = new, State = State, StateCallback = StateCallback, Socket = Socket }