|
@@ -1,135 +1,133 @@
|
|
|
--rewrite from https://github.com/creationix/nodemcu-webide
|
|
|
|
|
|
local function decode(chunk)
|
|
|
- if #chunk < 2 then return end
|
|
|
- local second = string.byte(chunk, 2)
|
|
|
- local len = bit.band(second, 0x7f)
|
|
|
- local offset
|
|
|
- if len == 126 then
|
|
|
- if #chunk < 4 then return end
|
|
|
- len = bit.bor(
|
|
|
- bit.lshift(string.byte(chunk, 3), 8),
|
|
|
- string.byte(chunk, 4))
|
|
|
- offset = 4
|
|
|
- elseif len == 127 then
|
|
|
- if #chunk < 10 then return end
|
|
|
- len = bit.bor(
|
|
|
+ if #chunk < 2 then return end
|
|
|
+ local second = string.byte(chunk, 2)
|
|
|
+ local len = bit.band(second, 0x7f)
|
|
|
+ local offset
|
|
|
+
|
|
|
+ if len == 126 then
|
|
|
+ if #chunk < 4 then return end
|
|
|
+
|
|
|
+ len = bit.bor(bit.lshift(string.byte(chunk, 3), 8), string.byte(chunk, 4))
|
|
|
+ offset = 4
|
|
|
+
|
|
|
+ elseif len == 127 then
|
|
|
+
|
|
|
+ if #chunk < 10 then return end
|
|
|
+
|
|
|
-- Ignore lengths longer than 32bit
|
|
|
- bit.lshift(string.byte(chunk, 7), 24),
|
|
|
- bit.lshift(string.byte(chunk, 8), 16),
|
|
|
- bit.lshift(string.byte(chunk, 9), 8),
|
|
|
- string.byte(chunk, 10))
|
|
|
- offset = 10
|
|
|
- else
|
|
|
- offset = 2
|
|
|
- end
|
|
|
- local mask = bit.band(second, 0x80) > 0
|
|
|
- if mask then
|
|
|
- offset = offset + 4
|
|
|
- end
|
|
|
- if #chunk < offset + len then return end
|
|
|
-
|
|
|
- local first = string.byte(chunk, 1)
|
|
|
- local payload = string.sub(chunk, offset + 1, offset + len)
|
|
|
- assert(#payload == len, "Length mismatch")
|
|
|
- if mask then
|
|
|
- payload = crypto.mask(payload, string.sub(chunk, offset - 3, offset))
|
|
|
- end
|
|
|
- local extra = string.sub(chunk, offset + len + 1)
|
|
|
- local opcode = bit.band(first, 0xf)
|
|
|
- return extra, payload, opcode
|
|
|
+ len = bit.bor(bit.lshift(string.byte(chunk, 7), 24), bit.lshift(string.byte(chunk, 8), 16),
|
|
|
+ bit.lshift(string.byte(chunk, 9), 8), string.byte(chunk, 10))
|
|
|
+ offset = 10
|
|
|
+ else
|
|
|
+ offset = 2
|
|
|
+ end
|
|
|
+
|
|
|
+ local mask = bit.band(second, 0x80) > 0
|
|
|
+ if mask then
|
|
|
+ offset = offset + 4
|
|
|
+ end
|
|
|
+
|
|
|
+ if #chunk < offset + len then return end
|
|
|
+
|
|
|
+ local first = string.byte(chunk, 1)
|
|
|
+ local payload = string.sub(chunk, offset + 1, offset + len)
|
|
|
+ assert(#payload == len, "Length mismatch")
|
|
|
+ if mask then
|
|
|
+ payload = crypto.mask(payload, string.sub(chunk, offset - 3, offset))
|
|
|
+ end
|
|
|
+ local extra = string.sub(chunk, offset + len + 1)
|
|
|
+ local opcode = bit.band(first, 0xf)
|
|
|
+
|
|
|
+ return extra, payload, opcode
|
|
|
end
|
|
|
|
|
|
local function encode(payload, opcode)
|
|
|
- opcode = opcode or 2
|
|
|
- assert(type(opcode) == "number", "opcode must be number")
|
|
|
- assert(type(payload) == "string", "payload must be string")
|
|
|
- local len = #payload
|
|
|
- local head = string.char(
|
|
|
- bit.bor(0x80, opcode),
|
|
|
- bit.bor(len < 126 and len or len < 0x10000 and 126 or 127)
|
|
|
- )
|
|
|
- if len >= 0x10000 then
|
|
|
- head = head .. string.char(
|
|
|
- 0,0,0,0, -- 32 bit length is plenty, assume zero for rest
|
|
|
- bit.band(bit.rshift(len, 24), 0xff),
|
|
|
- bit.band(bit.rshift(len, 16), 0xff),
|
|
|
- bit.band(bit.rshift(len, 8), 0xff),
|
|
|
- bit.band(len, 0xff)
|
|
|
- )
|
|
|
- elseif len >= 126 then
|
|
|
- head = head .. string.char(bit.band(bit.rshift(len, 8), 0xff), bit.band(len, 0xff))
|
|
|
- end
|
|
|
- return head .. payload
|
|
|
+ opcode = opcode or 2
|
|
|
+ assert(type(opcode) == "number", "opcode must be number")
|
|
|
+ assert(type(payload) == "string", "payload must be string")
|
|
|
+ local len = #payload
|
|
|
+ local head = string.char(bit.bor(0x80, opcode), bit.bor(len < 126 and len or len < 0x10000 and 126 or 127))
|
|
|
+
|
|
|
+ if len >= 0x10000 then
|
|
|
+ -- 32 bit length is plenty, assume zero for rest
|
|
|
+ head = head .. string.char(0, 0, 0, 0, bit.band(bit.rshift(len, 24), 0xff), bit.band(bit.rshift(len, 16), 0xff),
|
|
|
+ bit.band(bit.rshift(len, 8), 0xff), bit.band(len, 0xff))
|
|
|
+ elseif len >= 126 then
|
|
|
+ head = head .. string.char(bit.band(bit.rshift(len, 8), 0xff), bit.band(len, 0xff))
|
|
|
+ end
|
|
|
+
|
|
|
+ return head .. payload
|
|
|
end
|
|
|
|
|
|
local guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
|
local function acceptKey(key)
|
|
|
- return crypto.toBase64(crypto.hash("sha1", key .. guid))
|
|
|
+ return crypto.toBase64(crypto.hash("sha1", key .. guid))
|
|
|
end
|
|
|
|
|
|
-return function (connection, payload)
|
|
|
- local buffer = false
|
|
|
- local socket = {}
|
|
|
- local queue = {}
|
|
|
- local waiting = false
|
|
|
- local function onSend()
|
|
|
- if queue[1] then
|
|
|
- local data = table.remove(queue, 1)
|
|
|
- return connection:send(data, onSend)
|
|
|
- end
|
|
|
- waiting = false
|
|
|
- end
|
|
|
- function socket.send(...)
|
|
|
- local data = encode(...)
|
|
|
- if not waiting then
|
|
|
- waiting = true
|
|
|
- connection:send(data, onSend)
|
|
|
- else
|
|
|
- queue[#queue + 1] = data
|
|
|
- end
|
|
|
- collectgarbage()
|
|
|
- print(node.heap())
|
|
|
- end
|
|
|
-
|
|
|
- connection:on("receive", function(_, chunk)
|
|
|
- if buffer then
|
|
|
- buffer = buffer .. chunk
|
|
|
- while true do
|
|
|
- local extra, payload, opcode = decode(buffer)
|
|
|
- if not extra then return end
|
|
|
- buffer = extra
|
|
|
- socket.onmessage(payload, opcode)
|
|
|
+-- Dispatch function
|
|
|
+return function(connection, payload)
|
|
|
+ local buffer = false
|
|
|
+ local socket = {}
|
|
|
+ local queue = {}
|
|
|
+ local waiting = false
|
|
|
+
|
|
|
+ local function onSend()
|
|
|
+ if queue[1] then
|
|
|
+ local data = table.remove(queue, 1)
|
|
|
+ return connection:send(data, onSend)
|
|
|
+ end
|
|
|
+ waiting = false
|
|
|
+ end
|
|
|
+
|
|
|
+ function socket.send(...)
|
|
|
+ local data = encode(...)
|
|
|
+ if not waiting then
|
|
|
+ waiting = true
|
|
|
+ connection:send(data, onSend)
|
|
|
+ else
|
|
|
+ queue[#queue + 1] = data
|
|
|
+ end
|
|
|
+ collectgarbage()
|
|
|
+ print(node.heap())
|
|
|
+ end
|
|
|
+
|
|
|
+ connection:on("receive", function(_, chunk)
|
|
|
+ if buffer then
|
|
|
+ buffer = buffer .. chunk
|
|
|
+ while true do
|
|
|
+ local extra, payload, opcode = decode(buffer)
|
|
|
+ if not extra then return end
|
|
|
+ buffer = extra
|
|
|
+ socket.onmessage(payload, opcode)
|
|
|
+ end
|
|
|
end
|
|
|
- end
|
|
|
- end)
|
|
|
-
|
|
|
- connection:on("sent", function(_, _)
|
|
|
- if socket.onsent ~= nil then
|
|
|
- socket.onsent()
|
|
|
- end
|
|
|
- end)
|
|
|
-
|
|
|
- connection:on("disconnection", function(_, _)
|
|
|
- if socket.onclose ~= nil then
|
|
|
- socket.onclose()
|
|
|
- end
|
|
|
- end)
|
|
|
-
|
|
|
- local req = dofile("httpserver-request.lc")(payload)
|
|
|
- local key = payload:match("Sec%-WebSocket%-Key: ([A-Za-z0-9+/=]+)")
|
|
|
- local fileExists = file.open(req.uri.file, "r")
|
|
|
- file.close()
|
|
|
- if req.method == "GET" and key and fileExists then
|
|
|
- connection:send(
|
|
|
- "HTTP/1.1 101 Switching Protocols\r\n" ..
|
|
|
- "Upgrade: websocket\r\nConnection: Upgrade\r\n" ..
|
|
|
- "Sec-WebSocket-Accept: " .. acceptKey(key) .. "\r\n\r\n",
|
|
|
- function () dofile(req.uri.file)(socket) end)
|
|
|
- buffer = ""
|
|
|
- else
|
|
|
- connection:send(
|
|
|
- "HTTP/1.1 404 Not Found\r\nConnection: Close\r\n\r\n",
|
|
|
- connection.close)
|
|
|
- end
|
|
|
+ end)
|
|
|
+
|
|
|
+ connection:on("sent", function(_, _)
|
|
|
+ if socket.onsent ~= nil then
|
|
|
+ socket.onsent()
|
|
|
+ end
|
|
|
+ end)
|
|
|
+
|
|
|
+ connection:on("disconnection", function(_, _)
|
|
|
+ if socket.onclose ~= nil then
|
|
|
+ socket.onclose()
|
|
|
+ end
|
|
|
+ end)
|
|
|
+
|
|
|
+ local req = dofile("httpserver-request.lc")(payload)
|
|
|
+ local key = payload:match("Sec%-WebSocket%-Key: ([A-Za-z0-9+/=]+)")
|
|
|
+ local fileExists = file.open(req.uri.file, "r")
|
|
|
+ file.close()
|
|
|
+ if req.method == "GET" and key and fileExists then
|
|
|
+ connection:send("HTTP/1.1 101 Switching Protocols\r\n" ..
|
|
|
+ "Upgrade: websocket\r\nConnection: Upgrade\r\n" ..
|
|
|
+ "Sec-WebSocket-Accept: " .. acceptKey(key) .. "\r\n\r\n",
|
|
|
+ function() dofile(req.uri.file)(socket) end)
|
|
|
+ buffer = ""
|
|
|
+ else
|
|
|
+ connection:send("HTTP/1.1 404 Not Found\r\nConnection: Close\r\n\r\n", connection.close)
|
|
|
+ end
|
|
|
end
|