diff options
| author | yo mama <pepper@scannerjammer.com> | 2015-03-20 17:33:43 -0700 |
|---|---|---|
| committer | yo mama <pepper@scannerjammer.com> | 2015-03-20 17:33:43 -0700 |
| commit | 2afbcf4e7d000d99fdbc582d7113684ee00e80cc (patch) | |
| tree | 6ba3313b78625735fb295e3d375e59054c31e558 /node_modules/ws/test | |
first
Diffstat (limited to 'node_modules/ws/test')
21 files changed, 3882 insertions, 0 deletions
diff --git a/node_modules/ws/test/BufferPool.test.js b/node_modules/ws/test/BufferPool.test.js new file mode 100644 index 0000000..1ee7ff0 --- /dev/null +++ b/node_modules/ws/test/BufferPool.test.js @@ -0,0 +1,63 @@ +var BufferPool = require('../lib/BufferPool'); +require('should'); + +describe('BufferPool', function() { + describe('#ctor', function() { + it('allocates pool', function() { + var db = new BufferPool(1000); + db.size.should.eql(1000); + }); + }); + describe('#get', function() { + it('grows the pool if necessary', function() { + var db = new BufferPool(1000); + var buf = db.get(2000); + db.size.should.be.above(1000); + db.used.should.eql(2000); + buf.length.should.eql(2000); + }); + it('grows the pool after the first call, if necessary', function() { + var db = new BufferPool(1000); + var buf = db.get(1000); + db.used.should.eql(1000); + db.size.should.eql(1000); + buf.length.should.eql(1000); + var buf2 = db.get(1000); + db.used.should.eql(2000); + db.size.should.be.above(1000); + buf2.length.should.eql(1000); + }); + it('grows the pool according to the growStrategy if necessary', function() { + var db = new BufferPool(1000, function(db, length) { + return db.size + 2345; + }); + var buf = db.get(2000); + db.size.should.eql(3345); + buf.length.should.eql(2000); + }); + it('doesnt grow the pool if theres enough room available', function() { + var db = new BufferPool(1000); + var buf = db.get(1000); + db.size.should.eql(1000); + buf.length.should.eql(1000); + }); + }); + describe('#reset', function() { + it('shinks the pool', function() { + var db = new BufferPool(1000); + var buf = db.get(2000); + db.reset(true); + db.size.should.eql(1000); + }); + it('shrinks the pool according to the shrinkStrategy', function() { + var db = new BufferPool(1000, function(db, length) { + return db.used + length; + }, function(db) { + return 0; + }); + var buf = db.get(2000); + db.reset(true); + db.size.should.eql(0); + }); + }); +}); diff --git a/node_modules/ws/test/Receiver.hixie.test.js b/node_modules/ws/test/Receiver.hixie.test.js new file mode 100644 index 0000000..043d3bc --- /dev/null +++ b/node_modules/ws/test/Receiver.hixie.test.js @@ -0,0 +1,158 @@ +var assert = require('assert') + , expect = require('expect.js') + , Receiver = require('../lib/Receiver.hixie'); +require('./hybi-common'); + +describe('Receiver', function() { + it('can parse text message', function() { + var p = new Receiver(); + var packet = '00 48 65 6c 6c 6f ff'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('Hello', data); + }; + + p.add(getBufferFromHexString(packet)); + expect(gotData).to.equal(true); + }); + + it('can parse multiple text messages', function() { + var p = new Receiver(); + var packet = '00 48 65 6c 6c 6f ff 00 48 65 6c 6c 6f ff'; + + var gotData = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + + p.add(getBufferFromHexString(packet)); + expect(gotData).to.equal(true); + for (var i = 0; i < 2; ++i) { + expect(messages[i]).to.equal('Hello'); + } + }); + + it('can parse empty message', function() { + var p = new Receiver(); + var packet = '00 ff'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('', data); + }; + + p.add(getBufferFromHexString(packet)); + expect(gotData).to.equal(true); + }); + + it('can parse text messages delivered over multiple frames', function() { + var p = new Receiver(); + var packets = [ + '00 48', + '65 6c 6c', + '6f ff 00 48', + '65', + '6c 6c 6f', + 'ff' + ]; + + var gotData = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + + for (var i = 0; i < packets.length; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotData).to.equal(true); + for (var i = 0; i < 2; ++i) { + expect(messages[i]).to.equal('Hello'); + } + }); + + it('emits an error if a payload doesnt start with 0x00', function() { + var p = new Receiver(); + var packets = [ + '00 6c ff', + '00 6c ff ff', + 'ff 00 6c ff 00 6c ff', + '00', + '6c 6c 6f', + 'ff' + ]; + + var gotData = false; + var gotError = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + p.onerror = function(reason, code) { + gotError = code == true; + }; + + for (var i = 0; i < packets.length && !gotError; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotError).to.equal(true); + expect(messages[0]).to.equal('l'); + expect(messages[1]).to.equal('l'); + expect(messages.length).to.equal(2); + }); + + it('can parse close messages', function() { + var p = new Receiver(); + var packets = [ + 'ff 00' + ]; + + var gotClose = false; + var gotError = false; + p.onclose = function() { + gotClose = true; + }; + p.onerror = function(reason, code) { + gotError = code == true; + }; + + for (var i = 0; i < packets.length && !gotError; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotClose).to.equal(true); + expect(gotError).to.equal(false); + }); + + it('can parse binary messages delivered over multiple frames', function() { + var p = new Receiver(); + var packets = [ + '80 05 48', + '65 6c 6c', + '6f 80 80 05 48', + '65', + '6c 6c 6f' + ]; + + var gotData = false; + var messages = []; + p.ontext = function(data) { + gotData = true; + messages.push(data); + }; + + for (var i = 0; i < packets.length; ++i) { + p.add(getBufferFromHexString(packets[i])); + } + expect(gotData).to.equal(true); + for (var i = 0; i < 2; ++i) { + expect(messages[i]).to.equal('Hello'); + } + }); +}); diff --git a/node_modules/ws/test/Receiver.test.js b/node_modules/ws/test/Receiver.test.js new file mode 100644 index 0000000..b0b5c0a --- /dev/null +++ b/node_modules/ws/test/Receiver.test.js @@ -0,0 +1,255 @@ +var assert = require('assert') + , Receiver = require('../lib/Receiver'); +require('should'); +require('./hybi-common'); + +describe('Receiver', function() { + it('can parse unmasked text message', function() { + var p = new Receiver(); + var packet = '81 05 48 65 6c 6c 6f'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('Hello', data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse close message', function() { + var p = new Receiver(); + var packet = '88 00'; + + var gotClose = false; + p.onclose = function(data) { + gotClose = true; + }; + + p.add(getBufferFromHexString(packet)); + gotClose.should.be.ok; + }); + it('can parse masked text message', function() { + 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'; + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal('5:::{"name":"echo"}', data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a masked text message longer than 125 bytes', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + var packet = '81 FE ' + pack(4, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a really long masked text message', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 64*1024; ++i) message += (i % 5).toString(); + var packet = '81 FF ' + pack(16, message.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a fragmented masked text message of 300 bytes', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + var msgpiece1 = message.substr(0, 150); + var msgpiece2 = message.substr(150); + var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); + var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet1)); + p.add(getBufferFromHexString(packet2)); + gotData.should.be.ok; + }); + it('can parse a ping message', function() { + var p = new Receiver(); + var message = 'Hello'; + var packet = '89 ' + getHybiLengthAsHexString(message.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotPing = false; + p.onping = function(data) { + gotPing = true; + assert.equal(message, data); + }; + + p.add(getBufferFromHexString(packet)); + gotPing.should.be.ok; + }); + it('can parse a ping with no data', function() { + var p = new Receiver(); + var packet = '89 00'; + + var gotPing = false; + p.onping = function(data) { + gotPing = true; + }; + + p.add(getBufferFromHexString(packet)); + gotPing.should.be.ok; + }); + it('can parse a fragmented masked text message of 300 bytes with a ping in the middle', function() { + var p = new Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + + var msgpiece1 = message.substr(0, 150); + var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); + + var pingMessage = 'Hello'; + var pingPacket = '89 ' + getHybiLengthAsHexString(pingMessage.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')); + + var msgpiece2 = message.substr(150); + var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + var gotPing = false; + p.onping = function(data) { + gotPing = true; + assert.equal(pingMessage, data); + }; + + p.add(getBufferFromHexString(packet1)); + p.add(getBufferFromHexString(pingPacket)); + p.add(getBufferFromHexString(packet2)); + gotData.should.be.ok; + gotPing.should.be.ok; + }); + 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 Receiver(); + var message = 'A'; + for (var i = 0; i < 300; ++i) message += (i % 5).toString(); + + var msgpiece1 = message.substr(0, 150); + var packet1 = '01 FE ' + pack(4, msgpiece1.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece1, '34 83 a8 68')); + + var pingMessage = 'Hello'; + var pingPacket = '89 ' + getHybiLengthAsHexString(pingMessage.length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(pingMessage, '34 83 a8 68')); + + var msgpiece2 = message.substr(150); + var packet2 = '80 FE ' + pack(4, msgpiece2.length) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(msgpiece2, '34 83 a8 68')); + + var gotData = false; + p.ontext = function(data) { + gotData = true; + assert.equal(message, data); + }; + var gotPing = false; + p.onping = function(data) { + gotPing = true; + assert.equal(pingMessage, data); + }; + + var buffers = []; + buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet1))); + buffers = buffers.concat(splitBuffer(getBufferFromHexString(pingPacket))); + buffers = buffers.concat(splitBuffer(getBufferFromHexString(packet2))); + for (var i = 0; i < buffers.length; ++i) { + p.add(buffers[i]); + } + gotData.should.be.ok; + gotPing.should.be.ok; + }); + it('can parse a 100 byte long masked binary message', function() { + var p = new Receiver(); + var length = 100; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a 256 byte long masked binary message', function() { + var p = new Receiver(); + var length = 256; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a 200kb long masked binary message', function() { + var p = new Receiver(); + var length = 200 * 1024; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, true) + ' 34 83 a8 68 ' + getHexStringFromBuffer(mask(message, '34 83 a8 68')); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); + it('can parse a 200kb long unmasked binary message', function() { + var p = new Receiver(); + var length = 200 * 1024; + var message = new Buffer(length); + for (var i = 0; i < length; ++i) message[i] = i % 256; + var originalMessage = getHexStringFromBuffer(message); + var packet = '82 ' + getHybiLengthAsHexString(length, false) + ' ' + getHexStringFromBuffer(message); + + var gotData = false; + p.onbinary = function(data) { + gotData = true; + assert.equal(originalMessage, getHexStringFromBuffer(data)); + }; + + p.add(getBufferFromHexString(packet)); + gotData.should.be.ok; + }); +}); + diff --git a/node_modules/ws/test/Sender.hixie.test.js b/node_modules/ws/test/Sender.hixie.test.js new file mode 100644 index 0000000..783f892 --- /dev/null +++ b/node_modules/ws/test/Sender.hixie.test.js @@ -0,0 +1,134 @@ +var assert = require('assert') + , Sender = require('../lib/Sender.hixie'); +require('should'); +require('./hybi-common'); + +describe('Sender', function() { + describe('#send', function() { + it('frames and sends a text message', function(done) { + var message = 'Hello world'; + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(message, {}, function() { + received.toString('utf8').should.eql('\u0000' + message + '\ufffd'); + done(); + }); + }); + + it('frames and sends an empty message', function(done) { + var socket = { + write: function(data, encoding, cb) { + done(); + } + }; + var sender = new Sender(socket, {}); + sender.send('', {}, function() {}); + }); + + it('frames and sends a buffer', function(done) { + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(new Buffer('foobar'), {}, function() { + received.toString('utf8').should.eql('\u0000foobar\ufffd'); + done(); + }); + }); + + it('frames and sends a binary message', function(done) { + var message = 'Hello world'; + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(message, {binary: true}, function() { + received.toString('hex').should.eql( + // 0x80 0x0b H e l l o <sp> w o r l d + '800b48656c6c6f20776f726c64'); + done(); + }); + }); +/* + it('throws an exception for binary data', function(done) { + var socket = { + write: function(data, encoding, cb) { + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.on('error', function() { + done(); + }); + sender.send(new Buffer(100), {binary: true}, function() {}); + }); +*/ + it('can fauxe stream data', function(done) { + var received = []; + var socket = { + write: function(data, encoding, cb) { + received.push(data); + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(new Buffer('foobar'), { fin: false }, function() {}); + sender.send('bazbar', { fin: false }, function() {}); + sender.send(new Buffer('end'), { fin: true }, function() { + received[0].toString('utf8').should.eql('\u0000foobar'); + received[1].toString('utf8').should.eql('bazbar'); + received[2].toString('utf8').should.eql('end\ufffd'); + done(); + }); + }); + }); + + describe('#close', function() { + it('sends a hixie close frame', function(done) { + var received; + var socket = { + write: function(data, encoding, cb) { + received = data; + process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.close(null, null, null, function() { + received.toString('utf8').should.eql('\ufffd\u0000'); + done(); + }); + }); + + it('sends a message end marker if fauxe streaming has started, before hixie close frame', function(done) { + var received = []; + var socket = { + write: function(data, encoding, cb) { + received.push(data); + if (cb) process.nextTick(cb); + } + }; + var sender = new Sender(socket, {}); + sender.send(new Buffer('foobar'), { fin: false }, function() {}); + sender.close(null, null, null, function() { + received[0].toString('utf8').should.eql('\u0000foobar'); + received[1].toString('utf8').should.eql('\ufffd'); + received[2].toString('utf8').should.eql('\ufffd\u0000'); + done(); + }); + }); + }); +}); diff --git a/node_modules/ws/test/Sender.test.js b/node_modules/ws/test/Sender.test.js new file mode 100644 index 0000000..43b4864 --- /dev/null +++ b/node_modules/ws/test/Sender.test.js @@ -0,0 +1,24 @@ +var Sender = require('../lib/Sender'); +require('should'); + +describe('Sender', function() { + describe('#frameAndSend', function() { + it('does not modify a masked binary buffer', function() { + var sender = new Sender({ write: function() {} }); + var buf = new Buffer([1, 2, 3, 4, 5]); + sender.frameAndSend(2, buf, true, true); + buf[0].should.eql(1); + buf[1].should.eql(2); + buf[2].should.eql(3); + buf[3].should.eql(4); + buf[4].should.eql(5); + }); + + it('does not modify a masked text buffer', function() { + var sender = new Sender({ write: function() {} }); + var text = 'hi there'; + sender.frameAndSend(1, text, true, true); + text.should.eql('hi there'); + }); + }); +}); diff --git a/node_modules/ws/test/Validation.test.js b/node_modules/ws/test/Validation.test.js new file mode 100644 index 0000000..37c3399 --- /dev/null +++ b/node_modules/ws/test/Validation.test.js @@ -0,0 +1,23 @@ +var Validation = require('../lib/Validation').Validation; +require('should'); + +describe('Validation', function() { + describe('isValidUTF8', function() { + it('should return true for a valid utf8 string', function() { + var validBuffer = new Buffer('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque gravida mattis rhoncus. Donec iaculis, metus quis varius accumsan, erat mauris condimentum diam, et egestas erat enim ut ligula. Praesent sollicitudin tellus eget dolor euismod euismod. Nullam ac augue nec neque varius luctus. Curabitur elit mi, consequat ultricies adipiscing mollis, scelerisque in erat. Phasellus facilisis fermentum ullamcorper. Nulla et sem eu arcu pharetra pellentesque. Praesent consectetur tempor justo, vel iaculis dui ullamcorper sit amet. Integer tristique viverra ullamcorper. Vivamus laoreet, nulla eget suscipit eleifend, lacus lectus feugiat libero, non fermentum erat nisi at risus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut pulvinar dignissim tellus, eu dignissim lorem vulputate quis. Morbi ut pulvinar augue.'); + Validation.isValidUTF8(validBuffer).should.be.ok; + }); + it('should return false for an erroneous string', function() { + var invalidBuffer = new Buffer([0xce, 0xba, 0xe1, 0xbd, 0xb9, 0xcf, 0x83, 0xce, 0xbc, 0xce, 0xb5, 0xed, 0xa0, 0x80, 0x65, 0x64, 0x69, 0x74, 0x65, 0x64]); + Validation.isValidUTF8(invalidBuffer).should.not.be.ok; + }); + it('should return true for valid cases from the autobahn test suite', function() { + Validation.isValidUTF8(new Buffer('\xf0\x90\x80\x80')).should.be.ok; + Validation.isValidUTF8(new Buffer([0xf0, 0x90, 0x80, 0x80])).should.be.ok; + }); + it('should return false for erroneous autobahn strings', function() { + Validation.isValidUTF8(new Buffer([0xce, 0xba, 0xe1, 0xbd])).should.not.be.ok; + }); + }); +}); + diff --git a/node_modules/ws/test/WebSocket.integration.js b/node_modules/ws/test/WebSocket.integration.js new file mode 100644 index 0000000..51a7e3a --- /dev/null +++ b/node_modules/ws/test/WebSocket.integration.js @@ -0,0 +1,42 @@ +var assert = require('assert') + , WebSocket = require('../') + , server = require('./testserver'); + +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() { + it('communicates successfully with echo service', function(done) { + var ws = new WebSocket('ws://echo.websocket.org', {protocolVersion: 8, origin: 'http://websocket.org'}); + var str = Date.now().toString(); + var dataReceived = false; + ws.on('open', function() { + ws.send(str, {mask: true}); + }); + ws.on('close', function() { + assert.equal(true, dataReceived); + done(); + }); + ws.on('message', function(data, flags) { + assert.equal(str, data); + ws.terminate(); + dataReceived = true; + }); + }); +});
\ No newline at end of file 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(); + }); + }); + }); + }); + }); +}); diff --git a/node_modules/ws/test/WebSocketServer.test.js b/node_modules/ws/test/WebSocketServer.test.js new file mode 100644 index 0000000..c21fd97 --- /dev/null +++ b/node_modules/ws/test/WebSocketServer.test.js @@ -0,0 +1,1103 @@ +var http = require('http') + , https = require('https') + , WebSocket = require('../') + , WebSocketServer = WebSocket.Server + , fs = require('fs') + , should = require('should'); + +var port = 8000; + +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('WebSocketServer', function() { + describe('#ctor', function() { + it('throws an error if no option object is passed', function() { + var gotException = false; + try { + var wss = new WebSocketServer(); + } + catch (e) { + gotException = true; + } + gotException.should.be.ok; + }); + + it('throws an error if no port or server is specified', function() { + var gotException = false; + try { + var wss = new WebSocketServer({}); + } + catch (e) { + gotException = true; + } + gotException.should.be.ok; + }); + + it('does not throw an error if no port or server is specified, when the noServer option is true', function() { + var gotException = false; + try { + var wss = new WebSocketServer({noServer: true}); + } + catch (e) { + gotException = true; + } + gotException.should.eql(false); + }); + + it('emits an error if http server bind fails', function(done) { + var wss = new WebSocketServer({port: 1}); + wss.on('error', function() { done(); }); + }); + + it('starts a server on a given port', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + wss.close(); + done(); + }); + }); + + it('uses a precreated http server', function (done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws://localhost:' + port); + + wss.on('connection', function(client) { + wss.close(); + srv.close(); + done(); + }); + }); + }); + + it('uses a precreated http server listening on unix socket', function (done) { + var srv = http.createServer(); + var sockPath = '/tmp/ws_socket_'+new Date().getTime()+'.'+Math.floor(Math.random() * 1000); + srv.listen(sockPath, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws+unix://'+sockPath); + + wss.on('connection', function(client) { + wss.close(); + srv.close(); + done(); + }); + }); + }); + + it('emits path specific connection event', function (done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws://localhost:' + port+'/endpointName'); + + wss.on('connection/endpointName', function(client) { + wss.close(); + srv.close(); + done(); + }); + }); + }); + + it('can have two different instances listening on the same http server with two different paths', function(done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss1 = new WebSocketServer({server: srv, path: '/wss1'}) + , wss2 = new WebSocketServer({server: srv, path: '/wss2'}); + var doneCount = 0; + wss1.on('connection', function(client) { + wss1.close(); + if (++doneCount == 2) { + srv.close(); + done(); + } + }); + wss2.on('connection', function(client) { + wss2.close(); + if (++doneCount == 2) { + srv.close(); + done(); + } + }); + var ws1 = new WebSocket('ws://localhost:' + port + '/wss1'); + var ws2 = new WebSocket('ws://localhost:' + port + '/wss2?foo=1'); + }); + }); + + it('cannot have two different instances listening on the same http server with the same path', function(done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss1 = new WebSocketServer({server: srv, path: '/wss1'}); + try { + var wss2 = new WebSocketServer({server: srv, path: '/wss1'}); + } + catch (e) { + wss1.close(); + srv.close(); + done(); + } + }); + }); + }); + + describe('#close', function() { + it('will close all clients', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('close', function() { + if (++closes == 2) done(); + }); + }); + var closes = 0; + wss.on('connection', function(client) { + client.on('close', function() { + if (++closes == 2) done(); + }); + wss.close(); + }); + }); + + it('does not close a precreated server', function(done) { + var srv = http.createServer(); + var realClose = srv.close; + srv.close = function() { + should.fail('must not close pre-created server'); + } + srv.listen(++port, function () { + var wss = new WebSocketServer({server: srv}); + var ws = new WebSocket('ws://localhost:' + port); + wss.on('connection', function(client) { + wss.close(); + srv.close = realClose; + srv.close(); + done(); + }); + }); + }); + + it('cleans up websocket data on a precreated server', function(done) { + var srv = http.createServer(); + srv.listen(++port, function () { + var wss1 = new WebSocketServer({server: srv, path: '/wss1'}) + , wss2 = new WebSocketServer({server: srv, path: '/wss2'}); + (typeof srv._webSocketPaths).should.eql('object'); + Object.keys(srv._webSocketPaths).length.should.eql(2); + wss1.close(); + Object.keys(srv._webSocketPaths).length.should.eql(1); + wss2.close(); + (typeof srv._webSocketPaths).should.eql('undefined'); + srv.close(); + done(); + }); + }); + }); + + 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('can be disabled', function(done) { + var wss = new WebSocketServer({port: ++port, clientTracking: false}, function() { + wss.clients.length.should.eql(0); + var ws = new WebSocket('ws://localhost:' + port); + }); + wss.on('connection', function(client) { + wss.clients.length.should.eql(0); + 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) { + 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(); + }); + }); + }); + }); + + 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 verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.origin.should.eql('http://foobarbaz.com'); + verifyClientCalled = true; + 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) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('verifyClient gets original request', function(done) { + var verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.req.headers['sec-websocket-key'].should.eql('dGhlIHNhbXBsZSBub25jZQ=='); + verifyClientCalled = true; + 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) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('verifyClient has secure:true for ssl connections', 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 success = false; + var wss = new WebSocketServer({ + server: app, + verifyClient: function(info) { + success = info.secure === true; + return true; + } + }); + 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(); + }); + }); + + 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('client can be denied asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + process.nextTick(function() { + cb(false); + }); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 8, + 'Sec-WebSocket-Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be accepted asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + process.nextTick(function() { + cb(true); + }); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Origin': 'http://foobar.com' + } + }; + var req = http.request(options); + req.end(); + }); + wss.on('connection', function(ws) { + ws.terminate(); + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('handles messages passed along with the upgrade request (upgrade head)', function(done) { + 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() {}); + }); + + it('selects the first protocol by default', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + ws.protocol.should.eql('prot1'); + wss.close(); + done(); + }); + }); + }); + + it('selects the last protocol via protocol handler', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(true, ps[ps.length-1]); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + ws.protocol.should.eql('prot2'); + wss.close(); + done(); + }); + }); + }); + + it('client detects invalid server protocol', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(true, 'prot3'); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + done(new Error('connection must not be established')); + }); + ws.on('error', function() { + done(); + }); + }); + }); + + it('client detects no server protocol', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(true); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + done(new Error('connection must not be established')); + }); + ws.on('error', function() { + done(); + }); + }); + }); + + it('client refuses server protocols', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + cb(false); }}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'prot1, prot2'}); + ws.on('open', function(client) { + done(new Error('connection must not be established')); + }); + ws.on('error', function() { + done(); + }); + }); + }); + + it('server detects invalid protocol handler', function(done) { + var wss = new WebSocketServer({port: ++port, handleProtocols: function(ps, cb) { + // not calling callback is an error and shouldn't timeout + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'websocket', + 'Sec-WebSocket-Key': 'dGhlIHNhbXBsZSBub25jZQ==', + 'Sec-WebSocket-Version': 13, + 'Sec-WebSocket-Origin': 'http://foobar.com' + } + }; + options.port = port; + var req = http.request(options); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(501); + wss.close(); + done(); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + }); + + describe('messaging', function() { + it('can send and receive data', function(done) { + var data = new Array(65*1024); + for (var i = 0; i < data.length; ++i) { + data[i] = String.fromCharCode(65 + ~~(25 * Math.random())); + } + data = data.join(''); + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port); + ws.on('message', function(message, flags) { + ws.send(message); + }); + }); + wss.on('connection', function(client) { + client.on('message', function(message) { + message.should.eql(data); + wss.close(); + done(); + }); + client.send(data); + }); + }); + }); + }); + + describe('hixie mode', function() { + it('can be disabled', function(done) { + var wss = new WebSocketServer({port: ++port, disableHixie: 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(); + 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() {}); + }); + + 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 verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.origin.should.eql('http://foobarbaz.com'); + verifyClientCalled = true; + 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) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('verifyClient gets original request', function(done) { + var verifyClientCalled = false; + var wss = new WebSocketServer({port: ++port, verifyClient: function(info) { + info.req.headers['sec-websocket-key1'].should.eql('3e6b263 4 17 80'); + verifyClientCalled = true; + 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) { + verifyClientCalled.should.be.ok; + wss.close(); + done(); + }); + }); + wss.on('error', function() {}); + }); + + it('client can be denied asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + cb(false); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Origin': 'http://foobarbaz.com', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + req.on('response', function(res) { + res.statusCode.should.eql(401); + process.nextTick(function() { + wss.close(); + done(); + }); + }); + }); + wss.on('connection', function(ws) { + done(new Error('connection must not be established')); + }); + wss.on('error', function() {}); + }); + + it('client can be accepted asynchronously', function(done) { + var wss = new WebSocketServer({port: ++port, verifyClient: function(o, cb) { + cb(true); + }}, function() { + var options = { + port: port, + host: '127.0.0.1', + headers: { + 'Connection': 'Upgrade', + 'Upgrade': 'WebSocket', + 'Origin': 'http://foobarbaz.com', + 'Sec-WebSocket-Key1': '3e6b263 4 17 80', + 'Sec-WebSocket-Key2': '17 9 G`ZD9 2 2b 7X 3 /r90' + } + }; + var req = http.request(options); + req.write('WjN}|M(6'); + req.end(); + }); + wss.on('connection', function(ws) { + wss.close(); + done(); + }); + wss.on('error', function() {}); + }); + + it('handles messages passed along with the upgrade request (upgrade head)', function(done) { + 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() {}); + }); + }); + }); + + describe('client properties', function() { + it('protocol is exposed', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocol: 'hi'}); + }); + wss.on('connection', function(client) { + client.protocol.should.eql('hi'); + wss.close(); + done(); + }); + }); + + it('protocolVersion is exposed', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8}); + }); + wss.on('connection', function(client) { + client.protocolVersion.should.eql(8); + wss.close(); + done(); + }); + }); + + it('upgradeReq is the original request object', function(done) { + var wss = new WebSocketServer({port: ++port}, function() { + var ws = new WebSocket('ws://localhost:' + port, {protocolVersion: 8}); + }); + wss.on('connection', function(client) { + client.upgradeReq.httpVersion.should.eql('1.1'); + wss.close(); + done(); + }); + }); + }); + +}); diff --git a/node_modules/ws/test/autobahn-server.js b/node_modules/ws/test/autobahn-server.js new file mode 100644 index 0000000..36fe0c2 --- /dev/null +++ b/node_modules/ws/test/autobahn-server.js @@ -0,0 +1,29 @@ +var WebSocketServer = require('../').Server; + +process.on('uncaughtException', function(err) { + console.log('Caught exception: ', err, err.stack); +}); + +process.on('SIGINT', function () { + try { + console.log('Updating reports and shutting down'); + var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws'); + ws.on('close', function() { + process.exit(); + }); + } + catch(e) { + process.exit(); + } +}); + +var wss = new WebSocketServer({port: 8181}); +wss.on('connection', function(ws) { + console.log('new connection'); + ws.on('message', function(data, flags) { + ws.send(flags.buffer, {binary: flags.binary === true}); + }); + ws.on('error', function() { + console.log('error', arguments); + }); +}); diff --git a/node_modules/ws/test/autobahn.js b/node_modules/ws/test/autobahn.js new file mode 100644 index 0000000..048cc90 --- /dev/null +++ b/node_modules/ws/test/autobahn.js @@ -0,0 +1,52 @@ +var WebSocket = require('../'); +var currentTest = 1; +var lastTest = -1; +var testCount = null; + +process.on('uncaughtException', function(err) { + console.log('Caught exception: ', err, err.stack); +}); + +process.on('SIGINT', function () { + try { + console.log('Updating reports and shutting down'); + var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws'); + ws.on('close', function() { + process.exit(); + }); + } + catch(e) { + process.exit(); + } +}); + +function nextTest() { + if (currentTest > testCount || (lastTest != -1 && currentTest > lastTest)) { + console.log('Updating reports and shutting down'); + var ws = new WebSocket('ws://localhost:9001/updateReports?agent=ws'); + ws.on('close', function() { + process.exit(); + }); + return; + }; + console.log('Running test case ' + currentTest + '/' + testCount); + var ws = new WebSocket('ws://localhost:9001/runCase?case=' + currentTest + '&agent=ws'); + ws.on('message', function(data, flags) { + ws.send(flags.buffer, {binary: flags.binary === true, mask: true}); + }); + ws.on('close', function(data) { + currentTest += 1; + process.nextTick(nextTest); + }); + ws.on('error', function(e) {}); +} + +var ws = new WebSocket('ws://localhost:9001/getCaseCount'); +ws.on('message', function(data, flags) { + testCount = parseInt(data); +}); +ws.on('close', function() { + if (testCount > 0) { + nextTest(); + } +});
\ No newline at end of file diff --git a/node_modules/ws/test/fixtures/agent1-cert.pem b/node_modules/ws/test/fixtures/agent1-cert.pem new file mode 100644 index 0000000..cccb9fb --- /dev/null +++ b/node_modules/ws/test/fixtures/agent1-cert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICbjCCAdcCCQCVvok5oeLpqzANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV +UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO +BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA +dGlueWNsb3Vkcy5vcmcwHhcNMTMwMzA4MDAzMDIyWhcNNDAwNzIzMDAzMDIyWjB9 +MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK +EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDzANBgNVBAMTBmFnZW50MTEgMB4G +CSqGSIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQAD +gY0AMIGJAoGBAL6GwKosYb0Yc3Qo0OtQVlCJ4208Idw11ij+t2W5sfYbCil5tyQo +jnhGM1CJhEXynQpXXwjKJuIeTQCkeUibTyFKa0bs8+li2FiGoKYbb4G81ovnqkmE +2iDVb8Gw3rrM4zeZ0ZdFnjMsAZac8h6+C4sB/pS9BiMOo6qTl15RQlcJAgMBAAEw +DQYJKoZIhvcNAQEFBQADgYEAOtmLo8DwTPnI4wfQbQ3hWlTS/9itww6IsxH2ODt9 +ggB7wi7N3uAdIWRZ54ke0NEAO5CW1xNTwsWcxQbiHrDOqX1vfVCjIenI76jVEEap +/Ay53ydHNBKdsKkib61Me14Mu0bA3lUul57VXwmH4NUEFB3w973Q60PschUhOEXj +7DY= +-----END CERTIFICATE----- diff --git a/node_modules/ws/test/fixtures/agent1-key.pem b/node_modules/ws/test/fixtures/agent1-key.pem new file mode 100644 index 0000000..cbd5f0c --- /dev/null +++ b/node_modules/ws/test/fixtures/agent1-key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC+hsCqLGG9GHN0KNDrUFZQieNtPCHcNdYo/rdlubH2Gwopebck +KI54RjNQiYRF8p0KV18IyibiHk0ApHlIm08hSmtG7PPpYthYhqCmG2+BvNaL56pJ +hNog1W/BsN66zOM3mdGXRZ4zLAGWnPIevguLAf6UvQYjDqOqk5deUUJXCQIDAQAB +AoGANu/CBA+SCyVOvRK70u4yRTzNMAUjukxnuSBhH1rg/pajYnwvG6T6F6IeT72n +P0gKkh3JUE6B0bds+p9yPUZTFUXghxjcF33wlIY44H6gFE4K5WutsFJ9c450wtuu +8rXZTsIg7lAXWjTFVmdtOEPetcGlO2Hpi1O7ZzkzHgB2w9ECQQDksCCYx78or1zY +ZSokm8jmpIjG3VLKdvI9HAoJRN40ldnwFoigrFa1AHwsFtWNe8bKyVRPDoLDUjpB +dkPWgweVAkEA1UfgqguQ2KIkbtp9nDBionu3QaajksrRHwIa8vdfRfLxszfHk2fh +NGY3dkRZF8HUAbzYLrd9poVhCBAEjWekpQJASOM6AHfpnXYHCZF01SYx6hEW5wsz +kARJQODm8f1ZNTlttO/5q/xBxn7ZFNRSTD3fJlL05B2j380ddC/Vf1FT4QJAP1BC +GliqnBSuGhZUWYxni3KMeTm9rzL0F29pjpzutHYlWB2D6ndY/FQnvL0XcZ0Bka58 +womIDGnl3x3aLBwLXQJBAJv6h5CHbXHx7VyDJAcNfppAqZGcEaiVg8yf2F33iWy2 +FLthhJucx7df7SO2aw5h06bRDRAhb9br0R9/3mLr7RE= +-----END RSA PRIVATE KEY----- diff --git a/node_modules/ws/test/fixtures/ca1-cert.pem b/node_modules/ws/test/fixtures/ca1-cert.pem new file mode 100644 index 0000000..1d0c0d6 --- /dev/null +++ b/node_modules/ws/test/fixtures/ca1-cert.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICazCCAdQCCQC9/g69HtxXRzANBgkqhkiG9w0BAQUFADB6MQswCQYDVQQGEwJV +UzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQKEwZKb3llbnQxEDAO +BgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqGSIb3DQEJARYRcnlA +dGlueWNsb3Vkcy5vcmcwHhcNMTMwMzA4MDAzMDIyWhcNNDAwNzIzMDAzMDIyWjB6 +MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExCzAJBgNVBAcTAlNGMQ8wDQYDVQQK +EwZKb3llbnQxEDAOBgNVBAsTB05vZGUuanMxDDAKBgNVBAMTA2NhMTEgMB4GCSqG +SIb3DQEJARYRcnlAdGlueWNsb3Vkcy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0A +MIGJAoGBAKxr1mARUcv7zaqx5y4AxJPK6c1jdbSg7StcL4vg8klaPAlfNO6o+/Cl +w5CdQD3ukaVUwUOJ4T/+b3Xf7785XcWBC33GdjVQkfbHATJYcka7j7JDw3qev5Jk +1rAbRw48hF6rYlSGcx1mccAjoLoa3I8jgxCNAYHIjUQXgdmU893rAgMBAAEwDQYJ +KoZIhvcNAQEFBQADgYEAis05yxjCtJRuv8uX/DK6TX/j9C9Lzp1rKDNFTaTZ0iRw +KCw1EcNx4OXSj9gNblW4PWxpDvygrt1AmH9h2cb8K859NSHa9JOBFw6MA5C2A4Sj +NQfNATqUl4T6cdORlcDEZwHtT8b6D4A6Er31G/eJF4Sen0TUFpjdjd+l9RBjHlo= +-----END CERTIFICATE----- diff --git a/node_modules/ws/test/fixtures/ca1-key.pem b/node_modules/ws/test/fixtures/ca1-key.pem new file mode 100644 index 0000000..df14950 --- /dev/null +++ b/node_modules/ws/test/fixtures/ca1-key.pem @@ -0,0 +1,17 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIFeWxJE1BrRECAggA +MBQGCCqGSIb3DQMHBAgu9PlMSQ+BOASCAoDEZN2tX0xWo/N+Jg+PrvCrFDk3P+3x +5xG/PEDjtMCAWPBEwbnaYHDzYmhNcAmxzGqEHGMDiWYs46LbO560VS3uMvFbEWPo +KYYVb13vkxl2poXdonCb5cHZA5GUYzTIVVJFptl4LHwBczHoMHtA4FqAhKlYvlWw +EOrdLB8XcwMmGPFabbbGxno0+EWWM27uNjlogfoxj35mQqSW4rOlhZ460XjOB1Zx +LjXMuZeONojkGYQRG5EUMchBoctQpCOM6cAi9r1B9BvtFCBpDV1c1zEZBzTEUd8o +kLn6tjLmY+QpTdylFjEWc7U3ppLY/pkoTBv4r85a2sEMWqkhSJboLaTboWzDJcU3 +Ke61pMpovt/3yCUd3TKgwduVwwQtDVTlBe0p66aN9QVj3CrFy/bKAGO3vxlli24H +aIjZf+OVoBY21ESlW3jLvNlBf7Ezf///2E7j4SCDLyZSFMTpFoAG/jDRyvi+wTKX +Kh485Bptnip6DCSuoH4u2SkOqwz3gJS/6s02YKe4m311QT4Pzne5/FwOFaS/HhQg +Xvyh2/d00OgJ0Y0PYQsHILPRgTUCKUXvj1O58opn3fxSacsPxIXwj6Z4FYAjUTaV +2B85k1lpant/JJEilDqMjqzx4pHZ/Z3Uto1lSM1JZs9SNL/0UR+6F0TXZTULVU9V +w8jYzz4sPr7LEyrrTbzmjQgnQFVbhAN/eKgRZK/SpLjxpmBV5MfpbPKsPUZqT4UC +4nXa8a/NYUQ9e+QKK8enq9E599c2W442W7Z1uFRZTWReMx/lF8wwA6G8zOPG0bdj +d+T5Gegzd5mvRiXMBklCo8RLxOOvgxun1n3PY4a63aH6mqBhdfhiLp5j +-----END ENCRYPTED PRIVATE KEY----- diff --git a/node_modules/ws/test/fixtures/certificate.pem b/node_modules/ws/test/fixtures/certificate.pem new file mode 100644 index 0000000..0efc2ef --- /dev/null +++ b/node_modules/ws/test/fixtures/certificate.pem @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICATCCAWoCCQDPufXH86n2QzANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJu +bzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTEyMDEwMTE0NDQwMFoXDTIwMDMxOTE0NDQwMFowRTELMAkG +A1UEBhMCbm8xEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtrQ7 ++r//2iV/B6F+4boH0XqFn7alcV9lpjvAmwRXNKnxAoa0f97AjYPGNLKrjpkNXXhB +JROIdbRbZnCNeC5fzX1a+JCo7KStzBXuGSZr27TtFmcV4H+9gIRIcNHtZmJLnxbJ +sIhkGR8yVYdmJZe4eT5ldk1zoB1adgPF1hZhCBMCAwEAATANBgkqhkiG9w0BAQUF +AAOBgQCeWBEHYJ4mCB5McwSSUox0T+/mJ4W48L/ZUE4LtRhHasU9hiW92xZkTa7E +QLcoJKQiWfiLX2ysAro0NX4+V8iqLziMqvswnPzz5nezaOLE/9U/QvH3l8qqNkXu +rNbsW1h/IO6FV8avWFYVFoutUwOaZ809k7iMh2F2JMgXQ5EymQ== +-----END CERTIFICATE----- diff --git a/node_modules/ws/test/fixtures/key.pem b/node_modules/ws/test/fixtures/key.pem new file mode 100644 index 0000000..176fe32 --- /dev/null +++ b/node_modules/ws/test/fixtures/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC2tDv6v//aJX8HoX7hugfReoWftqVxX2WmO8CbBFc0qfEChrR/ +3sCNg8Y0squOmQ1deEElE4h1tFtmcI14Ll/NfVr4kKjspK3MFe4ZJmvbtO0WZxXg +f72AhEhw0e1mYkufFsmwiGQZHzJVh2Yll7h5PmV2TXOgHVp2A8XWFmEIEwIDAQAB +AoGAAlVY8sHi/aE+9xT77twWX3mGHV0SzdjfDnly40fx6S1Gc7bOtVdd9DC7pk6l +3ENeJVR02IlgU8iC5lMHq4JEHPE272jtPrLlrpWLTGmHEqoVFv9AITPqUDLhB9Kk +Hjl7h8NYBKbr2JHKICr3DIPKOT+RnXVb1PD4EORbJ3ooYmkCQQDfknUnVxPgxUGs +ouABw1WJIOVgcCY/IFt4Ihf6VWTsxBgzTJKxn3HtgvE0oqTH7V480XoH0QxHhjLq +DrgobWU9AkEA0TRJ8/ouXGnFEPAXjWr9GdPQRZ1Use2MrFjneH2+Sxc0CmYtwwqL +Kr5kS6mqJrxprJeluSjBd+3/ElxURrEXjwJAUvmlN1OPEhXDmRHd92mKnlkyKEeX +OkiFCiIFKih1S5Y/sRJTQ0781nyJjtJqO7UyC3pnQu1oFEePL+UEniRztQJAMfav +AtnpYKDSM+1jcp7uu9BemYGtzKDTTAYfoiNF42EzSJiGrWJDQn4eLgPjY0T0aAf/ +yGz3Z9ErbhMm/Ysl+QJBAL4kBxRT8gM4ByJw4sdOvSeCCANFq8fhbgm8pGWlCPb5 +JGmX3/GHFM8x2tbWMGpyZP1DLtiNEFz7eCGktWK5rqE= +-----END RSA PRIVATE KEY----- diff --git a/node_modules/ws/test/fixtures/request.pem b/node_modules/ws/test/fixtures/request.pem new file mode 100644 index 0000000..51bc7f6 --- /dev/null +++ b/node_modules/ws/test/fixtures/request.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIBhDCB7gIBADBFMQswCQYDVQQGEwJubzETMBEGA1UECAwKU29tZS1TdGF0ZTEh +MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEB +AQUAA4GNADCBiQKBgQC2tDv6v//aJX8HoX7hugfReoWftqVxX2WmO8CbBFc0qfEC +hrR/3sCNg8Y0squOmQ1deEElE4h1tFtmcI14Ll/NfVr4kKjspK3MFe4ZJmvbtO0W +ZxXgf72AhEhw0e1mYkufFsmwiGQZHzJVh2Yll7h5PmV2TXOgHVp2A8XWFmEIEwID +AQABoAAwDQYJKoZIhvcNAQEFBQADgYEAjsUXEARgfxZNkMjuUcudgU2w4JXS0gGI +JQ0U1LmU0vMDSKwqndMlvCbKzEgPbJnGJDI8D4MeINCJHa5Ceyb8c+jaJYUcCabl +lQW5Psn3+eWp8ncKlIycDRj1Qk615XuXtV0fhkrgQM2ZCm9LaQ1O1Gd/CzLihLjF +W0MmgMKMMRk= +-----END CERTIFICATE REQUEST----- diff --git a/node_modules/ws/test/fixtures/textfile b/node_modules/ws/test/fixtures/textfile new file mode 100644 index 0000000..a10483b --- /dev/null +++ b/node_modules/ws/test/fixtures/textfile @@ -0,0 +1,9 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam egestas, massa at aliquam luctus, sapien erat viverra elit, nec pulvinar turpis eros sagittis urna. Pellentesque imperdiet tempor varius. Pellentesque blandit, ipsum in imperdiet venenatis, mi elit faucibus odio, id condimentum ante enim sed lectus. Aliquam et odio non odio pellentesque pulvinar. Vestibulum a erat dolor. Integer pretium risus sit amet nisl volutpat nec venenatis magna egestas. Ut bibendum felis eu tellus laoreet eleifend. Nam pulvinar auctor tortor, eu iaculis leo vestibulum quis. In euismod risus ac purus vehicula et fermentum ligula consectetur. Vivamus condimentum tempus lacinia. + +Curabitur sodales condimentum urna id dictum. Sed quis justo sit amet quam ultrices tincidunt vel laoreet nulla. Nullam quis ipsum sed nisi mollis bibendum at sit amet nisi. Donec laoreet consequat velit sit amet mollis. Nam sed sapien a massa iaculis dapibus. Sed dui nunc, ultricies et pellentesque ullamcorper, aliquet vitae ligula. Integer eu velit in neque iaculis venenatis. Ut rhoncus cursus est, ac dignissim leo vehicula a. Nulla ullamcorper vulputate mauris id blandit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque eleifend, nisi a tempor sollicitudin, odio massa pretium urna, quis congue sapien elit at tortor. Curabitur ipsum orci, vehicula non commodo molestie, laoreet id enim. Pellentesque convallis ultrices congue. Pellentesque nec iaculis lorem. In sagittis pharetra ipsum eget sodales. + +Fusce id nulla odio. Nunc nibh justo, placerat vel tincidunt sed, ornare et enim. Nulla vel urna vel ante commodo bibendum in vitae metus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis erat nunc, semper eget sagittis sit amet, ullamcorper eget lacus. Donec hendrerit ipsum vitae eros vestibulum eu gravida neque tincidunt. Ut molestie lacinia nulla. Donec mattis odio at magna egestas at pellentesque eros accumsan. Praesent interdum sem sit amet nibh commodo dignissim. Duis laoreet, enim ultricies fringilla suscipit, enim libero cursus nulla, sollicitudin adipiscing erat velit ut dui. Nulla eleifend mauris at velit fringilla a molestie lorem venenatis. + +Donec sit amet scelerisque metus. Cras ac felis a nulla venenatis vulputate. Duis porttitor eros ac neque rhoncus eget aliquet neque egestas. Quisque sed nunc est, vitae dapibus quam. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; In vehicula, est vitae posuere ultricies, diam purus pretium sapien, nec rhoncus dolor nisl eget arcu. Aliquam et nisi vitae risus tincidunt auctor. In vehicula, erat a cursus adipiscing, lorem orci congue est, nec ultricies elit dui in nunc. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + +Duis congue tempus elit sit amet auctor. Duis dignissim, risus ut sollicitudin ultricies, dolor ligula gravida odio, nec congue orci purus ut ligula. Fusce pretium dictum lectus at volutpat. Sed non auctor mauris. Etiam placerat vestibulum massa id blandit. Quisque consequat lacus ut nulla euismod facilisis. Sed aliquet ipsum nec mi imperdiet viverra. Pellentesque ullamcorper, lectus nec varius gravida, odio justo cursus risus, eu sagittis metus arcu quis felis. Phasellus consectetur vehicula libero, at condimentum orci euismod vel. Nunc purus tortor, suscipit nec fringilla nec, vulputate et nibh. Nam porta vehicula neque. Praesent porttitor, sapien eu auctor euismod, arcu quam elementum urna, sed hendrerit magna augue sed quam.
\ No newline at end of file diff --git a/node_modules/ws/test/hybi-common.js b/node_modules/ws/test/hybi-common.js new file mode 100644 index 0000000..006f9c6 --- /dev/null +++ b/node_modules/ws/test/hybi-common.js @@ -0,0 +1,99 @@ +/** + * Returns a Buffer from a "ff 00 ff"-type hex string. + */ + +getBufferFromHexString = function(byteStr) { + var bytes = byteStr.split(' '); + var buf = new Buffer(bytes.length); + for (var i = 0; i < bytes.length; ++i) { + buf[i] = parseInt(bytes[i], 16); + } + return buf; +} + +/** + * Returns a hex string from a Buffer. + */ + +getHexStringFromBuffer = function(data) { + var s = ''; + for (var i = 0; i < data.length; ++i) { + s += padl(data[i].toString(16), 2, '0') + ' '; + } + return s.trim(); +} + +/** + * Splits a buffer in two parts. + */ + +splitBuffer = function(buffer) { + var b1 = new Buffer(Math.ceil(buffer.length / 2)); + buffer.copy(b1, 0, 0, b1.length); + var b2 = new Buffer(Math.floor(buffer.length / 2)); + buffer.copy(b2, 0, b1.length, b1.length + b2.length); + return [b1, b2]; +} + +/** + * Performs hybi07+ type masking on a hex string or buffer. + */ + +mask = function(buf, maskString) { + if (typeof buf == 'string') buf = new Buffer(buf); + var mask = getBufferFromHexString(maskString || '34 83 a8 68'); + for (var i = 0; i < buf.length; ++i) { + buf[i] ^= mask[i % 4]; + } + return buf; +} + +/** + * Returns a hex string representing the length of a message + */ + +getHybiLengthAsHexString = function(len, masked) { + if (len < 126) { + var buf = new Buffer(1); + buf[0] = (masked ? 0x80 : 0) | len; + } + else if (len < 65536) { + var buf = new Buffer(3); + buf[0] = (masked ? 0x80 : 0) | 126; + getBufferFromHexString(pack(4, len)).copy(buf, 1); + } + else { + var buf = new Buffer(9); + buf[0] = (masked ? 0x80 : 0) | 127; + getBufferFromHexString(pack(16, len)).copy(buf, 1); + } + return getHexStringFromBuffer(buf); +} + +/** + * Unpacks a Buffer into a number. + */ + +unpack = function(buffer) { + var n = 0; + for (var i = 0; i < buffer.length; ++i) { + n = (i == 0) ? buffer[i] : (n * 256) + buffer[i]; + } + return n; +} + +/** + * Returns a hex string, representing a specific byte count 'length', from a number. + */ + +pack = function(length, number) { + return padl(number.toString(16), length, '0').replace(/([0-9a-f][0-9a-f])/gi, '$1 ').trim(); +} + +/** + * Left pads the string 's' to a total length of 'n' with char 'c'. + */ + +padl = function(s, n, c) { + return new Array(1 + n - s.length).join(c) + s; +} diff --git a/node_modules/ws/test/testserver.js b/node_modules/ws/test/testserver.js new file mode 100644 index 0000000..3e7a966 --- /dev/null +++ b/node_modules/ws/test/testserver.js @@ -0,0 +1,180 @@ +var http = require('http') + , util = require('util') + , crypto = require('crypto') + , events = require('events') + , Sender = require('../lib/Sender') + , Receiver = require('../lib/Receiver'); + +module.exports = { + handlers: { + valid: validServer, + invalidKey: invalidRequestHandler, + closeAfterConnect: closeAfterConnectHandler, + return401: return401 + }, + createServer: function(port, handler, cb) { + if (handler && !cb) { + cb = handler; + handler = null; + } + var webServer = http.createServer(function (req, res) { + res.writeHead(200, {'Content-Type': 'text/plain'}); + res.end('okay'); + }); + var srv = new Server(webServer); + webServer.on('upgrade', function(req, socket) { + webServer._socket = socket; + (handler || validServer)(srv, req, socket); + }); + webServer.listen(port, '127.0.0.1', function() { cb(srv); }); + } +}; + +/** + * Test strategies + */ + +function validServer(server, req, socket) { + if (typeof req.headers.upgrade === 'undefined' || + req.headers.upgrade.toLowerCase() !== 'websocket') { + throw new Error('invalid headers'); + return; + } + + if (!req.headers['sec-websocket-key']) { + socket.end(); + throw new Error('websocket key is missing'); + } + + // calc key + var key = req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.setTimeout(0); + socket.setNoDelay(true); + + var sender = new Sender(socket); + var receiver = new Receiver(); + receiver.ontext = function (message, flags) { + server.emit('message', message, flags); + sender.send(message); + }; + receiver.onbinary = function (message, flags) { + flags = flags || {}; + flags.binary = true; + server.emit('message', message, flags); + sender.send(message, {binary: true}); + }; + receiver.onping = function (message, flags) { + flags = flags || {}; + server.emit('ping', message, flags); + }; + receiver.onpong = function (message, flags) { + flags = flags || {}; + server.emit('pong', message, flags); + }; + receiver.onclose = function (code, message, flags) { + flags = flags || {}; + server.emit('close', code, message, flags); + }; + socket.on('data', function (data) { + receiver.add(data); + }); + socket.on('end', function() { + socket.end(); + }); +} + +function invalidRequestHandler(server, req, socket) { + if (typeof req.headers.upgrade === 'undefined' || + req.headers.upgrade.toLowerCase() !== 'websocket') { + throw new Error('invalid headers'); + return; + } + + if (!req.headers['sec-websocket-key']) { + socket.end(); + throw new Error('websocket key is missing'); + } + + // calc key + var key = req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "bogus"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.end(); +} + +function closeAfterConnectHandler(server, req, socket) { + if (typeof req.headers.upgrade === 'undefined' || + req.headers.upgrade.toLowerCase() !== 'websocket') { + throw new Error('invalid headers'); + return; + } + + if (!req.headers['sec-websocket-key']) { + socket.end(); + throw new Error('websocket key is missing'); + } + + // calc key + var key = req.headers['sec-websocket-key']; + var shasum = crypto.createHash('sha1'); + shasum.update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + key = shasum.digest('base64'); + + var headers = [ + 'HTTP/1.1 101 Switching Protocols' + , 'Upgrade: websocket' + , 'Connection: Upgrade' + , 'Sec-WebSocket-Accept: ' + key + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.end(); +} + + +function return401(server, req, socket) { + var headers = [ + 'HTTP/1.1 401 Unauthorized' + , 'Content-type: text/html' + ]; + + socket.write(headers.concat('', '').join('\r\n')); + socket.end(); +} + +/** + * Server object, which will do the actual emitting + */ + +function Server(webServer) { + this.webServer = webServer; +} + +util.inherits(Server, events.EventEmitter); + +Server.prototype.close = function() { + this.webServer.close(); + if (this._socket) this._socket.end(); +} |
