🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Book a DemoInstallSign in
Socket

ws

Package Overview
Dependencies
Maintainers
1
Versions
172
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ws - npm Package Compare versions

Comparing version

to
0.3.6

test/Sender.test.js

21

History.md

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

v0.3.2 - Dec 11th 2011
v0.3.6 - Dec 15th 2011
======================
* Compile fix for Linux
* Added option to let WebSocket.Server use an already existing http server [mmalecki]
* Migrating various option structures to use options.js module [einaros]
* Added a few more tests, options and handshake verifications to ensure that faulty connections are dealt with [einaros]
* Code cleanups in Sender and Receiver, to ensure even faster parsing [einaros]
v0.3.5 - Dec 13th 2011
======================
* Optimized Sender.js, Receiver.js and bufferutil.cc:
* Apply loop-unrolling-like small block copies rather than use node.js Buffer#copy() (which is slow).
* Mask blocks of data using combination of 32bit xor and loop-unrolling, instead of single bytes.
* Keep pre-made send buffer for small transfers.
* Leak fixes and code cleanups.
v0.3.3 - Dec 12th 2011
======================
* Compile fix for Linux.
* Rewrote parts of WebSocket.js, to avoid try/catch and thus avoid optimizer bailouts.

@@ -6,0 +23,0 @@

106

lib/Receiver.js

@@ -18,21 +18,4 @@ /*!

*/
var isNodeV4 = /^v0\.4/.test(process.version);
var readUInt16BE = !isNodeV4
? Buffer.prototype.readUInt16BE
: function(start) {
var n = 0;
for (var i = start; i < start + 2; ++i) {
n = (i == 0) ? this[i] : (n * 256) + this[i];
}
return n;
};
var readUInt32BE = !isNodeV4
? Buffer.prototype.readUInt32BE
: function(start) {
var n = 0;
for (var i = start; i < start + 4; ++i) {
n = (i == 0) ? this[i] : (n * 256) + this[i];
}
return n;
};

