summaryrefslogtreecommitdiff
path: root/node_modules/ws/test/WebSocket.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/ws/test/WebSocket.test.js')
-rw-r--r--node_modules/ws/test/WebSocket.test.js1609
1 files changed, 1609 insertions, 0 deletions
diff --git a/node_modules/ws/test/WebSocket.test.js b/node_modules/ws/test/WebSocket.test.js
new file mode 100644
index 0000000..93e95da
--- /dev/null
+++ b/node_modules/ws/test/WebSocket.test.js
@@ -0,0 +1,1609 @@
+var assert = require('assert')
+ , https = require('https')
+ , http = require('http')
+ , should = require('should')
+ , WebSocket = require('../')
+ , WebSocketServer = require('../').Server
+ , fs = require('fs')
+ , server = require('./testserver')
+ , crypto = require('crypto');
+
+var port = 20000;
+
+function getArrayBuffer(buf) {
+ var l = buf.length;
+ var arrayBuf = new ArrayBuffer(l);
+ for (var i = 0; i < l; ++i) {
+ arrayBuf[i] = buf[i];
+ }
+ return arrayBuf;
+}
+
+function areArraysEqual(x, y) {
+ if (x.length != y.length) return false;
+ for (var i = 0, l = x.length; i < l; ++i) {
+ if (x[i] !== y[i]) return false;
+ }
+ return true;
+}
+
+describe('WebSocket', function() {
+ describe('#ctor', function() {
+ it('throws exception for invalid url', function(done) {
+ try {
+ var ws = new WebSocket('echo.websocket.org');
+ }
+ catch (e) {
+ done();
+ }
+ });
+ });
+
+ describe('properties', function() {
+ it('#bytesReceived exposes number of bytes received', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('message', function() {
+ ws.bytesReceived.should.eql(8);
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(ws) {
+ ws.send('foobar');
+ });
+ });
+
+ it('#url exposes the server url', function(done) {
+ server.createServer(++port, function(srv) {
+ var url = 'ws://localhost:' + port;
+ var ws = new WebSocket(url);
+ assert.equal(url, ws.url);
+ ws.terminate();
+ ws.on('close', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ 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('#bufferedAmount', function() {
+ it('defaults to zero', function(done) {
+ server.createServer(++port, function(srv) {
+ var url = 'ws://localhost:' + port;
+ var ws = new WebSocket(url);
+ assert.equal(0, ws.bufferedAmount);
+ ws.terminate();
+ ws.on('close', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('stress kernel write buffer', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ });
+ wss.on('connection', function(ws) {
+ while (true) {
+ if (ws.bufferedAmount > 0) break;
+ ws.send((new Array(10000)).join('hello'));
+ }
+ ws.terminate();
+ ws.on('close', function() {
+ wss.close();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('#readyState', function() {
+ it('defaults to connecting', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ assert.equal(WebSocket.CONNECTING, ws.readyState);
+ ws.terminate();
+ ws.on('close', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('set to open once connection is established', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ assert.equal(WebSocket.OPEN, ws.readyState);
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('set to closed once connection is closed', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.close(1001);
+ ws.on('close', function() {
+ assert.equal(WebSocket.CLOSED, ws.readyState);
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('set to closed once connection is terminated', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.terminate();
+ ws.on('close', function() {
+ assert.equal(WebSocket.CLOSED, ws.readyState);
+ srv.close();
+ done();
+ });
+ });
+ });
+ });
+
+ /*
+ * Ready state constants
+ */
+
+ var readyStates = {
+ CONNECTING: 0,
+ OPEN: 1,
+ CLOSING: 2,
+ CLOSED: 3
+ };
+
+ /*
+ * Ready state constant tests
+ */
+
+ Object.keys(readyStates).forEach(function(state) {
+ describe('.' + state, function() {
+ it('is enumerable property of class', function() {
+ var propertyDescripter = Object.getOwnPropertyDescriptor(WebSocket, state)
+ assert.equal(readyStates[state], propertyDescripter.value);
+ assert.equal(true, propertyDescripter.enumerable);
+ });
+ });
+ });
+
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ Object.keys(readyStates).forEach(function(state) {
+ describe('.' + state, function() {
+ it('is property of instance', function() {
+ assert.equal(readyStates[state], ws[state]);
+ });
+ });
+ });
+ });
+ });
+
+ describe('events', function() {
+ it('emits a ping event', function(done) {
+ var wss = new WebSocketServer({port: ++port});
+ wss.on('connection', function(client) {
+ client.ping();
+ });
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('ping', function() {
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+
+ it('emits a pong event', function(done) {
+ var wss = new WebSocketServer({port: ++port});
+ wss.on('connection', function(client) {
+ client.pong();
+ });
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('pong', function() {
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ });
+
+ 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();
+ });
+ });
+ });
+
+ 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();
+ });
+ });
+ });
+
+ 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('error is emitted if server aborts connection', function(done) {
+ server.createServer(++port, server.handlers.return401, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ assert.fail('connect shouldnt be raised here');
+ });
+ ws.on('error', function() {
+ srv.close();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('#pause and #resume', function() {
+ it('pauses the underlying stream', function(done) {
+ // this test is sort-of racecondition'y, since an unlikely slow connection
+ // to localhost can cause the test to succeed even when the stream pausing
+ // isn't working as intended. that is an extremely unlikely scenario, though
+ // and an acceptable risk for the test.
+ var client;
+ var serverClient;
+ var openCount = 0;
+ function onOpen() {
+ if (++openCount == 2) {
+ var paused = true;
+ serverClient.on('message', function() {
+ paused.should.not.be.ok;
+ wss.close();
+ done();
+ });
+ serverClient.pause();
+ setTimeout(function() {
+ paused = false;
+ serverClient.resume();
+ }, 200);
+ client.send('foo');
+ }
+ }
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ serverClient = ws;
+ serverClient.on('open', onOpen);
+ });
+ wss.on('connection', function(ws) {
+ client = ws;
+ onOpen();
+ });
+ });
+ });
+
+ describe('#ping', 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.ping();
+ }
+ 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.ping('', {}, true);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+
+ it('without message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.ping();
+ });
+ srv.on('ping', function(message) {
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.ping('hi');
+ });
+ srv.on('ping', function(message) {
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with encoded message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.ping('hi', {mask: true});
+ });
+ srv.on('ping', function(message, flags) {
+ assert.ok(flags.masked);
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+ });
+
+ 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) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.pong();
+ });
+ srv.on('pong', function(message) {
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.pong('hi');
+ });
+ srv.on('pong', function(message) {
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with encoded message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.pong('hi', {mask: true});
+ });
+ srv.on('pong', function(message, flags) {
+ assert.ok(flags.masked);
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('#send', function() {
+ it('very long binary data can be sent and received (with echoing server)', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var array = new Float32Array(5 * 1024 * 1024);
+ for (var i = 0; i < array.length; ++i) array[i] = i / 5;
+ ws.on('open', function() {
+ ws.send(array, {binary: true});
+ });
+ ws.on('message', function(message, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message))));
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('can send and receive text data', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.send('hi');
+ });
+ ws.on('message', function(message, flags) {
+ assert.equal('hi', message);
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('send and receive binary data as an array', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var array = new Float32Array(5);
+ for (var i = 0; i < array.length; ++i) array[i] = i / 2;
+ ws.on('open', function() {
+ ws.send(array, {binary: true});
+ });
+ ws.on('message', function(message, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message))));
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('binary data can be sent and received as buffer', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var buf = new Buffer('foobar');
+ ws.on('open', function() {
+ ws.send(buf, {binary: true});
+ });
+ ws.on('message', function(message, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(buf, message));
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('ArrayBuffer is auto-detected without binary flag', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var array = new Float32Array(5);
+ for (var i = 0; i < array.length; ++i) array[i] = i / 2;
+ ws.on('open', function() {
+ ws.send(array.buffer);
+ });
+ ws.onmessage = function (event) {
+ assert.ok(event.type = 'Binary');
+ assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(event.data))));
+ ws.terminate();
+ srv.close();
+ done();
+ };
+ });
+ });
+
+ it('Buffer is auto-detected without binary flag', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var buf = new Buffer('foobar');
+ ws.on('open', function() {
+ ws.send(buf);
+ });
+ ws.onmessage = function (event) {
+ assert.ok(event.type = 'Binary');
+ assert.ok(areArraysEqual(event.data, buf));
+ ws.terminate();
+ srv.close();
+ done();
+ };
+ });
+ });
+
+ 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.send('hi');
+ }
+ catch (e) {
+ ws.terminate();
+ srv.close();
+ done();
+ }
+ });
+ });
+
+ it('before connect should pass error through callback, if present', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {});
+ ws.send('hi', function(error) {
+ assert.ok(error instanceof Error);
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('without data should be successful', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.send();
+ });
+ srv.on('message', function(message, flags) {
+ assert.equal('', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('calls optional callback when flushed', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.send('hi', function() {
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+ });
+
+ it('with unencoded message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.send('hi');
+ });
+ srv.on('message', function(message, flags) {
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with encoded message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.send('hi', {mask: true});
+ });
+ srv.on('message', function(message, flags) {
+ assert.ok(flags.masked);
+ assert.equal('hi', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with unencoded binary message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var array = new Float32Array(5);
+ for (var i = 0; i < array.length; ++i) array[i] = i / 2;
+ ws.on('open', function() {
+ ws.send(array, {binary: true});
+ });
+ srv.on('message', function(message, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message))));
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with encoded binary message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var array = new Float32Array(5);
+ for (var i = 0; i < array.length; ++i) array[i] = i / 2;
+ ws.on('open', function() {
+ ws.send(array, {mask: true, binary: true});
+ });
+ srv.on('message', function(message, flags) {
+ assert.ok(flags.binary);
+ assert.ok(flags.masked);
+ assert.ok(areArraysEqual(array, new Float32Array(getArrayBuffer(message))));
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with binary stream will send fragmented data', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var callbackFired = false;
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream, {binary: true}, function(error) {
+ assert.equal(null, error);
+ callbackFired = true;
+ });
+ });
+ srv.on('message', function(data, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile'), data));
+ ws.terminate();
+ });
+ ws.on('close', function() {
+ assert.ok(callbackFired);
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('with text stream will send fragmented data', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var callbackFired = false;
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream, {binary: false}, function(error) {
+ assert.equal(null, error);
+ callbackFired = true;
+ });
+ });
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ ws.terminate();
+ });
+ ws.on('close', function() {
+ assert.ok(callbackFired);
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('will cause intermittent send to be delayed in order', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream);
+ ws.send('foobar');
+ ws.send('baz');
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ ++receivedIndex;
+ if (receivedIndex == 1) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ }
+ else if (receivedIndex == 2) {
+ assert.ok(!flags.binary);
+ assert.equal('foobar', data);
+ }
+ else {
+ assert.ok(!flags.binary);
+ assert.equal('baz', data);
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent stream to be delayed in order', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream);
+ var i = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i == 1) send('foo');
+ else send('bar', true);
+ });
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ ++receivedIndex;
+ if (receivedIndex == 1) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ }
+ else if (receivedIndex == 2) {
+ assert.ok(!flags.binary);
+ assert.equal('foobar', data);
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent ping to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream);
+ ws.ping('foobar');
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ srv.on('ping', function(data) {
+ assert.equal('foobar', data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent pong to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream);
+ ws.pong('foobar');
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ srv.on('pong', function(data) {
+ assert.equal('foobar', data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent close to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream);
+ ws.close(1000, 'foobar');
+ });
+ ws.on('close', function() {
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ ws.on('error', function() { /* That's quite alright -- a send was attempted after close */ });
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.ok(areArraysEqual(fs.readFileSync('test/fixtures/textfile', 'utf8'), data));
+ });
+ srv.on('close', function(code, data) {
+ assert.equal(1000, code);
+ assert.equal('foobar', data);
+ });
+ });
+ });
+ });
+
+ describe('#stream', function() {
+ it('very long binary data can be streamed', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var buffer = new Buffer(10 * 1024);
+ for (var i = 0; i < buffer.length; ++i) buffer[i] = i % 0xff;
+ ws.on('open', function() {
+ var i = 0;
+ var blockSize = 800;
+ var bufLen = buffer.length;
+ ws.stream({binary: true}, function(error, send) {
+ assert.ok(!error);
+ var start = i * blockSize;
+ var toSend = Math.min(blockSize, bufLen - (i * blockSize));
+ var end = start + toSend;
+ var isFinal = toSend < blockSize;
+ send(buffer.slice(start, end), isFinal);
+ i += 1;
+ });
+ });
+ srv.on('message', function(data, flags) {
+ assert.ok(flags.binary);
+ assert.ok(areArraysEqual(buffer, data));
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('before connect should pass error through callback', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {});
+ ws.stream(function(error) {
+ assert.ok(error instanceof Error);
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('without callback should fail', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ ws.on('open', function() {
+ try {
+ ws.stream();
+ }
+ catch (e) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent send to be delayed in order', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ ws.on('open', function() {
+ var i = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i == 1) {
+ send(payload.substr(0, 5));
+ ws.send('foobar');
+ ws.send('baz');
+ }
+ else {
+ send(payload.substr(5, 5), true);
+ }
+ });
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ ++receivedIndex;
+ if (receivedIndex == 1) {
+ assert.ok(!flags.binary);
+ assert.equal(payload, data);
+ }
+ else if (receivedIndex == 2) {
+ assert.ok(!flags.binary);
+ assert.equal('foobar', data);
+ }
+ else {
+ assert.ok(!flags.binary);
+ assert.equal('baz', data);
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent stream to be delayed in order', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ ws.on('open', function() {
+ var i = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i == 1) {
+ send(payload.substr(0, 5));
+ var i2 = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i2 == 1) send('foo');
+ else send('bar', true);
+ });
+ ws.send('baz');
+ }
+ else send(payload.substr(5, 5), true);
+ });
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ ++receivedIndex;
+ if (receivedIndex == 1) {
+ assert.ok(!flags.binary);
+ assert.equal(payload, data);
+ }
+ else if (receivedIndex == 2) {
+ assert.ok(!flags.binary);
+ assert.equal('foobar', data);
+ }
+ else if (receivedIndex == 3){
+ assert.ok(!flags.binary);
+ assert.equal('baz', data);
+ setTimeout(function() {
+ srv.close();
+ ws.terminate();
+ done();
+ }, 1000);
+ }
+ else throw new Error('more messages than we actually sent just arrived');
+ });
+ });
+ });
+
+ it('will cause intermittent ping to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ ws.on('open', function() {
+ var i = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i == 1) {
+ send(payload.substr(0, 5));
+ ws.ping('foobar');
+ }
+ else {
+ send(payload.substr(5, 5), true);
+ }
+ });
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.equal(payload, data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ srv.on('ping', function(data) {
+ assert.equal('foobar', data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent pong to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ ws.on('open', function() {
+ var i = 0;
+ ws.stream(function(error, send) {
+ assert.ok(!error);
+ if (++i == 1) {
+ send(payload.substr(0, 5));
+ ws.pong('foobar');
+ }
+ else {
+ send(payload.substr(5, 5), true);
+ }
+ });
+ });
+ var receivedIndex = 0;
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.equal(payload, data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ srv.on('pong', function(data) {
+ assert.equal('foobar', data);
+ if (++receivedIndex == 2) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('will cause intermittent close to be delivered', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var payload = 'HelloWorld';
+ var errorGiven = false;
+ ws.on('open', function() {
+ var i = 0;
+ ws.stream(function(error, send) {
+ if (++i == 1) {
+ send(payload.substr(0, 5));
+ ws.close(1000, 'foobar');
+ }
+ else if(i == 2) {
+ send(payload.substr(5, 5), true);
+ }
+ else if (i == 3) {
+ assert.ok(error);
+ errorGiven = true;
+ }
+ });
+ });
+ ws.on('close', function() {
+ assert.ok(errorGiven);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ srv.on('message', function(data, flags) {
+ assert.ok(!flags.binary);
+ assert.equal(payload, data);
+ });
+ srv.on('close', function(code, data) {
+ assert.equal(1000, code);
+ assert.equal('foobar', data);
+ });
+ });
+ });
+ });
+
+ describe('#close', function() {
+ it('will raise error callback, if any, if called during send stream', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var errorGiven = false;
+ ws.on('open', function() {
+ var fileStream = fs.createReadStream('test/fixtures/textfile');
+ fileStream.setEncoding('utf8');
+ fileStream.bufferSize = 100;
+ ws.send(fileStream, function(error) {
+ errorGiven = error != null;
+ });
+ ws.close(1000, 'foobar');
+ });
+ ws.on('close', function() {
+ setTimeout(function() {
+ assert.ok(errorGiven);
+ srv.close();
+ ws.terminate();
+ done();
+ }, 1000);
+ });
+ });
+ });
+
+ it('without invalid first argument throws exception', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ try {
+ ws.close('error');
+ }
+ catch (e) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('without reserved error code 1004 throws exception', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ try {
+ ws.close(1004);
+ }
+ catch (e) {
+ srv.close();
+ ws.terminate();
+ done();
+ }
+ });
+ });
+ });
+
+ it('without message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.close(1000);
+ });
+ srv.on('close', function(code, message, flags) {
+ assert.equal('', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.close(1000, 'some reason');
+ });
+ srv.on('close', function(code, message, flags) {
+ assert.ok(flags.masked);
+ assert.equal('some reason', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('with encoded message is successfully transmitted to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('open', function() {
+ ws.close(1000, 'some reason', {mask: true});
+ });
+ srv.on('close', function(code, message, flags) {
+ assert.ok(flags.masked);
+ assert.equal('some reason', message);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+
+ it('ends connection to the server', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var connectedOnce = false;
+ ws.on('open', function() {
+ connectedOnce = true;
+ ws.close(1000, 'some reason', {mask: true});
+ });
+ ws.on('close', function() {
+ assert.ok(connectedOnce);
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+ });
+
+ describe('W3C API emulation', function() {
+ it('should not throw errors when getting and setting', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var listener = function () {};
+
+ ws.onmessage = listener;
+ ws.onerror = listener;
+ ws.onclose = listener;
+ ws.onopen = listener;
+
+ assert.ok(ws.onopen === listener);
+ assert.ok(ws.onmessage === listener);
+ assert.ok(ws.onclose === listener);
+ assert.ok(ws.onerror === listener);
+
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+
+ it('should work the same as the EventEmitter api', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ var listener = function() {};
+ var message = 0;
+ var close = 0;
+ var open = 0;
+
+ ws.onmessage = function(messageEvent) {
+ assert.ok(!!messageEvent.data);
+ ++message;
+ ws.close();
+ };
+
+ ws.onopen = function() {
+ ++open;
+ }
+
+ ws.onclose = function() {
+ ++close;
+ }
+
+ ws.on('open', function() {
+ ws.send('foo');
+ });
+
+ ws.on('close', function() {
+ process.nextTick(function() {
+ assert.ok(message === 1);
+ assert.ok(open === 1);
+ assert.ok(close === 1);
+
+ srv.close();
+ ws.terminate();
+ done();
+ });
+ });
+ });
+ });
+
+ it('should receive text data wrapped in a MessageEvent when using addEventListener', function(done) {
+ server.createServer(++port, function(srv) {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.addEventListener('open', function() {
+ ws.send('hi');
+ });
+ ws.addEventListener('message', function(messageEvent) {
+ assert.equal('hi', messageEvent.data);
+ ws.terminate();
+ srv.close();
+ done();
+ });
+ });
+ });
+
+ it('should receive valid CloseEvent when server closes with code 1000', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.addEventListener('close', function(closeEvent) {
+ assert.equal(true, closeEvent.wasClean);
+ assert.equal(1000, closeEvent.code);
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(client) {
+ client.close(1000);
+ });
+ });
+
+ it('should receive valid CloseEvent when server closes with code 1001', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.addEventListener('close', function(closeEvent) {
+ assert.equal(false, closeEvent.wasClean);
+ assert.equal(1001, closeEvent.code);
+ assert.equal('some daft reason', closeEvent.reason);
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(client) {
+ client.close(1001, 'some daft reason');
+ });
+ });
+
+ it('should have target set on Events', function(done) {
+ var wss = new WebSocketServer({port: ++port}, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.addEventListener('open', function(openEvent) {
+ assert.equal(ws, openEvent.target);
+ });
+ ws.addEventListener('message', function(messageEvent) {
+ assert.equal(ws, messageEvent.target);
+ wss.close();
+ });
+ ws.addEventListener('close', function(closeEvent) {
+ assert.equal(ws, closeEvent.target);
+ ws.emit('error', new Error('forced'));
+ });
+ ws.addEventListener('error', function(errorEvent) {
+ assert.equal(errorEvent.message, 'forced');
+ assert.equal(ws, errorEvent.target);
+ ws.terminate();
+ done();
+ });
+ });
+ wss.on('connection', function(client) {
+ client.send('hi')
+ });
+ });
+ });
+
+ describe('ssl', function() {
+ it('can connect to secure websocket server', function(done) {
+ var options = {
+ 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 wss = new WebSocketServer({server: app});
+ app.listen(++port, function() {
+ var ws = new WebSocket('wss://localhost:' + port);
+ });
+ wss.on('connection', function(ws) {
+ app.close();
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+
+ it('can connect to secure websocket server with client side certificate', function(done) {
+ var options = {
+ key: fs.readFileSync('test/fixtures/key.pem'),
+ cert: fs.readFileSync('test/fixtures/certificate.pem'),
+ ca: [fs.readFileSync('test/fixtures/ca1-cert.pem')],
+ requestCert: true
+ };
+ var clientOptions = {
+ key: fs.readFileSync('test/fixtures/agent1-key.pem'),
+ cert: fs.readFileSync('test/fixtures/agent1-cert.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.req.client.authorized;
+ return true;
+ }
+ });
+ app.listen(++port, function() {
+ var ws = new WebSocket('wss://localhost:' + port, clientOptions);
+ });
+ wss.on('connection', function(ws) {
+ app.close();
+ ws.terminate();
+ wss.close();
+ success.should.be.ok;
+ done();
+ });
+ });
+
+ it('cannot connect to secure websocket server via ws://', function(done) {
+ var options = {
+ 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 wss = new WebSocketServer({server: app});
+ app.listen(++port, function() {
+ var ws = new WebSocket('ws://localhost:' + port);
+ ws.on('error', function() {
+ app.close();
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ });
+
+ it('can send and receive text data', function(done) {
+ var options = {
+ 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 wss = new WebSocketServer({server: app});
+ app.listen(++port, function() {
+ var ws = new WebSocket('wss://localhost:' + port);
+ ws.on('open', function() {
+ ws.send('foobar');
+ });
+ });
+ wss.on('connection', function(ws) {
+ ws.on('message', function(message, flags) {
+ message.should.eql('foobar');
+ app.close();
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ });
+
+ it('can send and receive very long binary data', function(done) {
+ var options = {
+ 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();
+ });
+ crypto.randomBytes(5 * 1024 * 1024, function(ex, buf) {
+ if (ex) throw ex;
+ var wss = new WebSocketServer({server: app});
+ app.listen(++port, function() {
+ var ws = new WebSocket('wss://localhost:' + port);
+ ws.on('open', function() {
+ ws.send(buf, {binary: true});
+ });
+ ws.on('message', function(message, flags) {
+ flags.binary.should.be.ok;
+ areArraysEqual(buf, message).should.be.ok;
+ app.close();
+ ws.terminate();
+ wss.close();
+ done();
+ });
+ });
+ wss.on('connection', function(ws) {
+ ws.on('message', function(message, flags) {
+ ws.send(message, {binary: true});
+ });
+ });
+ });
+ });
+ });
+
+ 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();
+ });
+ });
+ });
+ });
+ });
+});