'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();
});
});
});
});
quality 95%
¤ Dauer der Verarbeitung: 0.5 Sekunden
¤
*© Formatika GbR, Deutschland