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/server_copas.lua

146 lines
3.5 KiB

local socket = require'socket'
local copas = require'copas'
local tools = require'websocket.tools'
local frame = require'websocket.frame'
local handshake = require'websocket.handshake'
local sync = require'websocket.sync'
local tconcat = table.concat
local tinsert = table.insert
local clients = {}
local client = function(sock,protocol)
local copas = require'copas'
local self = {}
self.state = 'OPEN'
self.is_server = true
self.sock_send = function(self,...)
return copas.send(sock,...)
end
self.sock_receive = function(self,...)
return copas.receive(sock,...)
end
self.sock_close = function(self)
sock:shutdown()
sock:close()
end
self = sync.extend(self)
self.on_close = function(self)
clients[protocol][self] = nil
end
self.broadcast = function(self,...)
for client in pairs(clients[protocol]) do
if client ~= self then
client:send(...)
end
end
self:send(...)
end
return self
end
local listen = function(opts)
local copas = require'copas'
assert(opts and (opts.protocols or opts.default))
local on_error = opts.on_error or function(s) print(s) end
local listener,err = socket.bind(opts.interface or '*',opts.port or 80)
if err then
error(err)
end
local protocols = {}
if opts.protocols then
for protocol in pairs(opts.protocols) do
clients[protocol] = {}
tinsert(protocols,protocol)
end
end
-- true is the 'magic' index for the default handler
clients[true] = {}
copas.addserver(
listener,
function(sock)
local request = {}
repeat
-- no timeout used, so should either return with line or err
local line,err = copas.receive(sock,'*l')
if line then
request[#request+1] = line
else
sock:close()
if on_error then
on_error('invalid request')
end
return
end
until line == ''
local upgrade_request = tconcat(request,'\r\n')
local response,protocol = handshake.accept_upgrade(upgrade_request,protocols)
if not response then
copas.send(sock,protocol)
sock:close()
if on_error then
on_error('invalid request')
end
return
end
copas.send(sock,response)
local handler
local new_client
local protocol_index
if protocol and opts.protocols[protocol] then
protocol_index = protocol
handler = opts.protocols[protocol]
elseif opts.default then
-- true is the 'magic' index for the default handler
protocol_index = true
handler = opts.default
else
sock:close()
if on_error then
on_error('bad protocol')
end
return
end
new_client = client(sock,protocol_index)
clients[protocol_index][new_client] = true
handler(new_client)
-- this is a dirty trick for preventing
-- copas from automatically and prematurely closing
-- the socket
while new_client.state ~= 'CLOSED' do
local dummy = {
send = function() end,
close = function() end
}
copas.send(dummy)
end
end)
local self = {}
self.close = function(_,keep_clients)
copas.removeserver(listener)
listener = nil
if not keep_clients then
for protocol,clients in pairs(clients) do
for client in pairs(clients) do
client:close()
end
end
end
end
return self
end
return {
listen = listen
}