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

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,
}