@@ -50,3 +33,3 @@ /**

};
this.overflow = null;
this.overflow = [];
this.bufferPool = new BufferPool(1024, function(db, length) {

@@ -74,3 +57,3 @@ return db.used + length;

self.expect(2, function(data) {
opcodes['1'].getData(readUInt16BE.call(data, 0, true));
opcodes['1'].getData(readUInt16BE.call(data, 0));
});

@@ -80,7 +63,7 @@ }

self.expect(8, function(data) {
if (readUInt32BE.call(data, 0, true) != 0) {
if (readUInt32BE.call(data, 0) != 0) {
self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
return;
}
opcodes['1'].getData(readUInt32BE.call(data, 4, true));
opcodes['1'].getData(readUInt32BE.call(data, 4));
});

@@ -129,3 +112,3 @@ }

self.expect(2, function(data) {
opcodes['2'].getData(readUInt16BE.call(data, 0, true));
opcodes['2'].getData(readUInt16BE.call(data, 0));
});

@@ -135,3 +118,3 @@ }

self.expect(8, function(data) {
if (readUInt32BE.call(data, 0, true) != 0) {
if (readUInt32BE.call(data, 0) != 0) {
self.error('packets with length spanning more than 32 bit is currently not supported', 1008);

@@ -208,3 +191,3 @@ return;

}
var code = data && data.length > 1 ? readUInt16BE.call(data, 0, true) : 1000;
var code = data && data.length > 1 ? readUInt16BE.call(data, 0) : 1000;
if (!ErrorCodes.isValidErrorCode(code)) {

@@ -319,8 +302,6 @@ self.error('invalid error code', 1002);

if (this.expectBuffer == null) {
this.addToOverflow(data);
this.overflow.push(data);
return;
}
var toRead = Math.min(data.length, this.expectBuffer.length - this.expectOffset);
// although ugly, this is a much faster approach for small buffers,
// than calling copy()
var dest = this.expectBuffer;

@@ -330,2 +311,10 @@ var offset = this.expectOffset;

default: data.copy(dest, offset, 0, toRead); break;
case 16: dest[offset+15] = data[15];
case 15: dest[offset+14] = data[14];
case 14: dest[offset+13] = data[13];
case 13: dest[offset+12] = data[12];
case 12: dest[offset+11] = data[11];
case 11: dest[offset+10] = data[10];
case 10: dest[offset+9] = data[9];
case 9: dest[offset+8] = data[8];
case 8: dest[offset+7] = data[7];

@@ -342,3 +331,3 @@ case 7: dest[offset+6] = data[6];

if (toRead < data.length) {
this.addToOverflow(data.slice(toRead, data.length));
this.overflow.push(data.slice(toRead, data.length));
}

@@ -354,31 +343,2 @@ if (this.expectOffset == this.expectBuffer.length) {

/**
* Adds a piece of data to the overflow.
*
* @api private
*/
Receiver.prototype.addToOverflow = function(data) {
if (this.overflow == null) this.overflow = data;
else {
var prevOverflow = this.overflow;
var dataLength = data.length;
this.overflow = this.allocateFromPool(this.overflow.length + dataLength);
prevOverflow.copy(this.overflow, 0);
var dest = this.overflow;
var offset = prevOverflow.length;
switch (dataLength) {
default: data.copy(dest, offset, 0, dataLength); break;
case 8: dest[offset+7] = data[7];
case 7: dest[offset+6] = data[6];
case 6: dest[offset+5] = data[5];
case 5: dest[offset+4] = data[4];
case 4: dest[offset+3] = data[3];
case 3: dest[offset+2] = data[2];
case 2: dest[offset+1] = data[1];
case 1: dest[offset] = data[0];
}
}
}
/**
* Waits for a certain amount of bytes to be available, then fires a callback.

@@ -397,6 +357,9 @@ *

this.expectHandler = handler;
if (this.overflow != null) {
var toOverflow = this.overflow;
this.overflow = null;
this.add(toOverflow);
var toRead = length;
while (toRead > 0 && this.overflow.length > 0) {
var buf = this.overflow.pop();
if (toRead < buf.length) this.overflow.push(buf.slice(toRead))
var read = Math.min(buf.length, toRead);
this.add(buf.slice(0, read));
toRead -= read;
}

@@ -493,3 +456,3 @@ }

this.expectHandler = null;
this.overflow = null;
this.overflow = [];
this.currentMessage = [];

@@ -536,2 +499,19 @@ }

/**
* Buffer utilities
*
*/
function readUInt16BE(start) {
return (this[start]<<8) +
this[start+1];
}
function readUInt32BE(start) {
return (this[start]<<24) +
(this[start+1]<<16) +
(this[start+2]<<8) +
this[start+3];
}
module.exports = Receiver;

