Click here to Skip to main content
15,897,891 members
Articles / Web Development / Node.js

Node.Js And Stuff

Rate me:
Please Sign up or sign in to vote.
4.97/5 (55 votes)
11 Feb 2013CPOL23 min read 362.4K   2.3K   172  
Small demo app using Node.Js/Socket.IO/MongoDB/D3.Js and jQuery.
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('#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', function() {
          var propertyDescripter = Object.getOwnPropertyDescriptor(WebSocket, state)
          assert.equal(readyStates[state], propertyDescripter.value);
          assert.equal(true, propertyDescripter.enumerable);
        });
      });
    });
  });

  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('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 vaild 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');
      });
    });
  });

  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('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();
          });
        });
      });
    });
  });
});

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions