Chromium closes WebSocket with no explanation
I have Chromium 12.0.742.112 (90304) and I'm trying to set up a simple WebSocket server with it. I receive a handshake request like:
Upgrade: WebSocket
Connection: Upgrade
Host: akira:22222
Origin: http://akira:22222
Sec-WebSocket-Key1: ;39LP*eC48 n /r0P6 v6548
Sec-WebSocket-Key2: 1 0 r 362547 4 4 G
followed by 8 key bytes, in this case 88 09 F9 EE 21 13 F4 0D. I've verified that these are the same keys Chromium shows in the Network console tab. I calculate the first two keys as:
Key1: 3948066548 / 4 = 987016637
Key2: 1036254744 / 12 = 86354562
and send my response:
000001 48 54 54 50 2F 31 2E 31 20 31 30 31 20 57 65 62 HTTP/1.1 101 Web
000011 20 53 6F 63 6B 65 74 20 50 72 6F 74 6F 63 6F 6C Socket Protocol
000021 20 48 61 6E 64 73 68 61 6B 65 0D 0A 55 70 67 72 Handshake..Upgr
000031 61 64 65 3A 20 57 65 62 53 6F 63 6B 65 74 0D 0A ade: WebSocket..
000041 43 6F 6E 6E 65 63 74 69 6F 6E 3A 20 55 70 67 72 Connection: Upgr
000051 61 64 65 0D 0A 53 65 63 2D 57 65 62 53 6F 63 6B ade..Sec-WebSock
000061 65 74 2D 4F 72 69 67 69 6E 3A 20 68 74 74 70 3A et-Origin: http:
000071 2F 2F 61 6B 69 72 61 3A 32 32 32 32 32 0D 0A 53 //akira:22222..S
000081 65 63 2D 57 65 62 53 6F 63 6B 65 74 2D 4C 6F 63 ec-WebSocket-Loc
000091 61 74 69 6F 6E 3A 20 77 73 3A 2F 2F 61 6B 69 72 ation: ws://akir
0000A1 61 3A 32 32 32 32 32 2F 73 6F 63 6B 65 74 0D 0A a:22222/socket..
0000B1 0D 0A FF F4 2E 12 9D DC 12 C2 56 40 B8 09 F3 84 ..........V@....
0000C1 CA EF .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..##############
Chromium just closes the socket without even printing a message in the JS console. I can't tell what I'm doing wrong?
The server-side code is in Lua (thus why the above hex dump is indexed starting from 1):
require('crypto')
local handshake = {
"HTTP/1.1 101 Web Socket Protocol Handshake",
"Upgrade: WebSocket",
"Connection: Upgrade",
"Sec-WebSocket-Origin: " .. request.header.origin,
"Sec-WebSocket-Location: " ..
request.header.origin:gsub('http:', 'ws:') .. "/socket",
'\r\n'}
log.debug("Request header:\n%s\n", table.concat(request.rawheader, '\n'))
local client = response.socket
client:settimeout(10)
local keys =
{request.header.sec_websocket_key1, request.header.sec_websocket_key2}
local sum = {}
for i, k in ipairs(keys) do
local nspc = 0
sum[i] = ''
k:gsub('%d', function(n) sum[i] = sum[i] ..n end)
k:gsub(' ', function() nspc = nspc + 1 end)
log.debug("Key%d: %s / %d = ", i, sum[i], nspc)
sum[i] = tostring(tonumber(sum[i]) / nspc)
log.debug("%s\n", sum[i])
end
local key = assert(client:receive(8))
local bytes = {key:byte(1, #key)}
local keydump = {}
for i = 1, #bytes do keydump[i] = ('%02X'):format(bytes[i]) end
log.debug("Key3: %s\n", table.concat(keydump, ' '))
local resp = crypto.evp.digest('md5', table.concat(sum) .. key, true)
handshake = table.concat(handshake, '\r\n') .. resp
client:settimeout(0.1)
log.debug("Send handshake:\n%s\n", rena.debug.hexdump(handshake))
client:send(handshake)
repeat
local res, err = client:receive('*l')
if res then log.debug("R: %s\n", res) end
local res, err = client:send("Test " .. tostring(os.time()) .. '\n')
if res then socket.sleep(1)
elseif err == 'timeout' then log.debug("WS: Timed out\n")
elseif err == 'closed' then log.debug("WS: Closed\n")
else log.error("WS: Error: %s\n", tostring(err))
end
until not res
(This script is loaded by the server script which presets some variables such as request and response, and uses LuaCrypto for MD5). Everything looks right (though I notice all the examples show nice ASCII characters for the binary keys whereas mine are mostly unprintable), but it just closes the socket.
Unfortunately Chromium is the only browser I have that supports WebSocket at all (unless there's some way to enable it in Firefox 6?), so I c开发者_StackOverflow中文版an't check in anything else.
Well I found the issue. The first two keys are meant to be concatenated as 32-bit integers (into a single 64-bit integer), not as ASCII strings:
- sum[i] = tostring(tonumber(sum[i]) / nspc)
+ sum[i] = ('%08X'):format(tonumber(sum[i]) / nspc)
- local resp = crypto.evp.digest('md5', table.concat(sum) .. key, true)
- handshake = table.concat(handshake, '\r\n') .. resp
+ local challenge = ''
+ (sum[1] .. sum[2]):gsub('..', function(byte)
+ challenge = challenge .. string.char(tonumber(byte, 16))
+ end)
+
+ challenge = challenge .. key
+ log.debug("Ch: %s\n", challenge:tohex())
+
+ local resp = crypto.evp.digest('md5', challenge, true)
+ handshake = table.concat(handshake, '\r\n') .. resp
With that Chromium accepts the connection. I've filed a bug to complain about the lack of error message with an incorrect hash.
精彩评论