Some time ago, I started experimenting with WebSockets using Node.js, taking care of the backend. It worked fine, but now, when I return the protocol, it has been updated, and I can not get it to work fine.
In particular, the problem is the Sec-WebSocket-Accept header. It seems that I am doing something wrong in the calculation, although I can not understand what it can be. As far as I can tell, I follow the instructions on Wikipedia to the point.
Here is my code:
var magicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; var secWsKey = req.headers['sec-websocket-key']; var hash = require('crypto') .createHash('SHA1') .update(secWsKey + magicString) .digest('hex'); var b64hash = new Buffer(hash).toString('base64'); var handshake = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" + "Upgrade: WebSocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: " + b64hash + "\r\n" + "\r\n"; socket.write(handshake);
Connection example:
// The incoming headers { upgrade: 'websocket', connection: 'Upgrade', host: 'localhost:8888', origin: 'http://localhost:8888', 'sec-websocket-key': '4aRdFZG5uYrEUw8dsNLW6g==', 'sec-websocket-version': '13' } // The outgoing handshake HTTP/1.1 101 Switching Protocols Upgrade: WebSocket Connection: Upgrade Sec-WebSocket-Accept: YTYwZWRlMjQ4NWFhNzJiYmRjZTQ5ODI4NjUwMWNjNjE1YTM0MzZkNg== // Result: Error during WebSocket handshake: Sec-WebSocket-Accept mismatch
Looking more at this, I tried to reproduce the computed hash on a wiki, and it fails.
var hash = require('crypto') .createHash('SHA1') .update('x3JJHMbDL1EzLkh9GBhXDw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11') .digest('hex'); // Result : 1d29ab734b0c9585240069a6e4e3e91b61da1969 // Expected: 1d29ab734b0c9585240069a6e4e3e91b61da1969 var buf = new Buffer(hash).toString('base64'); // Result : MWQyOWFiNzM0YjBjOTU4NTI0MDA2OWE2ZTRlM2U5MWI2MWRhMTk2OQ== // Expected: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
As you can see, the SHA1 hash is correct, but the base64 encoding is not. Looking at this answer , it seems that I will do everything right. I tried the same process in PHP and I get the same result, so I clearly say "Doing It Wrong".
I am running Node.js v0.6.8.
Getting closer
Experimenting with more familiar PHP to me, and based on the behavior of printf in the shell, I came up with this working snippet:
$hash = sha1('x3JJHMbDL1EzLkh9GBhXDw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11'); $hashdec = ''; for ($i = 0; $i < strlen($hash); $i += 2) { $hashdec .= chr(hexdec(substr($hash, $i, 2))); }; echo base64_encode($hashdec); // Result : HSmrc0sMlYUkAGmm5OPpG2HaGWk= // Expected: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Then I tried to reproduce this in JavaScript, but to no avail.
var magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; var key = "4aRdFZG5uYrEUw8dsNLW6g=="; var magic_key = magic + key; var hash = require('crypto').createHash('sha1').update(magic_key).digest('hex'); var buf = new Buffer(hash.length / 2); for (var i = 0; i < hash.length; i += 2) { var token = hash.substr(i, 2); var int = parseInt(token.toString(16), 16); var chr = String.fromCharCode(int); buf.write(chr); } console.log(buf.toString('base64'));