@@ -10,2 +10,3 @@ /*!

, EventEmitter = events.EventEmitter
, Options = require('options')
, ErrorCodes = require('./ErrorCodes')

@@ -15,27 +16,13 @@ , bufferUtil = new require('./BufferUtil').BufferUtil;

/**
* Node version 0.4 and 0.6 compatibility
*/
var isNodeV4 = /^v0\.4/.test(process.version);
var writeUInt16BE = !isNodeV4
? Buffer.prototype.writeUInt16BE
: function(value, offset) {
this[offset] = value >>> 8;
this[offset + 1] = value % 256;
};
var writeUInt32BE = !isNodeV4
? Buffer.prototype.writeUInt32BE
: function(value, offset) {
for (var i = offset + 3; i >= offset; --i) {
this[i] = value & 0xff;
value >>>= 8;
}
};
/**
* HyBi Sender implementation
*/
function Sender (socket, config) {
this._sendCacheSize = (config && config.SendBufferCacheSize) ? config.SendBufferCacheSize : 65536;
this._sendCache = new Buffer(this._sendCacheSize);
function Sender (socket, options) {
options = new Options({
sendBufferCacheSize: 65536
}).merge(options);
if (options.value.sendBufferCacheSize > 0) {
this._sendCacheSize = options.value.sendBufferCacheSize;
this._sendCache = new Buffer(this._sendCacheSize);
}
this._socket = socket;

@@ -64,3 +51,3 @@ this.firstFragment = true;

var dataBuffer = new Buffer(2 + (data ? Buffer.byteLength(data) : 0));
writeUInt16BE.call(dataBuffer, code, 0, true);
writeUInt16BE.call(dataBuffer, code, 0);
if (dataBuffer.length > 2) dataBuffer.write(data, 2);

@@ -162,7 +149,7 @@ var buf = this.frameData(0x8, dataBuffer, true, mask);

case 126:
writeUInt16BE.call(outputBuffer, dataLength, 2, true);
writeUInt16BE.call(outputBuffer, dataLength, 2);
break;
case 127:
writeUInt32BE.call(outputBuffer, 0, 2, true);
writeUInt32BE.call(outputBuffer, dataLength, 6, true);
writeUInt32BE.call(outputBuffer, 0, 2);
writeUInt32BE.call(outputBuffer, dataLength, 6);
}

@@ -188,2 +175,14 @@ if (maskData) {

function writeUInt16BE(value, offset) {
this[offset] = (value & 0xff00)>>8;
this[offset+1] = value & 0xff;
}
function writeUInt32BE(value, offset) {
this[offset] = (value & 0xff000000)>>24;
this[offset+1] = (value & 0xff0000)>>16;
this[offset+2] = (value & 0xff00)>>8;
this[offset+3] = value & 0xff;
}
function getArrayBuffer(array) {

@@ -190,0 +189,0 @@ var l = array.byteLength

@@ -13,2 +13,3 @@ /*!

, fs = require('fs')
, Options = require('options')
, Sender = require('./Sender')

@@ -34,2 +35,12 @@ , Receiver = require('./Receiver');

options = new Options({
protocolVersion: protocolVersion,
protocol: null
}).merge(options);
Object.defineProperty(this, 'protocol', {
value: options.value.protocol
});
Object.defineProperty(this, 'protocolVersion', {
value: options.value.protocolVersion
});
this._state = 'connecting';

@@ -52,10 +63,12 @@ this._isServer = true;

options = options || {};
options.origin = options.origin || null;
options.protocolVersion = options.protocolVersion || protocolVersion;
if (options.protocolVersion != 8 && options.protocolVersion != 13) {
options = new Options({
origin: null,
protocolVersion: protocolVersion,
protocol: null
}).merge(options);
if (options.value.protocolVersion != 8 && options.value.protocolVersion != 13) {
throw new Error('unsupported protocol version');
}
var key = new Buffer(options.protocolVersion + '-' + Date.now()).toString('base64');
var key = new Buffer(options.value.protocolVersion + '-' + Date.now()).toString('base64');
var shasum = crypto.createHash('sha1');

@@ -82,6 +95,9 @@ shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11');

'Upgrade': 'websocket',
'Sec-WebSocket-Version': options.protocolVersion,
'Sec-WebSocket-Version': options.value.protocolVersion,
'Sec-WebSocket-Key': key
}
};
if (options.value.protocol) {
requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol;
}
if (isNodeV4) {

@@ -92,5 +108,5 @@ requestOptions.path = (serverUrl.pathname || '/') + (serverUrl.search || '');

else requestOptions.path = serverUrl.path || '/';
if (options.origin) {
if (options.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.origin;
else requestOptions.headers['Origin'] = options.origin;
if (options.value.origin) {
if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin;
else requestOptions.headers['Origin'] = options.value.origin;
}

@@ -97,0 +113,0 @@

@@ -12,2 +12,3 @@ /*!

, url = require('url')
, Options = require('options')
, WebSocket = require('./WebSocket');

@@ -20,9 +21,23 @@

function WebSocketServer(options, callback) {
if (typeof options !== 'object' || typeof options.port == 'undefined') {
throw new Error('port must be provided');
options = new Options({
host: '127.0.0.1',
port: null,
server: null,
verifyOrigin: null
}).merge(options);
if (!options.value.port && !options.value.server) {
throw new TypeError('`port` or a `server` must be provided');
}
this._server = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('okay');
});
if (!options.value.server) {
this._server = http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('okay');
});
this._server.listen(options.value.port, options.value.host || '127.0.0.1', callback);
}
else {
this._server = options.value.server;
}
this._clients = [];

@@ -33,13 +48,35 @@ var self = this;

});
this._server.on('upgrade', function(req, socket, upgradeHead) {
if (typeof req.headers.upgrade === 'undefined' || req.headers.upgrade.toLowerCase() !== 'websocket') {
socket.end();
self.emit('error', 'client connection with invalid headers');
abortConnection(socket, 400, 'Bad Request');
return;
}
// verify key presence
if (!req.headers['sec-websocket-key']) {
socket.end();
self.emit('error', 'websocket key is missing');
abortConnection(socket, 400, 'Bad Request');
return;
}
// verify version
var version = parseInt(req.headers['sec-websocket-version']);
if ([8, 13].indexOf(version) === -1) {
abortConnection(socket, 400, 'Bad Request');
return;
}
// verify origin
var origin = version < 13 ?
req.headers['sec-websocket-origin'] :
req.headers['origin'];
if (typeof options.value.verifyOrigin == 'function') {
if (!options.value.verifyOrigin(origin)) {
abortConnection(socket, 401, 'Unauthorized');
return;
}
}
var protocol = req.headers['sec-websocket-protocol'];
// calc key

@@ -57,3 +94,5 @@ var key = req.headers['sec-websocket-key'];

];
if (typeof protocol != 'undefined') {
headers['Sec-WebSocket-Protocol'] = protocol;
}
try {

@@ -64,3 +103,2 @@ socket.write(headers.concat('', '').join('\r\n'));

try { socket.end(); } catch (_) {}
self.emit('error', 'socket error: ' + e);
return;

@@ -70,5 +108,6 @@ }

socket.setNoDelay(true);
self._socket = socket;
var client = new WebSocket(Array.prototype.slice.call(arguments, 0));
var client = new WebSocket(Array.prototype.slice.call(arguments, 0), {
protocolVersion: version,
protocol: protocol
});
self._clients.push(client);

@@ -85,5 +124,2 @@ client.on('open', function() {

});
this._server.listen(options.port, '127.0.0.1', function() {
if (typeof callback == 'function') callback();
});
this.__defineGetter__('clients', function() {

@@ -118,7 +154,5 @@ return self._clients;

this._server.close();
this._socket.end();
}
finally {
this._server = null;
this._socket = null;
}

@@ -129,1 +163,18 @@ if (error) throw error;

module.exports = WebSocketServer;
/**
* Entirely private apis,
* which may or may not be bound to a sepcific WebSocket instance.
*/
function abortConnection(socket, code, name) {
try {
var response = [
'HTTP/1.1 ' + code + ' ' + name,
'Content-type: text/html'
];
socket.write(response.concat('', '').join('\r\n'));
socket.end();
}
catch (e) {}
}

@@ -5,3 +5,3 @@ {

"description": "simple and very fast websocket protocol client for node.js",
"version": "0.3.5",
"version": "0.3.6",
"repository": {

@@ -11,2 +11,6 @@ "type": "git",

},
"contributors": [
{ "name": "Einar Otto Stangvik", "email": "einaros@gmail.com" },
{ "name": "Maciej Małecki", "email": "maciej.malecki@notimplemented.org" }
],
"bin": {

@@ -23,3 +27,4 @@ "wscat": "./bin/wscat"

"dependencies": {
"commander": "0.5.0"
"commander": "0.5.0",
"options": "latest"
},

@@ -26,0 +31,0 @@ "devDependencies": {

var assert = require('assert')
, http = require('http')
, WebSocket = require('../')

@@ -52,2 +53,7 @@ , WebSocketServer = WebSocket.Server

})
it('uses passed server object', function () {
var srv = http.createServer()
, wss = new WebSocketServer({server: srv});
wss._server.should.equal(srv);
});
})

@@ -83,2 +89,135 @@

it('works with a http server', function (done) {
var srv = http.createServer();
srv.listen(++port, function () {
var wss = new WebSocketServer({server: srv});
var ws = new WebSocket('ws://localhost:' + port);
wss.on('connection', function(client) {
wss.close();
done();
});
});
})
it('does not accept connections with no sec-websocket-key', function(done) {
var wss = new WebSocketServer({port: ++port}, function() {
var options = {
port: port,
host: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket'
}
};
var req = http.request(options);
req.end();
req.on('response', function(res) {
res.statusCode.should.eql(400);
wss.close();
done();
});
});
wss.on('error', function() {});
})
it('does not accept connections with no sec-websocket-version', function(done) {
var wss = new WebSocketServer({port: ++port}, function() {
var options = {
port: port,
host: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ=='
}
};
var req = http.request(options);
req.end();
req.on('response', function(res) {
res.statusCode.should.eql(400);
wss.close();
done();
});
});
wss.on('error', function() {});
})
it('does not accept connections with invalid sec-websocket-version', function(done) {
var wss = new WebSocketServer({port: ++port}, function() {
var options = {
port: port,
host: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
'Sec-WebSocket-Version': 12
}
};
var req = http.request(options);
req.end();
req.on('response', function(res) {
res.statusCode.should.eql(400);
wss.close();
done();
});
});
wss.on('error', function() {});
})
it('does not accept connections with invalid sec-websocket-origin (8)', function(done) {
var wss = new WebSocketServer({port: ++port, verifyOrigin: function(o) {
o.should.eql('http://foobar.com');
return false;
}}, function() {
var options = {
port: port,
host: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
'Sec-WebSocket-Version': 8,
'Sec-WebSocket-Origin': 'http://foobar.com'
}
};
var req = http.request(options);
req.end();
req.on('response', function(res) {
res.statusCode.should.eql(401);
wss.close();
done();
});
});
wss.on('error', function() {});
})
it('does not accept connections with invalid origin', function(done) {
var wss = new WebSocketServer({port: ++port, verifyOrigin: function(o) {
o.should.eql('http://foobar.com');
return false;
}}, function() {
var options = {
port: port,
host: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
'Sec-WebSocket-Version': 13,
'Origin': 'http://foobar.com'
}
};
var req = http.request(options);
req.end();
req.on('response', function(res) {
res.statusCode.should.eql(401);
wss.close();
done();
});
});
wss.on('error', function() {});
})
it('can send data', function(done) {

@@ -98,2 +237,24 @@ var wss = new WebSocketServer({port: ++port}, function() {

it('tracks the client protocol', function(done) {
var wss = new WebSocketServer({port: ++port}, function() {
var ws = new WebSocket('ws://localhost:' + port, {protocol: 'hi'});
});
wss.on('connection', function(client) {
client.protocol.should.eql('hi');
wss.close();
done();
});
})
it('tracks the client protocolVersion', function(done) {
var wss = new WebSocketServer({port: ++port}, function() {
var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8});
});
wss.on('connection', function(client) {
client.protocolVersion.should.eql(8);
wss.close();
done();
});
})
describe('#clients', function() {

@@ -100,0 +261,0 @@ it('returns a list of connected clients', function(done) {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet