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.1 to 0.4.2

bench/parser.benchmark.js

10

History.md

@@ -0,1 +1,11 @@

v0.4.2 - Feb 4th 2012
=====================
* Breaking change: WebSocketServer's verifyOrigin option has been renamed to verifyClient. [einaros]
* verifyClient now receives { origin: 'origin header', secure: true/false }, where 'secure' will be true for ssl connections. [einaros]
* Split benchmark, in preparation for more thorough case. [einaros]
* Introduced hixie-76 draft support for server, since Safari (iPhone / iPad / OS X) and Opera still aren't updated to use Hybi. [einaros]
* Expose 'supports' object from WebSocket, to indicate e.g. the underlying transport's support for binary data. [einaros]
* Test and code cleanups. [einaros]
v0.4.1 - Jan 25th 2012

@@ -2,0 +12,0 @@ =====================

6

lib/Receiver.js

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

var buf = this.overflow.pop();
if (toRead < buf.length) this.overflow.push(buf.slice(toRead))
if (toRead < buf.length) this.overflow.push(buf.slice(toRead));
var read = Math.min(buf.length, toRead);

@@ -156,3 +156,3 @@ this.add(buf.slice(0, read));

var buf = this.overflow.pop();
if (toRead < buf.length) this.overflow.push(buf.slice(toRead))
if (toRead < buf.length) this.overflow.push(buf.slice(toRead));
var read = Math.min(buf.length, toRead);

@@ -193,3 +193,3 @@ this.add(buf.slice(0, read));

if (!(this.state.opcode == 1 || this.state.opcode == 2)) {
this.error('continuation frame cannot follow current opcode', 1002)
this.error('continuation frame cannot follow current opcode', 1002);
return;

@@ -196,0 +196,0 @@ }

@@ -16,3 +16,5 @@ /*!

, Sender = require('./Sender')
, Receiver = require('./Receiver');
, Receiver = require('./Receiver')
, SenderHixie = require('./Sender.hixie')
, ReceiverHixie = require('./Receiver.hixie');

@@ -23,3 +25,4 @@ /**

var protocolPrefix = "HyBi-";
// Default protocol version
var protocolVersion = 13;

@@ -33,125 +36,6 @@

var self = this;
if (Object.prototype.toString.call(address) == '[object Array]') {
/**
* Act as server client
*/
options = new Options({
protocolVersion: protocolVersion,
protocol: null
}).merge(options);
// Exposed properties
Object.defineProperty(this, 'protocol', {
value: options.value.protocol,
configurable: false,
enumerable: true
});
Object.defineProperty(this, 'protocolVersion', {
value: options.value.protocolVersion,
configurable: false,
enumerable: true
});
Object.defineProperty(this, 'upgradeReq', {
value: address[0],
configurable: false,
enumerable: true
});
Object.defineProperty(this, '_readyState', { writable: true, value: WebSocket.CONNECTING });
Object.defineProperty(this, '_isServer', { writable: false, value: true });
}
else {
/**
* Act as regular client
*/
Object.defineProperty(this, '_isServer', { writable: false, value: false });
var serverUrl = url.parse(address);
if (!serverUrl.host) throw new Error('invalid url');
var httpObj = (serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:') ? https : http;
Object.defineProperty(this, 'url', {
writable: false,
configurable: false,
enumerable: true,
value: address
});
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.value.protocolVersion + '-' + Date.now()).toString('base64');
var shasum = crypto.createHash('sha1');
shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
var expectedServerKey = shasum.digest('base64');
// node<=v0.4.x compatibility
var isNodeV4 = false;
var agent;
if (/^v0\.4/.test(process.version)) {
isNodeV4 = true;
agent = new httpObj.Agent({
host: serverUrl.hostname,
port: serverUrl.port || 80
});
}
var requestOptions = {
port: serverUrl.port || 80,
host: serverUrl.hostname,
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Sec-WebSocket-Version': options.value.protocolVersion,
'Sec-WebSocket-Key': key
}
};
if (options.value.protocol) {
requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol;
}
if (isNodeV4) {
requestOptions.path = (serverUrl.pathname || '/') + (serverUrl.search || '');
requestOptions.agent = agent;
}
else requestOptions.path = serverUrl.path || '/';
if (options.value.origin) {
if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin;
else requestOptions.headers['Origin'] = options.value.origin;
}
var req = httpObj.request(requestOptions);
(isNodeV4 ? agent : req).on('error', function(error) {
self.emit('error', error);
});
(isNodeV4 ? agent : req).on('upgrade', function(res, socket, upgradeHead) {
if (self.readyState == WebSocket.CLOSED) {
// client closed before server accepted connection
self.emit('close');
socket.end();
return;
}
var serverKey = res.headers['sec-websocket-accept'];
if (typeof serverKey == 'undefined' || serverKey !== expectedServerKey) {
self.emit('error', 'invalid server key');
socket.end();
return;
}
upgrade.call(self, res, socket, upgradeHead);
});
req.end();
Object.defineProperty(this, '_readyState', { writable: true, value: WebSocket.CONNECTING });
}
var realEmit = this.emit;
this.emit = function(event) {
if (event == 'error') delete this._queue;
if (event == 'error') delete self._queue;
realEmit.apply(this, arguments);

@@ -162,9 +46,27 @@ }

