You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lua-lib/websocket/ev_common.lua

161 lines
4.0 KiB

local ev = require'ev'
local frame = require'websocket.frame'
local tinsert = table.insert
local tconcat = table.concat
local eps = 2^-40
local detach = function(f,loop)
if ev.Idle then
ev.Idle.new(function(loop,io)
io:stop(loop)
f()
end):start(loop)
else
ev.Timer.new(function(loop,io)
io:stop(loop)
f()
end,eps):start(loop)
end
end
local async_send = function(sock,loop)
assert(sock)
loop = loop or ev.Loop.default
local sock_send = sock.send
local buffer
local index
local callbacks = {}
local send = function(loop,write_io)
local len = #buffer
local sent,err,last = sock_send(sock,buffer,index)
if not sent and err ~= 'timeout' then
write_io:stop(loop)
if callbacks.on_err then
if write_io:is_active() then
callbacks.on_err(err)
else
detach(function()
callbacks.on_err(err)
end,loop)
end
end
elseif sent then
local copy = buffer
buffer = nil
index = nil
write_io:stop(loop)
if callbacks.on_sent then
-- detach calling callbacks.on_sent from current
-- exection if thiis call context is not
-- the send io to let send_async(_,on_sent,_) truely
-- behave async.
if write_io:is_active() then
callbacks.on_sent(copy)
else
-- on_sent is only defined when responding to "on message for close op"
-- so this can happen only once per lifetime of a websocket instance.
-- callbacks.on_sent may be overwritten by a new call to send_async
-- (e.g. due to calling ws:close(...) or ws:send(...))
local on_sent = callbacks.on_sent
detach(function()
on_sent(copy)
end,loop)
end
end
else
assert(last < len)
index = last + 1
end
end
local io = ev.IO.new(send,sock:getfd(),ev.WRITE)
local stop = function()
io:stop(loop)
buffer = nil
index = nil
end
local send_async = function(data,on_sent,on_err)
if buffer then
-- a write io is still running
buffer = buffer..data
return #buffer
else
buffer = data
end
callbacks.on_sent = on_sent
callbacks.on_err = on_err
if not io:is_active() then
send(loop,io)
if index ~= nil then
io:start(loop)
end
end
local buffered = (buffer and #buffer - (index or 0)) or 0
return buffered
end
return send_async,stop
end
local message_io = function(sock,loop,on_message,on_error)
assert(sock)
assert(loop)
assert(on_message)
assert(on_error)
local last
local frames = {}
local first_opcode
assert(sock:getfd() > -1)
local message_io
local dispatch = function(loop,io)
-- could be stopped meanwhile by on_message function
while message_io:is_active() do
local encoded,err,part = sock:receive(100000)
if err then
if err == 'timeout' and #part == 0 then
return
elseif #part == 0 then
if message_io then
message_io:stop(loop)
end
on_error(err,io,sock)
return
end
end
if last then
encoded = last..(encoded or part)
last = nil
else
encoded = encoded or part
end
repeat
local decoded,fin,opcode,rest = frame.decode(encoded)
if decoded then
if not first_opcode then
first_opcode = opcode
end
tinsert(frames,decoded)
encoded = rest
if fin == true then
on_message(tconcat(frames),first_opcode)
frames = {}
first_opcode = nil
end
end
until not decoded
if #encoded > 0 then
last = encoded
end
end
end
message_io = ev.IO.new(dispatch,sock:getfd(),ev.READ)
message_io:start(loop)
-- the might be already data waiting (which will not trigger the IO)
dispatch(loop,message_io)
return message_io
end
return {
async_send = async_send,
message_io = message_io
}