summaryrefslogtreecommitdiffstats
path: root/testing/xpcshell/node-ws/test/sender.test.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--testing/xpcshell/node-ws/test/sender.test.js370
1 files changed, 370 insertions, 0 deletions
diff --git a/testing/xpcshell/node-ws/test/sender.test.js b/testing/xpcshell/node-ws/test/sender.test.js
new file mode 100644
index 0000000000..532239fa1a
--- /dev/null
+++ b/testing/xpcshell/node-ws/test/sender.test.js
@@ -0,0 +1,370 @@
+'use strict';
+
+const assert = require('assert');
+
+const extension = require('../lib/extension');
+const PerMessageDeflate = require('../lib/permessage-deflate');
+const Sender = require('../lib/sender');
+const { EMPTY_BUFFER } = require('../lib/constants');
+
+class MockSocket {
+ constructor({ write } = {}) {
+ this.readable = true;
+ this.writable = true;
+
+ if (write) this.write = write;
+ }
+
+ cork() {}
+ write() {}
+ uncork() {}
+}
+
+describe('Sender', () => {
+ describe('.frame', () => {
+ it('does not mutate the input buffer if data is `readOnly`', () => {
+ const buf = Buffer.from([1, 2, 3, 4, 5]);
+
+ Sender.frame(buf, {
+ readOnly: true,
+ rsv1: false,
+ mask: true,
+ opcode: 2,
+ fin: true
+ });
+
+ assert.ok(buf.equals(Buffer.from([1, 2, 3, 4, 5])));
+ });
+
+ it('honors the `rsv1` option', () => {
+ const list = Sender.frame(EMPTY_BUFFER, {
+ readOnly: false,
+ mask: false,
+ rsv1: true,
+ opcode: 1,
+ fin: true
+ });
+
+ assert.strictEqual(list[0][0] & 0x40, 0x40);
+ });
+
+ it('accepts a string as first argument', () => {
+ const list = Sender.frame('€', {
+ readOnly: false,
+ rsv1: false,
+ mask: false,
+ opcode: 1,
+ fin: true
+ });
+
+ assert.deepStrictEqual(list[0], Buffer.from('8103', 'hex'));
+ assert.deepStrictEqual(list[1], Buffer.from('e282ac', 'hex'));
+ });
+ });
+
+ describe('#send', () => {
+ it('compresses data if compress option is enabled', (done) => {
+ const chunks = [];
+ const perMessageDeflate = new PerMessageDeflate();
+ const mockSocket = new MockSocket({
+ write: (chunk) => {
+ chunks.push(chunk);
+ if (chunks.length !== 6) return;
+
+ assert.strictEqual(chunks[0].length, 2);
+ assert.strictEqual(chunks[0][0] & 0x40, 0x40);
+
+ assert.strictEqual(chunks[2].length, 2);
+ assert.strictEqual(chunks[2][0] & 0x40, 0x40);
+
+ assert.strictEqual(chunks[4].length, 2);
+ assert.strictEqual(chunks[4][0] & 0x40, 0x40);
+ done();
+ }
+ });
+ const sender = new Sender(mockSocket, {
+ 'permessage-deflate': perMessageDeflate
+ });
+
+ perMessageDeflate.accept([{}]);
+
+ const options = { compress: true, fin: true };
+ const array = new Uint8Array([0x68, 0x69]);
+
+ sender.send(array.buffer, options);
+ sender.send(array, options);
+ sender.send('hi', options);
+ });
+
+ describe('when context takeover is disabled', () => {
+ it('honors the compression threshold', (done) => {
+ const chunks = [];
+ const perMessageDeflate = new PerMessageDeflate();
+ const mockSocket = new MockSocket({
+ write: (chunk) => {
+ chunks.push(chunk);
+ if (chunks.length !== 2) return;
+
+ assert.strictEqual(chunks[0].length, 2);
+ assert.notStrictEqual(chunk[0][0] & 0x40, 0x40);
+ assert.strictEqual(chunks[1], 'hi');
+ done();
+ }
+ });
+ const sender = new Sender(mockSocket, {
+ 'permessage-deflate': perMessageDeflate
+ });
+ const extensions = extension.parse(
+ 'permessage-deflate; client_no_context_takeover'
+ );
+
+ perMessageDeflate.accept(extensions['permessage-deflate']);
+
+ sender.send('hi', { compress: true, fin: true });
+ });
+
+ it('compresses all fragments of a fragmented message', (done) => {
+ const chunks = [];
+ const perMessageDeflate = new PerMessageDeflate({ threshold: 3 });
+ const mockSocket = new MockSocket({
+ write: (chunk) => {
+ chunks.push(chunk);
+ if (chunks.length !== 4) return;
+
+ assert.strictEqual(chunks[0].length, 2);
+ assert.strictEqual(chunks[0][0] & 0x40, 0x40);
+ assert.strictEqual(chunks[1].length, 9);
+
+ assert.strictEqual(chunks[2].length, 2);
+ assert.strictEqual(chunks[2][0] & 0x40, 0x00);
+ assert.strictEqual(chunks[3].length, 4);
+ done();
+ }
+ });
+ const sender = new Sender(mockSocket, {
+ 'permessage-deflate': perMessageDeflate
+ });
+ const extensions = extension.parse(
+ 'permessage-deflate; client_no_context_takeover'
+ );
+
+ perMessageDeflate.accept(extensions['permessage-deflate']);
+
+ sender.send('123', { compress: true, fin: false });
+ sender.send('12', { compress: true, fin: true });
+ });
+
+ it('does not compress any fragments of a fragmented message', (done) => {
+ const chunks = [];
+ const perMessageDeflate = new PerMessageDeflate({ threshold: 3 });
+ const mockSocket = new MockSocket({
+ write: (chunk) => {
+ chunks.push(chunk);
+ if (chunks.length !== 4) return;
+
+ assert.strictEqual(chunks[0].length, 2);
+ assert.strictEqual(chunks[0][0] & 0x40, 0x00);
+ assert.strictEqual(chunks[1].length, 2);
+
+ assert.strictEqual(chunks[2].length, 2);
+ assert.strictEqual(chunks[2][0] & 0x40, 0x00);
+ assert.strictEqual(chunks[3].length, 3);
+ done();
+ }
+ });
+ const sender = new Sender(mockSocket, {
+ 'permessage-deflate': perMessageDeflate
+ });
+ const extensions = extension.parse(
+ 'permessage-deflate; client_no_context_takeover'
+ );
+
+ perMessageDeflate.accept(extensions['permessage-deflate']);
+
+ sender.send('12', { compress: true, fin: false });
+ sender.send('123', { compress: true, fin: true });
+ });
+
+ it('compresses empty buffer as first fragment', (done) => {
+ const chunks = [];
+ const perMessageDeflate = new PerMessageDeflate({ threshold: 0 });
+ const mockSocket = new MockSocket({
+ write: (chunk) => {
+ chunks.push(chunk);
+ if (chunks.length !== 4) return;
+
+ assert.strictEqual(chunks[0].length, 2);
+ assert.strictEqual(chunks[0][0] & 0x40, 0x40);
+ assert.strictEqual(chunks[1].length, 5);
+
+ assert.strictEqual(chunks[2].length, 2);
+ assert.strictEqual(chunks[2][0] & 0x40, 0x00);
+ assert.strictEqual(chunks[3].length, 6);
+ done();
+ }
+ });
+ const sender = new Sender(mockSocket, {
+ 'permessage-deflate': perMessageDeflate
+ });
+ const extensions = extension.parse(
+ 'permessage-deflate; client_no_context_takeover'
+ );
+
+ perMessageDeflate.accept(extensions['permessage-deflate']);
+
+ sender.send(Buffer.alloc(0), { compress: true, fin: false });
+ sender.send('data', { compress: true, fin: true });
+ });
+
+ it('compresses empty buffer as last fragment', (done) => {
+ const chunks = [];
+ const perMessageDeflate = new PerMessageDeflate({ threshold: 0 });
+ const mockSocket = new MockSocket({
+ write: (chunk) => {
+ chunks.push(chunk);
+ if (chunks.length !== 4) return;
+
+ assert.strictEqual(chunks[0].length, 2);
+ assert.strictEqual(chunks[0][0] & 0x40, 0x40);
+ assert.strictEqual(chunks[1].length, 10);
+
+ assert.strictEqual(chunks[2].length, 2);
+ assert.strictEqual(chunks[2][0] & 0x40, 0x00);
+ assert.strictEqual(chunks[3].length, 1);
+ done();
+ }
+ });
+ const sender = new Sender(mockSocket, {
+ 'permessage-deflate': perMessageDeflate
+ });
+ const extensions = extension.parse(
+ 'permessage-deflate; client_no_context_takeover'
+ );
+
+ perMessageDeflate.accept(extensions['permessage-deflate']);
+
+ sender.send('data', { compress: true, fin: false });
+ sender.send(Buffer.alloc(0), { compress: true, fin: true });
+ });
+ });
+ });
+
+ describe('#ping', () => {
+ it('works with multiple types of data', (done) => {
+ const perMessageDeflate = new PerMessageDeflate();
+ let count = 0;
+ const mockSocket = new MockSocket({
+ write: (data) => {
+ if (++count < 3) return;
+
+ if (count % 2) {
+ assert.ok(data.equals(Buffer.from([0x89, 0x02])));
+ } else if (count < 8) {
+ assert.ok(data.equals(Buffer.from([0x68, 0x69])));
+ } else {
+ assert.strictEqual(data, 'hi');
+ done();
+ }
+ }
+ });
+ const sender = new Sender(mockSocket, {
+ 'permessage-deflate': perMessageDeflate
+ });
+
+ perMessageDeflate.accept([{}]);
+
+ const array = new Uint8Array([0x68, 0x69]);
+
+ sender.send('foo', { compress: true, fin: true });
+ sender.ping(array.buffer, false);
+ sender.ping(array, false);
+ sender.ping('hi', false);
+ });
+ });
+
+ describe('#pong', () => {
+ it('works with multiple types of data', (done) => {
+ const perMessageDeflate = new PerMessageDeflate();
+ let count = 0;
+ const mockSocket = new MockSocket({
+ write: (data) => {
+ if (++count < 3) return;
+
+ if (count % 2) {
+ assert.ok(data.equals(Buffer.from([0x8a, 0x02])));
+ } else if (count < 8) {
+ assert.ok(data.equals(Buffer.from([0x68, 0x69])));
+ } else {
+ assert.strictEqual(data, 'hi');
+ done();
+ }
+ }
+ });
+ const sender = new Sender(mockSocket, {
+ 'permessage-deflate': perMessageDeflate
+ });
+
+ perMessageDeflate.accept([{}]);
+
+ const array = new Uint8Array([0x68, 0x69]);
+
+ sender.send('foo', { compress: true, fin: true });
+ sender.pong(array.buffer, false);
+ sender.pong(array, false);
+ sender.pong('hi', false);
+ });
+ });
+
+ describe('#close', () => {
+ it('throws an error if the first argument is invalid', () => {
+ const mockSocket = new MockSocket();
+ const sender = new Sender(mockSocket);
+
+ assert.throws(
+ () => sender.close('error'),
+ /^TypeError: First argument must be a valid error code number$/
+ );
+
+ assert.throws(
+ () => sender.close(1004),
+ /^TypeError: First argument must be a valid error code number$/
+ );
+ });
+
+ it('throws an error if the message is greater than 123 bytes', () => {
+ const mockSocket = new MockSocket();
+ const sender = new Sender(mockSocket);
+
+ assert.throws(
+ () => sender.close(1000, 'a'.repeat(124)),
+ /^RangeError: The message must not be greater than 123 bytes$/
+ );
+ });
+
+ it('should consume all data before closing', (done) => {
+ const perMessageDeflate = new PerMessageDeflate();
+
+ let count = 0;
+ const mockSocket = new MockSocket({
+ write: (data, cb) => {
+ count++;
+ if (cb) cb();
+ }
+ });
+ const sender = new Sender(mockSocket, {
+ 'permessage-deflate': perMessageDeflate
+ });
+
+ perMessageDeflate.accept([{}]);
+
+ sender.send('foo', { compress: true, fin: true });
+ sender.send('bar', { compress: true, fin: true });
+ sender.send('baz', { compress: true, fin: true });
+
+ sender.close(1000, undefined, false, () => {
+ assert.strictEqual(count, 8);
+ done();
+ });
+ });
+ });
+});