get: function() {
return this._readyState;
return self._readyState;
}
});
Object.defineProperty(this, 'supports', {
get: function() {
return {
'binary': self.protocolVersion != 'hixie-76'
};
}
});
if (this._isServer) {
upgrade.apply(this, address);
if (Object.prototype.toString.call(address) == '[object Array]') {
/**
* Act as server client
*/
initAsServerClient.apply(this, address.concat(options));
}
else {
/**
* Act as regular client
*/
initAsClient.apply(this, arguments);
}
}

@@ -410,3 +312,128 @@

function upgrade(res, socket, upgradeHead) {
function initAsServerClient(req, socket, upgradeHead, options) {
options = new Options({
protocolVersion: protocolVersion,
protocol: null
}).merge(options);
// expose state properties
Object.defineProperty(this, 'protocol', {
value: options.value.protocol,
configurable: false,
enumerable: true
});
Object.defineProperty(this, 'protocolVersion', {
value: options.value.protocolVersion,
configurable: false,
enumerable: true
});
Object.defineProperty(this, 'upgradeReq', {
value: req,
configurable: false,
enumerable: true
});
Object.defineProperty(this, '_readyState', { writable: true, value: WebSocket.CONNECTING });
Object.defineProperty(this, '_isServer', { writable: false, value: true });
// establish connection
if (options.value.protocolVersion == 'hixie-76') establishHixieConnection.call(this, socket, upgradeHead);
else establishHybiConnection.call(this, socket, upgradeHead);
}
function initAsClient(address, options) {
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');
}
// verify url and establish http class
var serverUrl = url.parse(address);
if (!serverUrl.host) throw new Error('invalid url');
var httpObj = (serverUrl.protocol === 'wss:' || serverUrl.protocol === 'https:') ? https : http;
// expose state properties
Object.defineProperty(this, '_isServer', { writable: false, value: false });
Object.defineProperty(this, 'url', {
writable: false,
configurable: false,
enumerable: true,
value: address
});
Object.defineProperty(this, 'protocolVersion', {
value: options.value.protocolVersion,
configurable: false,
enumerable: true
});
// begin handshake
var key = new Buffer(options.value.protocolVersion + '-' + Date.now()).toString('base64');
var shasum = crypto.createHash('sha1');
shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
var expectedServerKey = shasum.digest('base64');
// node<=v0.4.x compatibility
var isNodeV4 = false;
var agent;
if (/^v0\.4/.test(process.version)) {
isNodeV4 = true;
agent = new httpObj.Agent({
host: serverUrl.hostname,
port: serverUrl.port || 80
});
}
var requestOptions = {
port: serverUrl.port || 80,
host: serverUrl.hostname,
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Sec-WebSocket-Version': options.value.protocolVersion,
'Sec-WebSocket-Key': key
}
};
if (options.value.protocol) {
requestOptions.headers['Sec-WebSocket-Protocol'] = options.value.protocol;
}
if (isNodeV4) {
requestOptions.path = (serverUrl.pathname || '/') + (serverUrl.search || '');
requestOptions.agent = agent;
}
else requestOptions.path = serverUrl.path || '/';
if (options.value.origin) {
if (options.value.protocolVersion < 13) requestOptions.headers['Sec-WebSocket-Origin'] = options.value.origin;
else requestOptions.headers['Origin'] = options.value.origin;
}
var self = this;
var req = httpObj.request(requestOptions);
(isNodeV4 ? agent : req).on('error', function(error) {
self.emit('error', error);
});
(isNodeV4 ? agent : req).on('upgrade', function(res, socket, upgradeHead) {
if (self.readyState == WebSocket.CLOSED) {
// client closed before server accepted connection
self.emit('close');
socket.end();
return;
}
var serverKey = res.headers['sec-websocket-accept'];
if (typeof serverKey == 'undefined' || serverKey !== expectedServerKey) {
self.emit('error', 'invalid server key');
socket.end();
return;
}
establishHybiConnection.call(self, socket, upgradeHead);
});
req.end();
Object.defineProperty(this, '_readyState', { writable: true, value: WebSocket.CONNECTING });
}
function establishHybiConnection(socket, upgradeHead) {
this._socket = socket;

@@ -467,5 +494,72 @@ socket.setTimeout(0);

if (upgradeHead && upgradeHead.length > 0) receiver.add(upgradeHead);
if (upgradeHead && upgradeHead.length > 0) {
process.nextTick(function() {
receiver.add(upgradeHead);
});
}
}
function establishHixieConnection(socket, upgradeHead) {
this._socket = socket;
socket.setTimeout(0);
socket.setNoDelay(true);
var self = this;
socket.on('end', function() {
if (self.readyState == WebSocket.CLOSED) return;
self._readyState = WebSocket.CLOSED;
self.emit('close', self._closeCode || 1000, self._closeMessage || '');
});
socket.on('close', function() {
if (self.readyState == WebSocket.CLOSED) return;
self._readyState = WebSocket.CLOSED;
self.emit('close', self._closeCode || 1000, self._closeMessage || '');
});
var receiver = new ReceiverHixie();
socket.on('data', function (data) {
receiver.add(data);
});
receiver.on('text', function (data, flags) {
flags = flags || {};
self.emit('message', data, flags);
});
receiver.on('binary', function (data, flags) {
flags = flags || {};
flags.binary = true;
self.emit('message', data, flags);
});
receiver.on('ping', function(data, flags) {
flags = flags || {};
self.pong(data, {mask: !self._isServer, binary: flags.binary === true});
self.emit('ping', data, flags);
});
receiver.on('pong', function(data, flags) {
self.emit('pong', data, flags);
});
receiver.on('close', function(code, data, flags) {
flags = flags || {};
self.close(code, data, {mask: !self._isServer});
});
receiver.on('error', function(reason, errorCode) {
// close the connection when the receiver reports a HyBi error code
if (typeof errorCode !== 'undefined') {
self.close(errorCode, '', {mask: !self._isServer});
}
self.emit('error', reason, errorCode);
});
Object.defineProperty(this, '_sender', { value: new SenderHixie(socket) });
this._sender.on('error', function(error) {
self.emit('error', error);
});
this._readyState = WebSocket.OPEN;
this.emit('open');
if (upgradeHead && upgradeHead.length > 0) {
process.nextTick(function() {
receiver.add(upgradeHead);
});
}
}
function startQueue(instance) {

@@ -472,0 +566,0 @@ instance._queue = instance._queue || [];

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

, WebSocket = require('./WebSocket')
, tls = require('tls')
, url = require('url');

@@ -26,5 +27,6 @@

server: null,
verifyOrigin: null,
verifyClient: null,
path: null,
noServer: false
noServer: false,
disableHixie: false
}).merge(options);

