Files
Techmino/Zframework/websocket.lua
2021-02-12 22:00:30 +08:00

136 lines
3.6 KiB
Lua

--[[
websocket client pure lua implement for love2d
by flaribbit
usage:
local client = require("websocket").new()
client:connect("127.0.0.1", 5000)
client:settimeout(0)
client:send("hello from love2d")
print(client:read())
client:close()
]]
local socket = require"socket"
local bit = require"bit"
local band = bit.band
local bor = bit.bor
local bxor = bit.bxor
local shl = bit.lshift
local shr = bit.rshift
-- local log_debug = print
local log_debug = function()end
local b2s = function(b)return b and"true"or"false"end
local OPCODES = {
["CONTINUE"]=0,
["TEXT"] =1,
["BINARY"] =2,
["CLOSE"] =8,
["PING"] =9,
["PONG"] =10,
}
local _M = {
OPCODES = OPCODES,
}
_M.__index = _M
_M.new = function()
local m = {}
setmetatable(m, _M)
return m
end
_M.connect = function(self, host, port, path)
local socket = socket.tcp()
self.socket = socket
local res, err = socket:connect(host, port)
if res~=1 then
return res, err
end
log_debug("[handshake] connected")
-- WebSocket handshake
local seckey = "osT3F7mvlojIvf3/8uIsJQ=="
path = path or "/"
res, err = socket:send("GET "..path.." HTTP/1.1\r\nHost: "..host..":"..port.."\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: "..seckey.."\r\n\r\n")
repeat
res = socket:receive("*l")
until res==""
log_debug("[handshake] succeed")
end
_M.send = function(self, message)
local socket = self.socket
local mask_key = {1, 14, 5, 14}
-- message type
socket:send(string.char(bor(0x80, OPCODES.BINARY)))
-- length
local length = #message
log_debug("[encode] message length: "..length)
if length>65535 then
socket:send(string.char(bor(127, 0x80), 0, 0, 0, 0,
band(shr(length, 24), 0xff),
band(shr(length, 16), 0xff),
band(shr(length, 8), 0xff),
band(length, 0xff)))
elseif length>125 then
socket:send(string.char(bor(126, 0x80),
band(shr(length, 8), 0xff),
band(length, 0xff)))
else
socket:send(string.char(bor(length, 0x80)))
end
log_debug("[encode] masking")
socket:send(string.char(unpack(mask_key)))
local msgbyte = {message:byte(1, length)}
for i = 1, length do
msgbyte[i] = bxor(msgbyte[i], mask_key[(i-1)%4+1])
end
socket:send(string.char(unpack(msgbyte)))
log_debug("[encode] end")
end
_M.read = function(self)
-- byte 0-1
local socket = self.socket
local res, err = socket:receive(2)
if res==nil then
return res, err
end
local byte = res:byte()
local flag_FIN = byte>=0x80
local OPCODE = band(byte, 0x0f)
byte = res:byte(2)
local flag_MASK = byte>=0x80
log_debug("[decode] FIN="..b2s(flag_FIN)..", OPCODE="..OPCODE..", MASK="..b2s(flag_MASK))
-- length
local length = band(byte, 0x7f)
if length==126 then
res = socket:receive(2)
local b1, b2 = res:byte(1, 2)
length = shl(b1, 8) + b2
elseif length==127 then
res = socket:receive(8)
local b = {res:byte(1, 8)}
length = shl(b[5], 32) + shl(b[6], 24) + shl(b[7], 8) + b[8]
end
log_debug("[decode] message length: "..length)
-- data
res = socket:receive(length)
log_debug("[decode] string length: "..#res)
log_debug("[decode] end")
return res
end
_M.close = function(self)
self.socket:close()
end
_M.settimeout = function(self, t)
self.socket:settimeout(t)
end
return _M