Socket
Socket
Sign inDemoInstall

websocket-driver

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

websocket-driver - npm Package Compare versions

Comparing version 0.4.0 to 0.5.0

lib/websocket/driver/hybi/frame.js

4

CHANGELOG.md

@@ -0,1 +1,5 @@

### 0.5.0 / 2014-12-13
* Support protocol extensions via the websocket-extensions module
### 0.4.0 / 2014-11-08

@@ -2,0 +6,0 @@

4

examples/tcp_server.js
var net = require('net'),
websocket = require('../lib/websocket/driver');
websocket = require('../lib/websocket/driver'),
deflate = require('permessage-deflate');
var server = net.createServer(function(connection) {
var driver = websocket.server();
driver.addExtension(deflate);

@@ -7,0 +9,0 @@ driver.on('connect', function() {

@@ -0,1 +1,3 @@

'use strict';
// Protocol references:

@@ -38,3 +40,3 @@ //

return request.method === 'GET' &&
connection.toLowerCase().split(/\s*,\s*/).indexOf('upgrade') >= 0 &&
connection.toLowerCase().split(/ *, */).indexOf('upgrade') >= 0 &&
upgrade.toLowerCase() === 'websocket';

@@ -41,0 +43,0 @@ }

@@ -0,1 +1,3 @@

'use strict';
var Emitter = require('events').EventEmitter,

@@ -58,2 +60,6 @@ util = require('util'),

addExtension: function(extension) {
return false;
},
setHeader: function(name, value) {

@@ -60,0 +66,0 @@ if (this.readyState > 0) return false;

@@ -1,2 +0,5 @@

var url = require('url'),
'use strict';
var crypto = require('crypto'),
url = require('url'),
util = require('util'),

@@ -37,5 +40,3 @@ HttpParser = require('../http_parser'),

Client.generateKey = function() {
var buffer = new Buffer(16), i = buffer.length;
while (i--) buffer[i] = Math.floor(Math.random() * 256);
return buffer.toString('base64');
return crypto.randomBytes(16).toString('base64');
};

@@ -62,2 +63,5 @@

this._validateHandshake();
if (this.readyState === 3) return;
this._open();
this.parse(this._http.body);

@@ -67,2 +71,6 @@ },

_handshakeRequest: function() {
var extensions = this._extensions.generateOffer();
if (extensions)
this._headers.set('Sec-WebSocket-Extensions', extensions);
var start = 'GET ' + this._pathname + ' HTTP/1.1',

@@ -116,3 +124,7 @@ headers = [start, this._headers.toString(), ''];

this._open();
try {
this._extensions.activate(this.headers['sec-websocket-extensions']);
} catch (e) {
return this._failHandshake(e.message);
}
}

@@ -119,0 +131,0 @@ };

@@ -0,1 +1,3 @@

'use strict';
var Base = require('./base'),

@@ -2,0 +4,0 @@ util = require('util');

@@ -0,1 +1,3 @@

'use strict';
var Base = require('./base'),

@@ -69,9 +71,6 @@ Draft75 = require('./draft75'),

var headers = this._request.headers,
key1 = headers['sec-websocket-key1'],
value1 = numberFromKey(key1) / spacesInKey(key1),
key2 = headers['sec-websocket-key2'],
value2 = numberFromKey(key2) / spacesInKey(key2),
md5 = crypto.createHash('md5');

@@ -78,0 +77,0 @@

@@ -0,1 +1,3 @@

'use strict';
var Headers = function() {

@@ -2,0 +4,0 @@ this.clear();

@@ -1,10 +0,15 @@

var crypto = require('crypto'),
util = require('util'),
Base = require('./base'),
Reader = require('./hybi/stream_reader');
'use strict';
var crypto = require('crypto'),
util = require('util'),
Extensions = require('websocket-extensions'),
Base = require('./base'),
Frame = require('./hybi/frame'),
Message = require('./hybi/message'),
Reader = require('./hybi/stream_reader');
var Hybi = function(request, url, options) {
Base.apply(this, arguments);
this._reset();
this._extensions = new Extensions();
this._reader = new Reader();

@@ -18,3 +23,3 @@ this._stage = 0;

if (typeof this._protocols === 'string')
this._protocols = this._protocols.split(/\s*,\s*/);
this._protocols = this._protocols.split(/ *, */);

@@ -33,3 +38,3 @@ if (!this._request) return;

if (protos !== undefined) {
if (typeof protos === 'string') protos = protos.split(/\s*,\s*/);
if (typeof protos === 'string') protos = protos.split(/ *, */);
this.protocol = protos.filter(function(p) { return supported.indexOf(p) >= 0 })[0];

@@ -80,5 +85,5 @@ if (this.protocol) this._headers.set('Sec-WebSocket-Protocol', this.protocol);

OPCODE_CODES: [0, 1, 2, 8, 9, 10],
FRAGMENTED_OPCODES: [0, 1, 2],
OPENING_OPCODES: [1, 2],
OPCODE_CODES: [0, 1, 2, 8, 9, 10],
MESSAGE_OPCODES: [0, 1, 2],
OPENING_OPCODES: [1, 2],

@@ -106,2 +111,7 @@ TWO_POWERS: [0, 1, 2, 3, 4, 5, 6, 7].map(function(n) { return Math.pow(2, 8 * n) }),

addExtension: function(extension) {
this._extensions.add(extension);
return true;
},
parse: function(data) {

@@ -123,3 +133,3 @@ this._reader.put(data);

case 2:
buffer = this._reader.read(this._lengthSize);
buffer = this._reader.read(this._frame.lengthBytes);
if (buffer) this._parseExtendedLength(buffer);

@@ -131,3 +141,3 @@ break;

if (buffer) {
this._mask = buffer;
this._frame.maskingKey = buffer;
this._stage = 4;

@@ -138,3 +148,3 @@ }

case 4:
buffer = this._reader.read(this._length);
buffer = this._reader.read(this._frame.length);
if (buffer) {

@@ -152,57 +162,2 @@ this._emitFrame(buffer);

frame: function(data, type, code) {
if (this.readyState <= 0) return this._queue([data, type, code]);
if (this.readyState !== 1) return false;
if (data instanceof Array) data = new Buffer(data);
var isText = (typeof data === 'string'),
opcode = this.OPCODES[type || (isText ? 'text' : 'binary')],
buffer = isText ? new Buffer(data, 'utf8') : data,
insert = code ? 2 : 0,
length = buffer.length + insert,
header = (length <= 125) ? 2 : (length <= 65535 ? 4 : 10),
offset = header + (this._masking ? 4 : 0),
masked = this._masking ? this.MASK : 0,
frame = new Buffer(length + offset),
BYTE = this.BYTE,
mask, i;
frame[0] = this.FIN | opcode;
if (length <= 125) {
frame[1] = masked | length;
} else if (length <= 65535) {
frame[1] = masked | 126;
frame[2] = Math.floor(length / 256);
frame[3] = length & BYTE;
} else {
frame[1] = masked | 127;
frame[2] = Math.floor(length / Math.pow(2,56)) & BYTE;
frame[3] = Math.floor(length / Math.pow(2,48)) & BYTE;
frame[4] = Math.floor(length / Math.pow(2,40)) & BYTE;
frame[5] = Math.floor(length / Math.pow(2,32)) & BYTE;
frame[6] = Math.floor(length / Math.pow(2,24)) & BYTE;
frame[7] = Math.floor(length / Math.pow(2,16)) & BYTE;
frame[8] = Math.floor(length / Math.pow(2,8)) & BYTE;
frame[9] = length & BYTE;
}
if (code) {
frame[offset] = Math.floor(code / 256) & BYTE;
frame[offset+1] = code & BYTE;
}
buffer.copy(frame, offset + insert);
if (this._masking) {
mask = [Math.floor(Math.random() * 256), Math.floor(Math.random() * 256),
Math.floor(Math.random() * 256), Math.floor(Math.random() * 256)];
new Buffer(mask).copy(frame, header);
Hybi.mask(frame, mask, offset);
}
this._write(frame);
return true;
},
text: function(message) {

@@ -239,3 +194,100 @@ return this.frame(message, 'text');

frame: function(data, type, code) {
if (this.readyState <= 0) return this._queue([data, type, code]);
if (this.readyState !== 1) return false;
if (data instanceof Array) data = new Buffer(data);
var message = new Message(),
isText = (typeof data === 'string'),
payload, buffer;
message.rsv1 = message.rsv2 = message.rsv3 = false;
message.opcode = this.OPCODES[type || (isText ? 'text' : 'binary')];
payload = isText ? new Buffer(data, 'utf8') : data;
if (code) {
buffer = payload;
payload = new Buffer(2 + buffer.length);
payload[0] = ~~(code / 256) & this.BYTE;
payload[1] = code & this.BYTE;
buffer.copy(payload, 2);
}
message.data = payload;
var onMessageReady = function(message) {
var frame = new Frame();
frame.final = true;
frame.rsv1 = message.rsv1;
frame.rsv2 = message.rsv2;
frame.rsv3 = message.rsv3;
frame.opcode = message.opcode;
frame.masked = !!this._masking;
frame.length = message.data.length;
frame.payload = message.data;
if (frame.masked) frame.maskingKey = crypto.randomBytes(4);
this._sendFrame(frame);
};
if (this.MESSAGE_OPCODES.indexOf(message.opcode) >= 0)
this._extensions.processOutgoingMessage(message, function(error, message) {
if (error) return this._fail('extension_error', error.message);
onMessageReady.call(this, message);
}, this);
else
onMessageReady.call(this, message);
return true;
},
_sendFrame: function(frame) {
var length = frame.length,
header = (length <= 125) ? 2 : (length <= 65535 ? 4 : 10),
offset = header + (frame.masked ? 4 : 0),
buffer = new Buffer(offset + length),
BYTE = this.BYTE,
masked = frame.masked ? this.MASK : 0;
buffer[0] = (frame.final ? this.FIN : 0) |
(frame.rsv1 ? this.RSV1 : 0) |
(frame.rsv2 ? this.RSV2 : 0) |
(frame.rsv3 ? this.RSV3 : 0) |
frame.opcode;
if (length <= 125) {
buffer[1] = masked | length;
} else if (length <= 65535) {
buffer[1] = masked | 126;
buffer[2] = ~~(length / 256);
buffer[3] = length & BYTE;
} else {
buffer[1] = masked | 127;
buffer[2] = ~~(length / Math.pow(2, 56)) & BYTE;
buffer[3] = ~~(length / Math.pow(2, 48)) & BYTE;
buffer[4] = ~~(length / Math.pow(2, 40)) & BYTE;
buffer[5] = ~~(length / Math.pow(2, 32)) & BYTE;
buffer[6] = ~~(length / Math.pow(2, 24)) & BYTE;
buffer[7] = ~~(length / Math.pow(2, 16)) & BYTE;
buffer[8] = ~~(length / Math.pow(2, 8)) & BYTE;
buffer[9] = length & BYTE;
}
if (frame.masked) {
frame.maskingKey.copy(buffer, header);
Hybi.mask(frame.payload, frame.maskingKey).copy(buffer, offset);
} else {
frame.payload.copy(buffer, offset);
}
this._write(buffer);
},
_handshakeResponse: function() {
var extensions = this._extensions.generateResponse(this._request.headers['sec-websocket-extensions']);
if (extensions) this._headers.set('Sec-WebSocket-Extensions', extensions);
var start = 'HTTP/1.1 101 Switching Protocols',

@@ -249,5 +301,8 @@ headers = [start, this._headers.toString(), ''];

this.frame(reason, 'close', code);
delete this._frame;
delete this._message;
this.readyState = 3;
this._stage = 5;
this.emit('close', new Base.CloseEvent(code, reason));
this._extensions.close();
},

@@ -265,18 +320,23 @@

if (rsvs.filter(function(rsv) { return rsv }).length > 0)
var frame = this._frame = new Frame();
frame.final = (data & this.FIN) === this.FIN;
frame.rsv1 = rsvs[0];
frame.rsv2 = rsvs[1];
frame.rsv3 = rsvs[2];
frame.opcode = (data & this.OPCODE);
if (!this._extensions.validFrameRsv(frame))
return this._fail('protocol_error',
'One or more reserved bits are on: reserved1 = ' + (rsvs[0] ? 1 : 0) +
', reserved2 = ' + (rsvs[1] ? 1 : 0) +
', reserved3 = ' + (rsvs[2] ? 1 : 0));
'One or more reserved bits are on: reserved1 = ' + (frame.rsv1 ? 1 : 0) +
', reserved2 = ' + (frame.rsv2 ? 1 : 0) +
', reserved3 = ' + (frame.rsv3 ? 1 : 0));
this._final = (data & this.FIN) === this.FIN;
this._opcode = (data & this.OPCODE);
if (this.OPCODE_CODES.indexOf(frame.opcode) < 0)
return this._fail('protocol_error', 'Unrecognized frame opcode: ' + frame.opcode);
if (this.OPCODE_CODES.indexOf(this._opcode) < 0)
return this._fail('protocol_error', 'Unrecognized frame opcode: ' + this._opcode);
if (this.MESSAGE_OPCODES.indexOf(frame.opcode) < 0 && !frame.final)
return this._fail('protocol_error', 'Received fragmented control frame: opcode = ' + frame.opcode);
if (this.FRAGMENTED_OPCODES.indexOf(this._opcode) < 0 && !this._final)
return this._fail('protocol_error', 'Received fragmented control frame: opcode = ' + this._opcode);
if (this._mode && this.OPENING_OPCODES.indexOf(this._opcode) >= 0)
if (this._message && this.OPENING_OPCODES.indexOf(frame.opcode) >= 0)
return this._fail('protocol_error', 'Received new data frame but previous continuous frame is unfinished');

@@ -288,14 +348,16 @@

_parseLength: function(data) {
this._masked = (data & this.MASK) === this.MASK;
if (this._requireMasking && !this._masked)
var frame = this._frame;
frame.masked = (data & this.MASK) === this.MASK;
if (this._requireMasking && !frame.masked)
return this._fail('unacceptable', 'Received unmasked frame but masking is required');
this._length = (data & this.LENGTH);
frame.length = (data & this.LENGTH);
if (this._length >= 0 && this._length <= 125) {
if (frame.length >= 0 && frame.length <= 125) {
if (!this._checkFrameLength()) return;
this._stage = this._masked ? 3 : 4;
this._stage = frame.masked ? 3 : 4;
} else {
this._lengthSize = (this._length === 126 ? 2 : 8);
this._stage = 2;
frame.lengthBytes = (frame.length === 126 ? 2 : 8);
this._stage = 2;
}

@@ -305,14 +367,17 @@ },

_parseExtendedLength: function(buffer) {
this._length = this._getInteger(buffer);
var frame = this._frame;
frame.length = this._getInteger(buffer);
if (this.FRAGMENTED_OPCODES.indexOf(this._opcode) < 0 && this._length > 125)
return this._fail('protocol_error', 'Received control frame having too long payload: ' + this._length);
if (this.MESSAGE_OPCODES.indexOf(frame.opcode) < 0 && frame.length > 125)
return this._fail('protocol_error', 'Received control frame having too long payload: ' + frame.length);
if (!this._checkFrameLength()) return;
this._stage = this._masked ? 3 : 4;
this._stage = frame.masked ? 3 : 4;
},
_checkFrameLength: function() {
if (this.__blength + this._length > this._maxLength) {
var length = this._message ? this._message.length : 0;
if (length + this._frame.length > this._maxLength) {
this._fail('too_large', 'WebSocket frame length too large');

@@ -326,45 +391,28 @@ return false;

_emitFrame: function(buffer) {
var payload = Hybi.mask(buffer, this._mask),
isFinal = this._final,
opcode = this._opcode;
var frame = this._frame,
payload = frame.payload = Hybi.mask(buffer, frame.maskingKey),
opcode = frame.opcode,
message,
code, reason,
callbacks, callback;
this._final = this._opcode = this._length = this._lengthSize = this._masked = this._mask = null;
delete this._frame;
if (opcode === this.OPCODES.continuation) {
if (!this._mode) return this._fail('protocol_error', 'Received unexpected continuation frame');
this._buffer(payload);
if (isFinal) {
var message = this._concatBuffer();
if (this._mode === 'text') message = this._encode(message);
this._reset();
if (message === null)
this._fail('encoding_error', 'Could not decode a text frame as UTF-8');
else
this.emit('message', new Base.MessageEvent(message));
}
if (!this._message) return this._fail('protocol_error', 'Received unexpected continuation frame');
this._message.pushFrame(frame);
}
else if (opcode === this.OPCODES.text) {
if (isFinal) {
var message = this._encode(payload);
if (message === null)
this._fail('encoding_error', 'Could not decode a text frame as UTF-8');
else
this.emit('message', new Base.MessageEvent(message));
} else {
this._mode = 'text';
this._buffer(payload);
}
if (opcode === this.OPCODES.text || opcode === this.OPCODES.binary) {
this._message = new Message();
this._message.pushFrame(frame);
}
else if (opcode === this.OPCODES.binary) {
if (isFinal) {
this.emit('message', new Base.MessageEvent(payload));
} else {
this._mode = 'binary';
this._buffer(payload);
}
}
else if (opcode === this.OPCODES.close) {
var code = (payload.length >= 2) ? 256 * payload[0] + payload[1] : null,
reason = (payload.length > 2) ? this._encode(payload.slice(2)) : null;
if (frame.final && this.MESSAGE_OPCODES.indexOf(opcode) >= 0)
return this._emitMessage(this._message);
if (opcode === this.OPCODES.close) {
code = (payload.length >= 2) ? 256 * payload[0] + payload[1] : null;
reason = (payload.length > 2) ? this._encode(payload.slice(2)) : null;
if (!(payload.length === 0) &&

@@ -380,10 +428,12 @@ !(code !== null && code >= this.MIN_RESERVED_ERROR && code <= this.MAX_RESERVED_ERROR) &&

}
else if (opcode === this.OPCODES.ping) {
if (opcode === this.OPCODES.ping) {
this.frame(payload, 'pong');
}
else if (opcode === this.OPCODES.pong) {
var callbacks = this._pingCallbacks,
message = this._encode(payload),
callback = callbacks[message];
if (opcode === this.OPCODES.pong) {
callbacks = this._pingCallbacks;
message = this._encode(payload);
callback = callbacks[message];
delete callbacks[message];

@@ -394,22 +444,19 @@ if (callback) callback()

_buffer: function(fragment) {
this.__buffer.push(fragment);
this.__blength += fragment.length;
},
_emitMessage: function(message) {
var message = this._message;
message.read();
_concatBuffer: function() {
var buffer = new Buffer(this.__blength),
offset = 0;
delete this._message;
for (var i = 0, n = this.__buffer.length; i < n; i++) {
this.__buffer[i].copy(buffer, offset);
offset += this.__buffer[i].length;
}
return buffer;
},
this._extensions.processIncomingMessage(message, function(error, message) {
if (error) return this._fail('extension_error', error.message);
_reset: function() {
this._mode = null;
this.__buffer = [];
this.__blength = 0;
var payload = message.data;
if (message.opcode === this.OPCODES.text) payload = this._encode(payload);
if (payload === null)
return this._fail('encoding_error', 'Could not decode a text frame as UTF-8');
else
this.emit('message', new Base.MessageEvent(payload));
}, this);
},

@@ -416,0 +463,0 @@

@@ -0,1 +1,3 @@

'use strict';
var StreamReader = function() {

@@ -2,0 +4,0 @@ this._queue = [];

@@ -0,1 +1,3 @@

'use strict';
var Stream = require('stream').Stream,

@@ -2,0 +4,0 @@ url = require('url'),

@@ -0,1 +1,3 @@

'use strict';
var util = require('util'),

@@ -37,4 +39,4 @@ HttpParser = require('../http_parser'),

this._delegate.io = this.io;
this._open();
this._delegate.on('open', function() { self._open() });
this.EVENTS.forEach(function(event) {

@@ -59,3 +61,3 @@ this._delegate.on(event, function(e) { self.emit(event, e) });

['setHeader', 'start', 'state', 'frame', 'text', 'binary', 'ping', 'close'].forEach(function(method) {
['addExtension', 'setHeader', 'start', 'frame', 'text', 'binary', 'ping', 'close'].forEach(function(method) {
instance[method] = function() {

@@ -62,0 +64,0 @@ if (this._delegate) {

@@ -0,1 +1,3 @@

'use strict';
var HTTPParser = process.binding('http_parser').HTTPParser,

@@ -22,3 +24,8 @@ version = HTTPParser.RESPONSE ? 6 : 4;

this._parser.onHeaderValue = function(b, start, length) {
self.headers[current] = b.toString('utf8', start, start + length);
var value = b.toString('utf8', start, start + length);
if (self.headers.hasOwnProperty(current))
self.headers[current] += ', ' + value;
else
self.headers[current] = value;
};

@@ -31,7 +38,13 @@

var headers = info.headers;
var headers = info.headers, key, value;
if (!headers) return;
for (var i = 0, n = headers.length; i < n; i += 2)
self.headers[headers[i].toLowerCase()] = headers[i+1];
for (var i = 0, n = headers.length; i < n; i += 2) {
key = headers[i].toLowerCase();
value = headers[i+1];
if (self.headers.hasOwnProperty(key))
self.headers[key] += ', ' + value;
else
self.headers[key] = value;
}

@@ -38,0 +51,0 @@ self._complete = true;

@@ -0,1 +1,3 @@

'use strict';
/**

@@ -2,0 +4,0 @@

@@ -8,6 +8,7 @@ { "name" : "websocket-driver"

, "version" : "0.4.0"
, "engines" : {"node": ">=0.4.0"}
, "version" : "0.5.0"
, "engines" : {"node": ">=0.6.0"}
, "main" : "./lib/websocket/driver"
, "devDependencies" : {"jstest": ""}
, "dependencies" : {"websocket-extensions": ">=0.1.0"}
, "devDependencies" : {"jstest": "", "permessage-deflate": ""}

@@ -14,0 +15,0 @@ , "scripts" : {"test": "jstest spec/runner.js"}

@@ -17,2 +17,5 @@ # websocket-driver [![Build Status](https://travis-ci.org/faye/websocket-driver-node.svg)](https://travis-ci.org/faye/websocket-driver-node)

* Negotiate subprotocol selection based on `Sec-WebSocket-Protocol`
* Negotiate and use extensions via the
[websocket-extensions](https://github.com/faye/websocket-extensions-node)
module
* Buffer sent messages until the handshake process is finished

@@ -282,2 +285,9 @@ * Deal with proxies that defer delivery of the draft-76 handshake body

#### `driver.addExtension(extension)`
Registers a protocol extension whose operation will be negotiated via the
`Sec-WebSocket-Extensions` header. `extension` is any extension compatible with
the [websocket-extensions](https://github.com/faye/websocket-extensions-node)
framework.
#### `driver.setHeader(name, value)`

@@ -284,0 +294,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc