Socket
Socket
Sign inDemoInstall

ws

Package Overview
Dependencies
Maintainers
1
Versions
169
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 0.4.7 to 0.4.8

bench/speed.js

6

History.md

@@ -0,1 +1,7 @@

v0.4.8 - Feb 29th 2012
=====================
* Allow verifyClient to run asynchronously [karlsequin]
* Various bugfixes and cleanups. [einaros]
v0.4.7 - Feb 21st 2012

@@ -2,0 +8,0 @@ =====================

31

lib/Sender.js

@@ -19,9 +19,3 @@ /*!

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);
}
options = new Options({}).merge(options);
this._socket = socket;

@@ -100,3 +94,4 @@ this.firstFragment = true;

Sender.prototype.frameAndSend = function(opcode, data, finalFragment, maskData, cb) {
var dontModifyData = true;
var canModifyData = false;
if (!data) {

@@ -112,9 +107,12 @@ try {

}
else if (!Buffer.isBuffer(data)) {
dontModifyData = false;
if (!Buffer.isBuffer(data)) {
canModifyData = true;
data = (data && typeof data.buffer !== 'undefined') ? getArrayBuffer(data.buffer) : new Buffer(data);
}
var dataLength = data.length
, dataOffset = maskData ? 6 : 2
, secondByte = dataLength;
if (dataLength >= 65536) {

@@ -128,7 +126,8 @@ dataOffset += 8;

}
var totalLength = (maskData && dontModifyData) ? dataLength + dataOffset : dataOffset;
var outputBuffer = (this._sendCache && totalLength <= this._sendCacheSize)
? ((totalLength == this._sendCacheSize ? this._sendCache : this._sendCache.slice(0, totalLength)))
: new Buffer(totalLength);
var totalLength = (maskData && !canModifyData) ? dataLength + dataOffset : dataOffset;
var outputBuffer = new Buffer(totalLength);
outputBuffer[0] = finalFragment ? opcode | 0x80 : opcode;
switch (secondByte) {

@@ -142,4 +141,6 @@ case 126:

}
var sendsDone = 0;
var cbCaller = function() { if (++sendsDone == 2 && typeof cb == 'function') cb(null); }
if (maskData) {

@@ -152,3 +153,3 @@ outputBuffer[1] = secondByte | 0x80;

outputBuffer[dataOffset - 1] = mask[3];
if (dontModifyData) {
if (!canModifyData) {
bufferUtil.mask(data, mask, outputBuffer, dataOffset, dataLength);

@@ -155,0 +156,0 @@ try {

@@ -99,3 +99,3 @@ /*!

WebSocket.prototype.close = function(code, data) {
if (this.readyState == WebSocket.CLOSING) return;
if (this.readyState == WebSocket.CLOSING || this.readyState == WebSocket.CLOSED) return;
if (this.readyState == WebSocket.CONNECTING) {

@@ -105,3 +105,2 @@ this._readyState = WebSocket.CLOSED;

}
if (this.readyState != WebSocket.OPEN) throw new Error('not opened');
try {

@@ -136,7 +135,11 @@ this._readyState = WebSocket.CLOSING;

* @param {Object} Members - mask: boolean, binary: boolean
* @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open
* @api public
*/
WebSocket.prototype.ping = function(data, options) {
if (this.readyState != WebSocket.OPEN) throw new Error('not opened');
WebSocket.prototype.ping = function(data, options, dontFailWhenClosed) {
if (this.readyState != WebSocket.OPEN) {
if (dontFailWhenClosed === true) return;
throw new Error('not opened');
}
options = options || {};

@@ -152,7 +155,11 @@ if (typeof options.mask == 'undefined') options.mask = !this._isServer;

* @param {Object} Members - mask: boolean, binary: boolean
* @param {boolean} dontFailWhenClosed indicates whether or not to throw if the connection isnt open
* @api public
*/
WebSocket.prototype.pong = function(data, options) {
if (this.readyState != WebSocket.OPEN) throw new Error('not opened');
WebSocket.prototype.pong = function(data, options, dontFailWhenClosed) {
if (this.readyState != WebSocket.OPEN) {
if (dontFailWhenClosed === true) return;
throw new Error('not opened');
}
options = options || {};

@@ -543,3 +550,3 @@ if (typeof options.mask == 'undefined') options.mask = !this._isServer;

flags = flags || {};
self.pong(data, {mask: !self._isServer, binary: flags.binary === true});
self.pong(data, {mask: !self._isServer, binary: flags.binary === true}, true);
self.emit('ping', data, flags);

@@ -546,0 +553,0 @@ });

@@ -175,2 +175,4 @@ /*!

req.headers['origin'];
var args = [req, socket, upgradeHead, version, cb];
if (typeof this.options.verifyClient == 'function') {

@@ -182,3 +184,11 @@ var info = {

};
if (!this.options.verifyClient(info)) {
if (this.options.verifyClient.length == 2) {
var self = this;
this.options.verifyClient(info, function(result) {
if (!result) abortConnection(socket, 401, 'Unauthorized')
else completeUpgrade.apply(self, args);
});
return;
}
else if (!this.options.verifyClient(info)) {
abortConnection(socket, 401, 'Unauthorized');

@@ -189,4 +199,8 @@ return;

var protocol = req.headers['sec-websocket-protocol'];
completeUpgrade.apply(this, args);
}
function completeUpgrade(req, socket, upgradeHead, version, cb) {
var protocol = req.headers['sec-websocket-protocol'];
// calc key

@@ -243,2 +257,92 @@ var key = req.headers['sec-websocket-key'];

// setup handshake completion to run after client has been verified
var self = this;
var onClientVerified = function() {
var protocol = req.headers['sec-websocket-protocol'];
// handshake completion code to run once nonce has been successfully retrieved
var completeHandshake = function(nonce, rest) {
// calculate key
var k1 = req.headers['sec-websocket-key1']
, k2 = req.headers['sec-websocket-key2']
, md5 = crypto.createHash('md5');
[k1, k2].forEach(function (k) {
var n = parseInt(k.replace(/[^\d]/g, ''))
, spaces = k.replace(/[^ ]/g, '').length;
if (spaces === 0 || n % spaces !== 0){
abortConnection(socket, 400, 'Bad Request');
return;
}
n /= spaces;
md5.update(String.fromCharCode(
n >> 24 & 0xFF,
n >> 16 & 0xFF,
n >> 8 & 0xFF,
n & 0xFF));
});
md5.update(nonce.toString('binary'));
var headers = [
'HTTP/1.1 101 Switching Protocols'
, 'Upgrade: WebSocket'
, 'Connection: Upgrade'
, 'Sec-WebSocket-Location: ' + location
];
if (typeof protocol != 'undefined') headers.push('Sec-WebSocket-Protocol: ' + protocol);
if (typeof origin != 'undefined') headers.push('Sec-WebSocket-Origin: ' + origin);
socket.setTimeout(0);
socket.setNoDelay(true);
try {
socket.write(headers.concat('', '').join('\r\n'));
socket.write(md5.digest('binary'), 'binary');
}
catch (e) {
try { socket.end(); } catch (_) {}
return;
}
var client = new WebSocket([req, socket, rest], {
protocolVersion: 'hixie-76',
protocol: protocol
});
self._clients.push(client);
client.on('close', function() {
var index = self._clients.indexOf(client);
if (index != -1) {
self._clients.splice(index, 1);
}
});
cb(client);
}
// retrieve nonce
var nonceLength = 8;
if (upgradeHead && upgradeHead.length >= nonceLength) {
var nonce = upgradeHead.slice(0, nonceLength);
var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null;
completeHandshake.call(self, nonce, rest);
}
else {
// nonce not present in upgradeHead, so we must wait for enough data
// data to arrive before continuing
var nonce = new Buffer(nonceLength);
upgradeHead.copy(nonce, 0);
var received = upgradeHead.length;
var rest = null;
var handler = function (data) {
var toRead = Math.min(data.length, nonceLength - received);
if (toRead === 0) return;
data.copy(nonce, received, 0, toRead);
received += toRead;
if (received == nonceLength) {
socket.removeListener('data', handler);
if (toRead < data.length) rest = data.slice(toRead);
completeHandshake.call(self, nonce, rest);
}
}
socket.on('data', handler);
}
}
// verify client

@@ -253,98 +357,16 @@ var location = (socket.encrypted ? 'wss' : 'ws') + '://' + req.headers.host + req.url

};
if (!this.options.verifyClient(info)) {
abortConnection(socket, 401, 'Unauthorized');
if (this.options.verifyClient.length == 2) {
var self = this;
this.options.verifyClient(info, function(result) {
if (!result) abortConnection(socket, 401, 'Unauthorized')
else onClientVerified.apply(self);
});
return;
}
}
var protocol = req.headers['sec-websocket-protocol'];
var completeHandshake = function(nonce, rest) {
// calculate key
var k1 = req.headers['sec-websocket-key1']
, k2 = req.headers['sec-websocket-key2']
, md5 = crypto.createHash('md5')
, self = this;
[k1, k2].forEach(function (k) {
var n = parseInt(k.replace(/[^\d]/g, ''))
, spaces = k.replace(/[^ ]/g, '').length;
if (spaces === 0 || n % spaces !== 0){
abortConnection(socket, 400, 'Bad Request');
return;
}
n /= spaces;
md5.update(String.fromCharCode(
n >> 24 & 0xFF,
n >> 16 & 0xFF,
n >> 8 & 0xFF,
n & 0xFF));
});
md5.update(nonce.toString('binary'));
var headers = [
'HTTP/1.1 101 Switching Protocols'
, 'Upgrade: WebSocket'
, 'Connection: Upgrade'
, 'Sec-WebSocket-Location: ' + location
];
if (typeof protocol != 'undefined') {
headers.push('Sec-WebSocket-Protocol: ' + protocol);
}
if (typeof origin != 'undefined') {
headers.push('Sec-WebSocket-Origin: ' + origin);
}
socket.setTimeout(0);
socket.setNoDelay(true);
try {
socket.write(headers.concat('', '').join('\r\n'));
socket.write(md5.digest('binary'), 'binary');
}
catch (e) {
try { socket.end(); } catch (_) {}
else if (!this.options.verifyClient(info)) {
abortConnection(socket, 401, 'Unauthorized');
return;
}
var client = new WebSocket([req, socket, rest], {
protocolVersion: 'hixie-76',
protocol: protocol
});
this._clients.push(client);
var self = this;
client.on('close', function() {
var index = self._clients.indexOf(client);
if (index != -1) {
self._clients.splice(index, 1);
}
});
cb(client);
}
var nonceLength = 8;
if (upgradeHead && upgradeHead.length >= nonceLength) {
var nonce = upgradeHead.slice(0, nonceLength);
var rest = upgradeHead.length > nonceLength ? upgradeHead.slice(nonceLength) : null;
completeHandshake.call(this, nonce, rest);
}
else {
// nonce not present in upgradeHead, so we must wait for enough data
// data to arrive before continuing
var nonce = new Buffer(nonceLength);
upgradeHead.copy(nonce, 0);
var received = upgradeHead.length;
var rest = null;
var self = this;
var handler = function (data) {
var toRead = Math.min(data.length, nonceLength - received);
if (toRead === 0) return;
data.copy(nonce, received, 0, toRead);
received += toRead;
if (received == nonceLength) {
socket.removeListener('data', handler);
if (toRead < data.length) rest = data.slice(toRead);
completeHandshake.call(self, nonce, rest);
}
}
socket.on('data', handler);
}
onClientVerified();
}

@@ -351,0 +373,0 @@

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

"description": "simple to use, blazing fast and thoroughly tested websocket client, server and console for node.js, up-to-date against RFC-6455",
"version": "0.4.7",
"version": "0.4.8",
"repository": {

@@ -29,4 +29,5 @@ "type": "git",

"benchmark": "0.3.x",
"tinycolor": "0.x"
"tinycolor": "0.x",
"ansi": "latest"
}
}
var Sender = require('../lib/Sender');
require('should');
describe('Sender', function() {
it('creates a send cache equal to options.sendBufferCacheSize', function() {
var sender = new Sender(null, {
sendBufferCacheSize: 10
});
sender._sendCache.length.should.eql(10);
});
it('keeps a send cache equal to null if options.sendBufferCacheSize is 0', function() {
var sender = new Sender(null, {
sendBufferCacheSize: 0
});
(typeof sender._sendCache).should.eql('undefined');
});
it('keeps a send cache equal to null if options.sendBufferCacheSize is -1', function() {
var sender = new Sender(null, {
sendBufferCacheSize: -1
});
(typeof sender._sendCache).should.eql('undefined');
});
describe('Sender', function() {
describe('#frameAndSend', function() {
it('does not modify a masked binary buffer', function() {
var sender = new Sender({ write: function() {} });
var sender = new Sender({ write: function() {} });
var buf = new Buffer([1, 2, 3, 4, 5]);

@@ -39,3 +18,3 @@ sender.frameAndSend(2, buf, true, true);

it('does not modify a masked text buffer', function() {
var sender = new Sender({ write: function() {} });
var sender = new Sender({ write: function() {} });
var text = 'hi there';

@@ -42,0 +21,0 @@ sender.frameAndSend(1, text, true, true);

@@ -290,2 +290,13 @@ var assert = require('assert')

it('before connect can silently fail', function(done) {
server.createServer(++port, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.on('error', function() {});
ws.ping('', {}, true);
srv.close();
ws.terminate();
done();
});
});
it('without message is successfully transmitted to the server', function(done) {

@@ -338,2 +349,28 @@ server.createServer(++port, function(srv) {

describe('#pong', function() {
it('before connect should fail', function(done) {
server.createServer(++port, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.on('error', function() {});
try {
ws.pong();
}
catch (e) {
srv.close();
ws.terminate();
done();
}
});
});
it('before connect can silently fail', function(done) {
server.createServer(++port, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.on('error', function() {});
ws.pong('', {}, true);
srv.close();
ws.terminate();
done();
});
});
it('without message is successfully transmitted to the server', function(done) {

@@ -340,0 +377,0 @@ server.createServer(++port, function(srv) {

@@ -505,2 +505,59 @@ var http = require('http')

it('client can be denied asynchronously', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
cb(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);
process.nextTick(function() {
wss.close();
done();
});
});
});
wss.on('connection', function(ws) {
done(new Error('connection must not be established'));
});
wss.on('error', function() {});
});
it('client can be accepted asynchronously', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
cb(true);
}}, 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();
});
wss.on('connection', function(ws) {
ws.terminate();
wss.close();
done();
});
wss.on('error', function() {});
});
it('handles messages passed along with the upgrade request (upgrade head)', function(done) {

@@ -784,2 +841,60 @@ var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {

it('client can be denied asynchronously', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
cb(false);
}}, function() {
var options = {
port: port,
host: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'WebSocket',
'Origin': 'http://foobarbaz.com',
'Sec-WebSocket-Key1': '3e6b263 4 17 80',
'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
}
};
var req = http.request(options);
req.write('WjN}|M(6');
req.end();
req.on('response', function(res) {
res.statusCode.should.eql(401);
process.nextTick(function() {
wss.close();
done();
});
});
});
wss.on('connection', function(ws) {
done(new Error('connection must not be established'));
});
wss.on('error', function() {});
});
it('client can be accepted asynchronously', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) {
cb(true);
}}, function() {
var options = {
port: port,
host: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'WebSocket',
'Origin': 'http://foobarbaz.com',
'Sec-WebSocket-Key1': '3e6b263 4 17 80',
'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
}
};
var req = http.request(options);
req.write('WjN}|M(6');
req.end();
});
wss.on('connection', function(ws) {
wss.close();
done();
});
wss.on('error', function() {});
});
it('handles messages passed along with the upgrade request (upgrade head)', function(done) {

@@ -786,0 +901,0 @@ var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {

Sorry, the diff of this file is not supported yet

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