@@ -70,4 +72,5 @@ if (!options.value.port && !options.value.server && !options.value.noServer) {

this._server.on('upgrade', function(req, socket, upgradeHead) {
var client = self.handleUpgrade(req, socket, upgradeHead);
if (client) self.emit('connection', client);
self.handleUpgrade(req, socket, upgradeHead, function(client) {
self.emit('connection', client);
});
});

@@ -91,3 +94,3 @@ }

/**
* Immediately shuts down the connection
* Immediately shuts down the connection.
*

@@ -129,3 +132,9 @@ * @api public

WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead) {
/**
* Handle a HTTP Upgrade request.
*
* @api public
*/
WebSocketServer.prototype.handleUpgrade = function(req, socket, upgradeHead, cb) {
// check for wrong path

@@ -142,2 +151,14 @@ if (this.options.path) {

if (req.headers['sec-websocket-key1']) handleHixieUpgrade.apply(this, arguments);
else handleHybiUpgrade.apply(this, arguments);
}
module.exports = WebSocketServer;
/**
* Entirely private apis,
* which may or may not be bound to a sepcific WebSocket instance.
*/
function handleHybiUpgrade(req, socket, upgradeHead, cb) {
// verify key presence

@@ -156,8 +177,12 @@ if (!req.headers['sec-websocket-key']) {

// verify origin
// verify client
var origin = version < 13 ?
req.headers['sec-websocket-origin'] :
req.headers['origin'];
if (typeof this.options.verifyOrigin == 'function') {
if (!this.options.verifyOrigin(origin)) {
if (typeof this.options.verifyClient == 'function') {
var info = {
origin: origin,
secure: typeof req.connection.encrypted !== 'undefined'
};
if (!this.options.verifyClient(info)) {
abortConnection(socket, 401, 'Unauthorized');

@@ -194,3 +219,3 @@ return;

socket.setNoDelay(true);
var client = new WebSocket(Array.prototype.slice.call(arguments, 0), {
var client = new WebSocket([req, socket, upgradeHead], {
protocolVersion: version,

@@ -207,12 +232,123 @@ protocol: protocol

});
return client;
cb(client);
}
module.exports = WebSocketServer;
function handleHixieUpgrade(req, socket, upgradeHead, cb) {
if (this.options.disableHixie) {
abortConnection(socket, 401, 'Hixie support disabled');
return;
}
/**
* Entirely private apis,
* which may or may not be bound to a sepcific WebSocket instance.
*/
// verify key presence
if (!req.headers['sec-websocket-key2']) {
abortConnection(socket, 400, 'Bad Request');
return;
}
// verify client
var location = (socket.encrypted ? 'wss' : 'ws') + '://' + req.headers.host + req.url
, origin = req.headers['origin'];
if (typeof this.options.verifyClient == 'function') {
var info = {
origin: origin,
secure: typeof req.connection.encrypted !== 'undefined'
};
if (!this.options.verifyClient(info)) {
abortConnection(socket, 401, 'Unauthorized');
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 (_) {}
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);
}
}
function abortConnection(socket, code, name) {

@@ -219,0 +355,0 @@ try {

{
"author": "Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)",
"name": "ws",
"description": "simple and very fast websocket protocol client for node.js",
"version": "0.4.1",
"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.2",
"repository": {

@@ -7,0 +7,0 @@ "type": "git",

[![Build Status](https://secure.travis-ci.org/einaros/ws.png)](http://travis-ci.org/einaros/ws)
# ws: a node.js websocket implementation #
# ws: a node.js websocket library #
`ws` is a simple to use websocket implementation, up-to-date against RFC-6455.
`ws` is a simple to use websocket implementation, up-to-date against RFC-6455, and [probably the fastest WebSocket library for node.js](http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs).
It is probably also the fastest websocket library running on node.js (http://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs).
Passes the quite extensible Autobahn test suite. See http://einaros.github.com/ws for the full reports.
The module also comes with a command line utility, `wscat`, which can either act as a server (--listen), or client (--connect); Use it to debug simple websocket services.
Comes with a command line utility, `wscat`, which can either act as a server (--listen), or client (--connect); Use it to debug simple websocket services.
## Protocol support ##
* **Hixie draft 76** (Old and deprecated, but still in use by Safari and Opera. Added to ws version 0.4.2, but server only. Can be disabled by setting the `disableHixie` option to true.)
* **HyBi drafts 07-12** (Use the option `protocolVersion: 8`, or argument `-p 8` for wscat)
* **HyBi drafts 13-17** (Current default, alternatively option `protocolVersion: 13`, or argument `-p 13` for wscat)
See the echo.websocket.org example below for how to use the `protocolVersion` option.
_See the echo.websocket.org example below for how to use the `protocolVersion` option._

@@ -105,2 +104,20 @@ ## Usage ##

## API Docs ##
_Note: This api documentation is currently incomplete. For a better understanding of the api, see the test set._
### WebSocket
`supports.binary`
> Returns true or false based on whether the underlying protocol (hixie, hybi, etc) has binary support.
Example:
```js
var wss = new WebSocketServer({port: 8080});
wss.on('connection', function(client) {
if (client.supports.binary) ...
});
```
## Todos ##

@@ -111,3 +128,3 @@

* Either expose a configurable setting indicating favoring speed or memory use, or do a timer based shrink of Receiver's pools.
* Consider rewriting add() without copy: slice the buffer, then copy to overflow.
* Make necessary changes to also support the even older hixie-75? Or at least write a few more tests for Hixie-76 to verify that fragmented nonce transfers really work.

@@ -114,0 +131,0 @@ ## License ##

var assert = require('assert')
, Parser = require('../lib/Receiver');
, Receiver = require('../lib/Receiver');
require('should');

@@ -8,3 +8,3 @@ require('./hybi-common');

it('can parse unmasked text message', function() {
var p = new Parser();
var p = new Receiver();
var packet = '81 05 48 65 6c 6c 6f';

@@ -22,3 +22,3 @@

it('can parse close message', function() {
var p = new Parser();
var p = new Receiver();
var packet = '88 00';

@@ -35,3 +35,3 @@

it('can parse masked text message', function() {
var p = new Parser();
var p = new Receiver();
var packet = '81 93 34 83 a8 68 01 b9 92 52 4f a1 c6 09 59 e6 8a 52 16 e6 cb 00 5b a1 d5';

@@ -49,3 +49,3 @@

it('can parse a masked text message longer than 125 bytes', function() {
var p = new Parser();
var p = new Receiver();
var message = 'A';

@@ -65,3 +65,3 @@ for (var i = 0; i < 300; ++i) message += (i % 5).toString();

it('can parse a really long masked text message', function() {
var p = new Parser();
var p = new Receiver();
var message = 'A';

@@ -81,3 +81,3 @@ for (var i = 0; i < 64*1024; ++i) message += (i % 5).toString();

it('can parse a fragmented masked text message of 300 bytes', function() {
var p = new Parser();
var p = new Receiver();
var message = 'A';

@@ -101,3 +101,3 @@ for (var i = 0; i < 300; ++i) message += (i % 5).toString();

it('can parse a ping message', function() {
var p = new Parser();
var p = new Receiver();
var message = 'Hello';

@@ -116,3 +116,3 @@ var packet = '89 ' + getHybiLengthAsHexString(message.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68'));

it('can parse a ping with no data', function() {
var p = new Parser();
var p = new Receiver();
var packet = '89 00';

@@ -129,3 +129,3 @@

it('can parse a fragmented masked text message of 300 bytes with a ping in the middle', function() {
var p = new Parser();
var p = new Receiver();
var message = 'A';

@@ -161,3 +161,3 @@ for (var i = 0; i < 300; ++i) message += (i % 5).toString();

it('can parse a fragmented masked text message of 300 bytes with a ping in the middle, which is delievered over sevaral tcp packets', function() {
var p = new Parser();
var p = new Receiver();
var message = 'A';

@@ -197,3 +197,3 @@ for (var i = 0; i < 300; ++i) message += (i % 5).toString();

it('can parse a 100 byte long masked binary message', function() {
var p = new Parser();
var p = new Receiver();
var length = 100;

@@ -215,3 +215,3 @@ var message = new Buffer(length);

it('can parse a 256 byte long masked binary message', function() {
var p = new Parser();
var p = new Receiver();
var length = 256;

@@ -233,3 +233,3 @@ var message = new Buffer(length);

it('can parse a 200kb long masked binary message', function() {
var p = new Parser();
var p = new Receiver();
var length = 200 * 1024;

@@ -251,3 +251,3 @@ var message = new Buffer(length);

it('can parse a 200kb long unmasked binary message', function() {
var p = new Parser();
var p = new Receiver();
var length = 200 * 1024;

@@ -254,0 +254,0 @@ var message = new Buffer(length);

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

@@ -54,2 +56,15 @@ , WebSocketServer = require('../').Server

it('#protocolVersion exposes the protocol version', function(done) {
server.createServer(++port, function(srv) {
var url = 'ws://localhost:' + port;
var ws = new WebSocket(url);
assert.equal(13, ws.protocolVersion);
ws.terminate();
ws.on('close', function() {
srv.close();
done();
});
});
});
describe('#readyState', function() {

@@ -152,45 +167,47 @@ it('defaults to connecting', function(done) {

it('can disconnect before connection is established', function(done) {server.createServer(++port, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.terminate();
ws.on('open', function() {
assert.fail('connect shouldnt be raised here');
describe('connection establishing', function() {
it('can disconnect before connection is established', function(done) {server.createServer(++port, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.terminate();
ws.on('open', function() {
assert.fail('connect shouldnt be raised here');
});
ws.on('close', function() {
srv.close();
done();
});
});
ws.on('close', function() {
srv.close();
done();
});
});
});
it('can close before connection is established', function(done) {
server.createServer(++port, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.close(1001);
ws.on('open', function() {
assert.fail('connect shouldnt be raised here');
it('can close before connection is established', function(done) {
server.createServer(++port, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.close(1001);
ws.on('open', function() {
assert.fail('connect shouldnt be raised here');
});
ws.on('close', function() {
srv.close();
done();
});
});
ws.on('close', function() {
srv.close();
done();
});
});
});
it('invalid server key is denied', function(done) {
server.createServer(++port, server.handlers.invalidKey, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.on('error', function() {
srv.close();
done();
it('invalid server key is denied', function(done) {
server.createServer(++port, server.handlers.invalidKey, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.on('error', function() {
srv.close();
done();
});
});
});
});
it('close event is raised when server closes connection', function(done) {
server.createServer(++port, server.handlers.closeAfterConnect, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.on('close', function() {
srv.close();
done();
it('close event is raised when server closes connection', function(done) {
server.createServer(++port, server.handlers.closeAfterConnect, function(srv) {
var ws = new WebSocket('ws://localhost:' + port);
ws.on('close', function() {
srv.close();
done();
});
});

@@ -1259,2 +1276,42 @@ });

});
describe('protocol support discovery', function() {
describe('#supports', function() {
describe('#binary', function() {
it('returns true for hybi transport', function(done) {
var wss = new WebSocketServer({port: ++port}, function() {
var ws = new WebSocket('ws://localhost:' + port);
});
wss.on('connection', function(client) {
assert.equal(true, client.supports.binary);
wss.close();
done();
});
});
it('returns false for hixie transport', 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-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(client) {
assert.equal(false, client.supports.binary);
wss.close();
done();
});
});
});
});
});
});
var http = require('http')
, https = require('https')
, WebSocket = require('../')

@@ -183,101 +184,342 @@ , WebSocketServer = WebSocket.Server

describe('connection establishing', function() {
it('does not accept connections with no sec-websocket-key', function(done) {
describe('#clients', function() {
it('returns a list of connected clients', 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.clients.length.should.eql(0);
var ws = new WebSocket('ws://localhost:' + port);
});
wss.on('connection', function(client) {
wss.clients.length.should.eql(1);
wss.close();
done();
});
});
it('is updated when client terminates the connection', function(done) {
var ws;
var wss = new WebSocketServer({port: ++port}, function() {
ws = new WebSocket('ws://localhost:' + port);
});
wss.on('connection', function(client) {
client.on('close', function() {
wss.clients.length.should.eql(0);
wss.close();
done();
});
ws.terminate();
});
wss.on('error', function() {});
});
it('does not accept connections with no sec-websocket-version', function(done) {
it('is updated when client closes the connection', function(done) {
var ws;
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);
ws = new WebSocket('ws://localhost:' + port);
});
wss.on('connection', function(client) {
client.on('close', function() {
wss.clients.length.should.eql(0);
wss.close();
done();
});
ws.close();
});
wss.on('error', function() {});
});
});
it('does not accept connections with invalid sec-websocket-version', function(done) {
describe('#options', function() {
it('exposes options passed to constructor', 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.options.port.should.eql(port);
wss.close();
done();
});
});
});
describe('#handleUpgrade', function() {
it('can be used for a pre-existing server', function (done) {
var srv = http.createServer();
srv.listen(++port, function () {
var wss = new WebSocketServer({noServer: true});
srv.on('upgrade', function(req, socket, upgradeHead) {
wss.handleUpgrade(req, socket, upgradeHead, function(client) {
client.send('hello');
});
});
var ws = new WebSocket('ws://localhost:' + port);
ws.on('message', function(message) {
message.should.eql('hello');
wss.close();
srv.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() {
describe('hybi mode', function() {
describe('connection establishing', function() {
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('connection', function(ws) {
done(new Error('connection must not be established'));
});
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('connection', function(ws) {
done(new Error('connection must not be established'));
});
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('connection', function(ws) {
done(new Error('connection must not be established'));
});
wss.on('error', function() {});
});
it('client can be denied', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
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);
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', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
return 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('verifyClient gets client origin', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(info) {
info.origin.should.eql('http://foobarbaz.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://foobarbaz.com'
}
};
var req = http.request(options);
req.end();
req.on('response', function(res) {
wss.close();
done();
});
});
wss.on('error', function() {});
});
it('verifyClient has secure:true for ssl connections', function(done) {
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'
key: fs.readFileSync('test/fixtures/key.pem'),
cert: fs.readFileSync('test/fixtures/certificate.pem')
};
var app = https.createServer(options, function (req, res) {
res.writeHead(200);
res.end();
});
var success = false;
var wss = new WebSocketServer({
server: app,
verifyClient: function(info) {
success = info.secure === true;
return true;
}
};
var req = http.request(options);
req.end();
req.on('response', function(res) {
res.statusCode.should.eql(401);
});
app.listen(++port, function() {
var ws = new WebSocket('wss://localhost:' + port);
});
wss.on('connection', function(ws) {
app.close();
ws.terminate();
wss.close();
success.should.be.ok;
done();
});
});
wss.on('error', function() {});
it('verifyClient has secure:false for non-ssl connections', function(done) {
var app = http.createServer(function (req, res) {
res.writeHead(200);
res.end();
});
var success = false;
var wss = new WebSocketServer({
server: app,
verifyClient: function(info) {
success = info.secure === false;
return true;
}
});
app.listen(++port, function() {
var ws = new WebSocket('ws://localhost:' + port);
});
wss.on('connection', function(ws) {
app.close();
ws.terminate();
wss.close();
success.should.be.ok;
done();
});
});
it('handles messages passed along with the upgrade request (upgrade head)', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
return 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.write(new Buffer([0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f], 'binary'));
req.end();
});
wss.on('connection', function(ws) {
ws.on('message', function(data) {
data.should.eql('Hello');
ws.terminate();
wss.close();
done();
});
});
wss.on('error', function() {});
});
});
describe('messaging', function() {
it('can send data', function(done) {
var wss = new WebSocketServer({port: ++port}, function() {
var ws = new WebSocket('ws://localhost:' + port);
ws.on('message', function(data, flags) {
data.should.eql('hello!');
wss.close();
done();
});
});
wss.on('connection', function(client) {
client.send('hello!');
});
});
});
});
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() {
describe('hixie mode', function() {
it('can be disabled', function(done) {
var wss = new WebSocketServer({port: ++port, disableHixie: true}, function() {
var options = {

@@ -288,32 +530,215 @@ port: port,

'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==',
'Sec-WebSocket-Version': 13,
'Origin': 'http://foobar.com'
'Upgrade': 'WebSocket',
'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);
wss.close();
done();
process.nextTick(function() {
wss.close();
done();
});
});
});
wss.on('connection', function(ws) {
done(new Error('connection must not be established'));
});
wss.on('error', function() {});
});
});
it('can send data', function(done) {
var wss = new WebSocketServer({port: ++port}, function() {
var ws = new WebSocket('ws://localhost:' + port);
ws.on('message', function(data, flags) {
data.should.eql('hello!');
wss.close();
done();
describe('connection establishing', function() {
it('does not accept connections with no sec-websocket-key1', 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-Key1': '3e6b263 4 17 80'
}
};
var req = http.request(options);
req.end();
req.on('response', function(res) {
res.statusCode.should.eql(400);
wss.close();
done();
});
});
wss.on('connection', function(ws) {
done(new Error('connection must not be established'));
});
wss.on('error', function() {});
});
it('does not accept connections with no sec-websocket-key2', 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-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90'
}
};
var req = http.request(options);
req.end();
req.on('response', function(res) {
res.statusCode.should.eql(400);
wss.close();
done();
});
});
wss.on('connection', function(ws) {
done(new Error('connection must not be established'));
});
wss.on('error', function() {});
});
it('accepts connections with valid handshake', 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-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) {
ws.terminate();
wss.close();
done();
});
wss.on('error', function() {});
});
it('client can be denied', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
return false;
}}, function() {
var options = {
port: port,
host: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'WebSocket',
'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', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
return true;
}}, function() {
var options = {
port: port,
host: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'WebSocket',
'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) {
ws.terminate();
wss.close();
done();
});
wss.on('error', function() {});
});
it('verifyClient gets client origin', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(info) {
info.origin.should.eql('http://foobarbaz.com');
return 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) {
wss.close();
done();
});
});
wss.on('error', function() {});
});
it('handles messages passed along with the upgrade request (upgrade head)', function(done) {
var wss = new WebSocketServer({port: ++port, verifyClient: function(o) {
return true;
}}, function() {
var options = {
port: port,
host: '127.0.0.1',
headers: {
'Connection': 'Upgrade',
'Upgrade': 'WebSocket',
'Sec-WebSocket-Key1': '3e6b263 4 17 80',
'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90',
'Origin': 'http://foobar.com'
}
};
var req = http.request(options);
req.write('WjN}|M(6');
req.write(new Buffer([0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xff], 'binary'));
req.end();
});
wss.on('connection', function(ws) {
ws.on('message', function(data) {
data.should.eql('Hello');
ws.terminate();
wss.close();
done();
});
});
wss.on('error', function() {});
});
});
wss.on('connection', function(client) {
client.send('hello!');
});
});

@@ -356,76 +781,2 @@

describe('#clients', function() {
it('returns a list of connected clients', function(done) {
var wss = new WebSocketServer({port: ++port}, function() {
wss.clients.length.should.eql(0);
var ws = new WebSocket('ws://localhost:' + port);
});
wss.on('connection', function(client) {
wss.clients.length.should.eql(1);
wss.close();
done();
});
});
it('is updated when client terminates the connection', function(done) {
var ws;
var wss = new WebSocketServer({port: ++port}, function() {
ws = new WebSocket('ws://localhost:' + port);
});
wss.on('connection', function(client) {
client.on('close', function() {
wss.clients.length.should.eql(0);
wss.close();
done();
});
ws.terminate();
});
});
it('is updated when client closes the connection', function(done) {
var ws;
var wss = new WebSocketServer({port: ++port}, function() {
ws = new WebSocket('ws://localhost:' + port);
});
wss.on('connection', function(client) {
client.on('close', function() {
wss.clients.length.should.eql(0);
wss.close();
done();
});
ws.close();
});
});
});
describe('#options', function() {
it('exposes options passed to constructor', function(done) {
var wss = new WebSocketServer({port: ++port}, function() {
wss.options.port.should.eql(port);
wss.close();
done();
});
});
});
describe('#handleUpgrade', function() {
it('can be used for a pre-existing server', function (done) {
var srv = http.createServer();
srv.listen(++port, function () {
var wss = new WebSocketServer({noServer: true});
srv.on('upgrade', function(req, socket, upgradeHead) {
var client = wss.handleUpgrade(req, socket, upgradeHead);
client.send('hello');
});
var ws = new WebSocket('ws://localhost:' + port);
ws.on('message', function(message) {
message.should.eql('hello');
wss.close();
srv.close();
done();
})
});
});
});
});

Sorry, the diff of this file is not supported yet

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