The functions included are for generating the base64-encode of a sha1 hash of the input string sent in as an argument.
This is the mechanism for processing something to be used as the header for either the Sec-WebSocket-Key
request header or Sec-WebSocket-Accept
response header in the websocket opening handshake.
To generate the Sec-WebSocket-Key
, you would use generateRandomBase64String
; for the matching Sec-WebSocket-Accept
header value, you would use base64EncodeOfSha1Hash(keyConcatenatedWithMagicString)
.
The client would send the initial request with the Sec-WebSocket-Key
; the server, on receipt, would after having done some validation of the request, send a response containing a Sec-WebSocket-Accept
header with the value as described above.
Finally, once the client received the response, it would do the same thing the server did with the key the client sent over -- i.e., calculate the base64EncodeOfSha1Hash
of the key concatenated with the magic string, and compare that to the received Sec-WebSocket-Accept
header value.
As an example, a Sec-WebSocket-Key
of 'xqBt3ImNzJbYqRINxEFlkg=='
should generate the Sec-WebSocket-Accept
header value of 'K7DJLdLooIwIG/MOpvWFB3y3FE8='
.
In a browser, 'K7DJLdLooIwIG/MOpvWFB3y3FE8=' === await generateWebSocketAcceptHeader('xqBt3ImNzJbYqRINxEFlkg==')
would be true
; in Node, you would instead expect 'K7DJLdLooIwIG/MOpvWFB3y3FE8=' === generateWebSocketAcceptHeader('xqBt3ImNzJbYqRINxEFlkg==')
to be true.
// client
/*
Note: if you're opening a websocket from the browser, you wouldn't do any of
this. This is only for scenarios when you can write requests manually, which
the browser prevents.
*/
const key = generateRandomBase64String();
// this is just a less-error-prone way of making the request, in my opinion
// (no worrying about newlines, forgetting the final carriage return, etc.)
const rawRequest = [
`GET / HTTP/1.1`,
`Connection: Upgrade`,
`Upgrade: websocket`,
`Sec-WebSocket-Version: 13`,
`Sec-WebSocket-Key: ${key}`, // <-- voila!
`\r\n` // <- don't forget this! without it, the request never completes.
].join('\n');
somehowSendRequest(rawRequest);
// server (node)
// this string is just part of the RFC protocol spec.
const MAGIC_SOCKET_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
// on receiving the request...
// this is a bit handwavey:
// we are assuming SOMETHING gives us a way to get the header neatly
const requestObject = magicallyParseRequest(receivedFromClientRequest);
const key = requestObject['sec-websocket-key'];
// we need to process the key concatenated with the magic string
// `${key}${MAGIC_SOCKET_STRING}` is how we concatenate it.
const acceptHeaderValue = base64EncodeOfSha1Hash(`${key}${MAGIC_SOCKET_STRING}`);
const rawResponse = [
`HTTP/1.1 101 Websocket upgrade`,
`Connection: upgrade`,
`Upgrade: websocket`,
`Sec-WebSocket-Accept: ${acceptHeaderValue}`,
`\r\n` // <- don't forget this...!
].join('\n');
somehowSendResponse(rawResponse);