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.
104 lines
2.9 KiB
104 lines
2.9 KiB
local sha1 = require'websocket.tools'.sha1
|
|
local base64 = require'websocket.tools'.base64
|
|
local tinsert = table.insert
|
|
|
|
local guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
|
|
local sec_websocket_accept = function(sec_websocket_key)
|
|
local a = sec_websocket_key..guid
|
|
local sha1 = sha1(a)
|
|
assert((#sha1 % 2) == 0)
|
|
return base64.encode(sha1)
|
|
end
|
|
|
|
local http_headers = function(request)
|
|
local headers = {}
|
|
if not request:match('.*HTTP/1%.1') then
|
|
return headers
|
|
end
|
|
request = request:match('[^\r\n]+\r\n(.*)')
|
|
local empty_line
|
|
for line in request:gmatch('[^\r\n]*\r\n') do
|
|
local name,val = line:match('([^%s]+)%s*:%s*([^\r\n]+)')
|
|
if name and val then
|
|
name = name:lower()
|
|
if not name:match('sec%-websocket') then
|
|
val = val:lower()
|
|
end
|
|
if not headers[name] then
|
|
headers[name] = val
|
|
else
|
|
headers[name] = headers[name]..','..val
|
|
end
|
|
elseif line == '\r\n' then
|
|
empty_line = true
|
|
else
|
|
assert(false,line..'('..#line..')')
|
|
end
|
|
end
|
|
return headers,request:match('\r\n\r\n(.*)')
|
|
end
|
|
|
|
local upgrade_request = function(req)
|
|
local format = string.format
|
|
local lines = {
|
|
format('GET %s HTTP/1.1',req.uri or ''),
|
|
format('Host: %s',req.host),
|
|
'Upgrade: websocket',
|
|
'Connection: Upgrade',
|
|
format('Sec-WebSocket-Key: %s',req.key),
|
|
format('Sec-WebSocket-Protocol: %s',table.concat(req.protocols,', ')),
|
|
'Sec-WebSocket-Version: 13',
|
|
}
|
|
if req.origin then
|
|
tinsert(lines,string.format('Origin: %s',req.origin))
|
|
end
|
|
if req.port and req.port ~= 80 then
|
|
lines[2] = format('Host: %s:%d',req.host,req.port)
|
|
end
|
|
tinsert(lines,'\r\n')
|
|
return table.concat(lines,'\r\n')
|
|
end
|
|
|
|
local accept_upgrade = function(request,protocols)
|
|
local headers = http_headers(request)
|
|
if headers['upgrade'] ~= 'websocket' or
|
|
not headers['connection'] or
|
|
not headers['connection']:match('upgrade') or
|
|
headers['sec-websocket-key'] == nil or
|
|
headers['sec-websocket-version'] ~= '13' then
|
|
return nil,'HTTP/1.1 400 Bad Request\r\n\r\n'
|
|
end
|
|
local prot
|
|
if headers['sec-websocket-protocol'] then
|
|
for protocol in headers['sec-websocket-protocol']:gmatch('([^,%s]+)%s?,?') do
|
|
for _,supported in ipairs(protocols) do
|
|
if supported == protocol then
|
|
prot = protocol
|
|
break
|
|
end
|
|
end
|
|
if prot then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
local lines = {
|
|
'HTTP/1.1 101 Switching Protocols',
|
|
'Upgrade: websocket',
|
|
'Connection: '..headers['connection'],
|
|
string.format('Sec-WebSocket-Accept: %s',sec_websocket_accept(headers['sec-websocket-key'])),
|
|
}
|
|
if prot then
|
|
tinsert(lines,string.format('Sec-WebSocket-Protocol: %s',prot))
|
|
end
|
|
tinsert(lines,'\r\n')
|
|
return table.concat(lines,'\r\n'),prot
|
|
end
|
|
|
|
return {
|
|
sec_websocket_accept = sec_websocket_accept,
|
|
http_headers = http_headers,
|
|
accept_upgrade = accept_upgrade,
|
|
upgrade_request = upgrade_request,
|
|
}
|
|
|