Skip to content

Commit 6b3904b

Browse files
author
Tom Atkinson
committed
Add threshold option to compression.
1 parent 63314f7 commit 6b3904b

File tree

5 files changed

+40
-28
lines changed

5 files changed

+40
-28
lines changed

doc/ws.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ If `handleProtocols` is not set then the handshake is accepted regardless the va
6363
* `serverMaxWindowBits` Number: The value of windowBits.
6464
* `clientMaxWindowBits` Number: The value of max windowBits to be requested to clients.
6565
* `memLevel` Number: The value of memLevel.
66+
* `threshold` Number: Payloads smaller than this will not be compressed. Default 1024 bytes.
6667

6768
If a property is empty then either an offered configuration or a default value is used.
6869

lib/PerMessageDeflate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class PerMessageDeflate {
1818
this._deflate = null;
1919
this.params = null;
2020
this._maxPayload = maxPayload || 0;
21+
this.threshold = this._options.threshold === undefined ? 1024 : this._options.threshold;
2122
}
2223

2324
/**

lib/Sender.js

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class Sender {
7878
*/
7979
doPing (data, options) {
8080
var mask = options && options.mask;
81-
this.frameAndSend(0x9, data || '', true, mask);
81+
this.frameAndSend(0x9, data ? Buffer.from(data.toString()) : null, true, mask);
8282
if (this.extensions[PerMessageDeflate.extensionName]) {
8383
this.messageHandlerCallback();
8484
}
@@ -104,7 +104,7 @@ class Sender {
104104
*/
105105
doPong (data, options) {
106106
var mask = options && options.mask;
107-
this.frameAndSend(0xa, data || '', true, mask);
107+
this.frameAndSend(0xa, data ? Buffer.from(data.toString()) : null, true, mask);
108108
if (this.extensions[PerMessageDeflate.extensionName]) {
109109
this.messageHandlerCallback();
110110
}
@@ -129,6 +129,17 @@ class Sender {
129129
}
130130
if (finalFragment) this.firstFragment = true;
131131

132+
if (data && !Buffer.isBuffer(data)) {
133+
if ((data.buffer || data) instanceof ArrayBuffer) {
134+
data = getBufferFromNative(data);
135+
} else {
136+
if (typeof data === 'number') {
137+
data = data.toString();
138+
}
139+
data = Buffer.from(data);
140+
}
141+
}
142+
132143
if (this.extensions[PerMessageDeflate.extensionName]) {
133144
this.enqueue([this.sendCompressed, [opcode, data, finalFragment, mask, compress, cb]]);
134145
} else {
@@ -142,6 +153,11 @@ class Sender {
142153
* @api private
143154
*/
144155
sendCompressed (opcode, data, finalFragment, mask, compress, cb) {
156+
if (data && data.length < this.extensions[PerMessageDeflate.extensionName].threshold) {
157+
this.frameAndSend(opcode, data, finalFragment, mask, false, cb);
158+
this.messageHandlerCallback();
159+
return;
160+
}
145161
this.applyExtensions(data, finalFragment, this.compress, (err, data) => {
146162
if (err) {
147163
if (cb) cb(err);
@@ -159,32 +175,13 @@ class Sender {
159175
* @api private
160176
*/
161177
frameAndSend (opcode, data, finalFragment, maskData, compressed, cb) {
162-
var canModifyData = false;
163-
164178
if (!data) {
165179
var buff = [opcode | (finalFragment ? 0x80 : 0), 0 | (maskData ? 0x80 : 0)]
166180
.concat(maskData ? [0, 0, 0, 0] : []);
167181
sendFramedData(this, new Buffer(buff), null, cb);
168182
return;
169183
}
170184

171-
if (!Buffer.isBuffer(data)) {
172-
if ((data.buffer || data) instanceof ArrayBuffer) {
173-
data = getBufferFromNative(data);
174-
} else {
175-
canModifyData = true;
176-
//
177-
// If people want to send a number, this would allocate the number in
178-
// bytes as memory size instead of storing the number as buffer value. So
179-
// we need to transform it to string in order to prevent possible
180-
// vulnerabilities / memory attacks.
181-
//
182-
if (typeof data === 'number') data = data.toString();
183-
184-
data = new Buffer(data);
185-
}
186-
}
187-
188185
var dataLength = data.length;
189186
var dataOffset = maskData ? 6 : 2;
190187
var secondByte = dataLength;
@@ -197,6 +194,7 @@ class Sender {
197194
secondByte = 126;
198195
}
199196

197+
var canModifyData = opcode === 1 || compressed;
200198
var mergeBuffers = dataLength < 32768 || (maskData && !canModifyData);
201199
var totalLength = mergeBuffers ? dataLength + dataOffset : dataOffset;
202200
var outputBuffer = new Buffer(totalLength);
@@ -276,9 +274,6 @@ class Sender {
276274
*/
277275
applyExtensions (data, fin, compress, callback) {
278276
if (compress && data) {
279-
if ((data.buffer || data) instanceof ArrayBuffer) {
280-
data = getBufferFromNative(data);
281-
}
282277
this.extensions[PerMessageDeflate.extensionName].compress(data, fin, callback);
283278
} else {
284279
callback(null, data);

test/Sender.test.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('Sender', function() {
3030
it('does not modify a masked text buffer', function() {
3131
var sender = new Sender({ write: function() {} });
3232
var text = 'hi there';
33-
sender.frameAndSend(1, text, true, true);
33+
sender.frameAndSend(1, Buffer.from(text), true, true);
3434
text.should.eql('hi there');
3535
});
3636

@@ -41,13 +41,13 @@ describe('Sender', function() {
4141
done();
4242
}
4343
});
44-
sender.frameAndSend(1, 'hi', true, false, true);
44+
sender.frameAndSend(1, Buffer.from('hi'), true, false, true);
4545
});
4646
});
4747

4848
describe('#send', function() {
4949
it('compresses data if compress option is enabled', function(done) {
50-
var perMessageDeflate = new PerMessageDeflate();
50+
var perMessageDeflate = new PerMessageDeflate({ threshold: 0 });
5151
perMessageDeflate.accept([{}]);
5252

5353
var sender = new Sender({
@@ -61,6 +61,21 @@ describe('Sender', function() {
6161
sender.send('hi', { compress: true });
6262
});
6363

64+
it('does not compress data for small payloads', function(done) {
65+
var perMessageDeflate = new PerMessageDeflate();
66+
perMessageDeflate.accept([{}]);
67+
68+
var sender = new Sender({
69+
write: function(data) {
70+
(data[0] & 0x40).should.not.equal(0x40);
71+
done();
72+
}
73+
}, {
74+
'permessage-deflate': perMessageDeflate
75+
});
76+
sender.send('hi', { compress: true });
77+
});
78+
6479
it('Should be able to handle many send calls while processing without crashing on flush', function(done) {
6580
var messageCount = 0;
6681
var maxMessages = 5000;

test/WebSocket.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2226,7 +2226,7 @@ describe('WebSocket', function() {
22262226
describe('#terminate', function() {
22272227
it('will raise error callback, if any, if called during send data', function(done) {
22282228
var wss = new WebSocketServer({port: ++port, perMessageDeflate: true}, function() {
2229-
var ws = new WebSocket('ws://localhost:' + port, {perMessageDeflate: true});
2229+
var ws = new WebSocket('ws://localhost:' + port, {perMessageDeflate: { threshold: 0 }});
22302230
var errorGiven = false;
22312231
ws.on('open', function() {
22322232
ws.send('hi', function(error) {

0 commit comments

Comments
 (0)