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