92 lines
2.7 KiB
JavaScript
92 lines
2.7 KiB
JavaScript
var crypto = require('crypto');
|
|
|
|
var Handshake = function(uri, protocols) {
|
|
this._uri = uri;
|
|
this._protocols = protocols;
|
|
|
|
var buffer = new Buffer(16), i = 16;
|
|
while (i--) buffer[i] = Math.floor(Math.random() * 256);
|
|
this._key = buffer.toString('base64');
|
|
|
|
var SHA1 = crypto.createHash('sha1');
|
|
SHA1.update(this._key + Handshake.GUID);
|
|
this._accept = SHA1.digest('base64');
|
|
|
|
var HTTPParser = process.binding('http_parser').HTTPParser,
|
|
parser = new HTTPParser(HTTPParser.RESPONSE || 'response'),
|
|
current = null,
|
|
self = this;
|
|
|
|
this._nodeVersion = HTTPParser.RESPONSE ? 6 : 4;
|
|
this._complete = false;
|
|
this._headers = {};
|
|
this._parser = parser;
|
|
|
|
parser.onHeaderField = function(b, start, length) {
|
|
current = b.toString('utf8', start, start + length);
|
|
};
|
|
parser.onHeaderValue = function(b, start, length) {
|
|
self._headers[current] = b.toString('utf8', start, start + length);
|
|
};
|
|
parser.onHeadersComplete = function(info) {
|
|
self._status = info.statusCode;
|
|
var headers = info.headers;
|
|
if (!headers) return;
|
|
for (var i = 0, n = headers.length; i < n; i += 2)
|
|
self._headers[headers[i]] = headers[i+1];
|
|
};
|
|
parser.onMessageComplete = function() {
|
|
self._complete = true;
|
|
};
|
|
};
|
|
|
|
Handshake.GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
|
|
|
Handshake.prototype.requestData = function() {
|
|
var u = this._uri;
|
|
|
|
var headers = [
|
|
'GET ' + (u.pathname || '/') + (u.search || '') + ' HTTP/1.1',
|
|
'Host: ' + u.hostname + (u.port ? ':' + u.port : ''),
|
|
'Upgrade: websocket',
|
|
'Connection: Upgrade',
|
|
'Sec-WebSocket-Key: ' + this._key,
|
|
'Sec-WebSocket-Version: 13'
|
|
];
|
|
|
|
if (this._protocols)
|
|
headers.push('Sec-WebSocket-Protocol: ' + this._protocols.join(', '));
|
|
|
|
return new Buffer(headers.concat('','').join('\r\n'), 'utf8');
|
|
};
|
|
|
|
Handshake.prototype.parse = function(data) {
|
|
var consumed = this._parser.execute(data, 0, data.length),
|
|
offset = (this._nodeVersion < 6) ? 1 : 0;
|
|
|
|
return (consumed === data.length) ? [] : data.slice(consumed + offset);
|
|
};
|
|
|
|
Handshake.prototype.isComplete = function() {
|
|
return this._complete;
|
|
};
|
|
|
|
Handshake.prototype.isValid = function() {
|
|
if (this._status !== 101) return false;
|
|
|
|
var upgrade = this._headers.Upgrade,
|
|
connection = this._headers.Connection,
|
|
protocol = this._headers['Sec-WebSocket-Protocol'];
|
|
|
|
this.protocol = this._protocols && this._protocols.indexOf(protocol) >= 0
|
|
? protocol
|
|
: null;
|
|
|
|
return upgrade && /^websocket$/i.test(upgrade) &&
|
|
connection && connection.split(/\s*,\s*/).indexOf('Upgrade') >= 0 &&
|
|
((!this._protocols && !protocol) || this.protocol) &&
|
|
this._headers['Sec-WebSocket-Accept'] === this._accept;
|
|
};
|
|
|
|
module.exports = Handshake;
|