Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a74a0471b | |||
| 3500cddfa7 | |||
| 8d58a3363d | |||
| f104377a0e | |||
| 8ddfd06f5b | |||
| 33df77ef24 | |||
| 0594d1b16b | |||
| 3fc65a8277 | |||
| c0ef506072 | |||
| 689e37cfb7 | |||
| 7abe8c8a7f | |||
| cbae000ab3 | |||
| 9e8a72316d | |||
| 2f277e23c8 | |||
| 0215b35a21 | |||
| f98b56db0f | |||
| cad0e4f121 | |||
| 0e8539acf7 | |||
| 48a078f8e7 | |||
| 60764f5757 | |||
| 6b75657a94 | |||
| d22edec996 | |||
| 1d219334bf | |||
| 913b174ea1 | |||
| 0eb99773db | |||
| 758ed46420 | |||
| 9cb62ecd49 | |||
| dca0a77b0d | |||
| 6e314bde56 | |||
| 47b3b3614b | |||
| 5da6a768b0 | |||
| f03aef1954 | |||
| adebc51630 | |||
| 958de927aa | |||
| 037afc350c | |||
| 10b5d6be57 |
@@ -1 +1,2 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
.git
|
||||
.gitignore
|
||||
.npmignore
|
||||
.travis.yml
|
||||
node_modules
|
||||
spec
|
||||
+12
-3
@@ -1,10 +1,19 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "0.6"
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
- "0.12"
|
||||
- "4"
|
||||
- "5"
|
||||
- "6"
|
||||
- "7"
|
||||
- "8"
|
||||
- "9"
|
||||
- "10"
|
||||
- "11"
|
||||
- "12"
|
||||
|
||||
before_install:
|
||||
- '[ "${TRAVIS_NODE_VERSION}" = "0.6" ] && npm conf set strict-ssl false || true'
|
||||
- '[ "${TRAVIS_NODE_VERSION}" = "0.8" ] && npm install -g npm@~1.4.0 || true'
|
||||
|
||||
+28
-4
@@ -1,14 +1,38 @@
|
||||
### 0.1.7 / 2019-06-10
|
||||
|
||||
- Use the `Buffer.alloc()` and `Buffer.from()` functions instead of the unsafe
|
||||
`Buffer()` constructor
|
||||
|
||||
### 0.1.6 / 2017-09-10
|
||||
|
||||
- Use `9` instead of `8` as the `windowBits` parameter to zlib, to deal with
|
||||
restrictions introduced in zlib v1.2.9
|
||||
|
||||
### 0.1.5 / 2016-02-24
|
||||
|
||||
- Catch errors thrown by `close()` on zlib streams
|
||||
|
||||
### 0.1.4 / 2015-11-06
|
||||
|
||||
- The server does not send `server_max_window_bits` if the client does not ask
|
||||
for it; this works around an issue in Firefox.
|
||||
|
||||
### 0.1.3 / 2015-04-10
|
||||
|
||||
- Fix a race condition causing some fragments of deflate output to be dropped
|
||||
- Make sure to emit minimal output on all Node versions
|
||||
|
||||
### 0.1.2 / 2014-12-18
|
||||
|
||||
* Don't allow configure() to be called with unrecognized options
|
||||
- Don't allow configure() to be called with unrecognized options
|
||||
|
||||
### 0.1.1 / 2014-12-15
|
||||
|
||||
* Fix race condition when using context takeover, where adjacent messages have
|
||||
- Fix race condition when using context takeover, where adjacent messages have
|
||||
data listeners bound at the same time and end up duplicating output
|
||||
* Use `DeflateRaw.flush()` correctly on v0.10 so that optimal compression is
|
||||
- Use `DeflateRaw.flush()` correctly on v0.10 so that optimal compression is
|
||||
achieved
|
||||
|
||||
### 0.1.0 / 2014-12-13
|
||||
|
||||
* Initial release
|
||||
- Initial release
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Code of Conduct
|
||||
|
||||
All projects under the [Faye](https://github.com/faye) umbrella are covered by
|
||||
the [Code of Conduct](https://github.com/faye/code-of-conduct).
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
Copyright 2014-2019 James Coglan
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed
|
||||
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations under the License.
|
||||
@@ -11,7 +11,7 @@ WebSocket protocol extension as a plugin for
|
||||
$ npm install permessage-deflate
|
||||
```
|
||||
|
||||
## Usage
|
||||
## Usage
|
||||
|
||||
Add the plugin to your extensions:
|
||||
|
||||
@@ -45,49 +45,25 @@ the peer, and those that are negotiated as part of the protocol. The settings
|
||||
only affecting the compressor are described fully in the [zlib
|
||||
documentation](http://nodejs.org/api/zlib.html#zlib_options):
|
||||
|
||||
* `level`: sets the compression level, can be an integer from `0` to `9`, or one
|
||||
- `level`: sets the compression level, can be an integer from `0` to `9`, or one
|
||||
of the constants `zlib.Z_NO_COMPRESSION`, `zlib.Z_BEST_SPEED`,
|
||||
`zlib.Z_BEST_COMPRESSION`, or `zlib.Z_DEFAULT_COMPRESSION`
|
||||
* `memLevel`: sets how much memory the compressor allocates, can be an integer
|
||||
- `memLevel`: sets how much memory the compressor allocates, can be an integer
|
||||
from `1` to `9`, or one of the constants `zlib.Z_MIN_MEMLEVEL`,
|
||||
`zlib.Z_MAX_MEMLEVEL`, or `zlib.Z_DEFAULT_MEMLEVEL`
|
||||
* `strategy`: can be one of the constants `zlib.Z_FILTERED`,
|
||||
- `strategy`: can be one of the constants `zlib.Z_FILTERED`,
|
||||
`zlib.Z_HUFFMAN_ONLY`, `zlib.Z_RLE`, `zlib.Z_FIXED`, or
|
||||
`zlib.Z_DEFAULT_STRATEGY`
|
||||
|
||||
The other options relate to settings that are negotiated via the protocol and
|
||||
can be used to set the local session's behaviour and control that of the peer:
|
||||
|
||||
* `noContextTakeover`: if `true`, stops the session reusing a deflate context
|
||||
- `noContextTakeover`: if `true`, stops the session reusing a deflate context
|
||||
between messages
|
||||
* `requestNoContextTakeover`: if `true`, makes the session tell the other peer
|
||||
- `requestNoContextTakeover`: if `true`, makes the session tell the other peer
|
||||
not to reuse a deflate context between messages
|
||||
* `maxWindowBits`: an integer from `8` to `15` inclusive that sets the maximum
|
||||
- `maxWindowBits`: an integer from `8` to `15` inclusive that sets the maximum
|
||||
size of the session's sliding window; a lower window size will be used if
|
||||
requested by the peer
|
||||
* `requestMaxWindowBits`: an integer from `8` to `15` inclusive to ask the other
|
||||
- `requestMaxWindowBits`: an integer from `8` to `15` inclusive to ask the other
|
||||
peer to use to set its maximum sliding window size, if supported
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014 James Coglan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the 'Software'), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,52 @@
|
||||
var fs = require('fs'),
|
||||
deflate = require('..');
|
||||
|
||||
var records = fs.readFileSync(__dirname + '/bad.out.log', 'utf8')
|
||||
.replace(/\s*$/g, '')
|
||||
.split(/\n/)
|
||||
.map(JSON.parse);
|
||||
|
||||
var client = deflate.createClientSession(),
|
||||
offer = client.generateOffer(),
|
||||
server = deflate.createServerSession([offer]),
|
||||
response = server.generateResponse(),
|
||||
compressed = [],
|
||||
size = [0, 0];
|
||||
|
||||
client.activate(response);
|
||||
|
||||
function compress(index) {
|
||||
var record = records[index];
|
||||
if (!record) {
|
||||
console.log(size, size[0] / size[1]);
|
||||
return decompress(0);
|
||||
}
|
||||
|
||||
var message = {data: new Buffer(record[3], 'base64')};
|
||||
size[0] += message.data.length;
|
||||
|
||||
server.processOutgoingMessage(message, function(error, message) {
|
||||
compressed[index] = message;
|
||||
size[1] += message.data.length;
|
||||
compress(index + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function decompress(index) {
|
||||
var record = records[index];
|
||||
if (!record) return;
|
||||
|
||||
var payload = record[3],
|
||||
message = compressed[index];
|
||||
|
||||
client.processIncomingMessage(message, function(error, message) {
|
||||
var output = message.data.toString('base64');
|
||||
if (output !== payload) {
|
||||
console.error('Failed on', record, message);
|
||||
process.exit(1);
|
||||
}
|
||||
decompress(index + 1);
|
||||
});
|
||||
}
|
||||
|
||||
compress(0);
|
||||
@@ -39,7 +39,7 @@ ClientSession.prototype.generateOffer = function() {
|
||||
|
||||
if (this._requestMaxWindowBits !== undefined) {
|
||||
if (common.VALID_WINDOW_BITS.indexOf(this._requestMaxWindowBits) < 0) {
|
||||
throw new Error('Invalid valud for requestMaxWindowBits');
|
||||
throw new Error('Invalid value for requestMaxWindowBits');
|
||||
}
|
||||
offer.server_max_window_bits = this._requestMaxWindowBits;
|
||||
}
|
||||
@@ -64,12 +64,12 @@ ClientSession.prototype.activate = function(params) {
|
||||
|
||||
this._ownContextTakeover = !(this._acceptNoContextTakeover || params.client_no_context_takeover);
|
||||
this._ownWindowBits = Math.min(
|
||||
this._acceptMaxWindowBits || common.DEFAULT_MAX_WINDOW_BITS,
|
||||
params.client_max_window_bits || common.DEFAULT_MAX_WINDOW_BITS
|
||||
this._acceptMaxWindowBits || common.MAX_WINDOW_BITS,
|
||||
params.client_max_window_bits || common.MAX_WINDOW_BITS
|
||||
);
|
||||
|
||||
this._peerContextTakeover = !params.server_no_context_takeover;
|
||||
this._peerWindowBits = params.server_max_window_bits || common.DEFAULT_MAX_WINDOW_BITS;
|
||||
this._peerWindowBits = params.server_max_window_bits || common.MAX_WINDOW_BITS;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
+4
-12
@@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var Buffer = require('safe-buffer').Buffer;
|
||||
|
||||
var common = {
|
||||
VALID_PARAMS: [
|
||||
'server_no_context_takeover',
|
||||
@@ -8,20 +10,10 @@ var common = {
|
||||
'client_max_window_bits'
|
||||
],
|
||||
|
||||
DEFAULT_MAX_WINDOW_BITS: 15,
|
||||
MIN_WINDOW_BITS: 9,
|
||||
MAX_WINDOW_BITS: 15,
|
||||
VALID_WINDOW_BITS: [8, 9, 10, 11, 12, 13, 14, 15],
|
||||
|
||||
concat: function(buffers, length) {
|
||||
var buffer = new Buffer(length),
|
||||
offset = 0;
|
||||
|
||||
for (var i = 0, n = buffers.length; i < n; i++) {
|
||||
buffers[i].copy(buffer, offset);
|
||||
offset += buffers[i].length;
|
||||
}
|
||||
return buffer;
|
||||
},
|
||||
|
||||
fetch: function(options, key, _default) {
|
||||
if (options.hasOwnProperty(key))
|
||||
return options[key];
|
||||
|
||||
@@ -11,7 +11,8 @@ var VALID_OPTIONS = [
|
||||
'noContextTakeover',
|
||||
'maxWindowBits',
|
||||
'requestNoContextTakeover',
|
||||
'requestMaxWindowBits'
|
||||
'requestMaxWindowBits',
|
||||
'zlib'
|
||||
];
|
||||
|
||||
var PermessageDeflate = {
|
||||
|
||||
+31
-27
@@ -21,42 +21,46 @@ ServerSession.validParams = function(params) {
|
||||
};
|
||||
|
||||
ServerSession.prototype.generateResponse = function() {
|
||||
var params = {};
|
||||
var response = {};
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression#section-8.1.1.1
|
||||
if (this._acceptNoContextTakeover || this._params.server_no_context_takeover)
|
||||
params.server_no_context_takeover = true;
|
||||
// https://tools.ietf.org/html/rfc7692#section-7.1.1.1
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression#section-8.1.1.2
|
||||
if (this._requestNoContextTakeover || this._params.client_no_context_takeover)
|
||||
params.client_no_context_takeover = true;
|
||||
this._ownContextTakeover = !this._acceptNoContextTakeover &&
|
||||
!this._params.server_no_context_takeover;
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression#section-8.1.2.1
|
||||
var acceptMax, serverMax;
|
||||
if (this._acceptMaxWindowBits || this._params.server_max_window_bits) {
|
||||
acceptMax = this._acceptMaxWindowBits || common.DEFAULT_MAX_WINDOW_BITS;
|
||||
serverMax = this._params.server_max_window_bits || common.DEFAULT_MAX_WINDOW_BITS;
|
||||
params.server_max_window_bits = Math.min(acceptMax, serverMax);
|
||||
}
|
||||
if (!this._ownContextTakeover) response.server_no_context_takeover = true;
|
||||
|
||||
// https://tools.ietf.org/html/rfc7692#section-7.1.1.2
|
||||
|
||||
this._peerContextTakeover = !this._requestNoContextTakeover &&
|
||||
!this._params.client_no_context_takeover;
|
||||
|
||||
if (!this._peerContextTakeover) response.client_no_context_takeover = true;
|
||||
|
||||
// https://tools.ietf.org/html/rfc7692#section-7.1.2.1
|
||||
|
||||
this._ownWindowBits = Math.min(this._acceptMaxWindowBits || common.MAX_WINDOW_BITS,
|
||||
this._params.server_max_window_bits || common.MAX_WINDOW_BITS);
|
||||
|
||||
// In violation of the spec, Firefox closes the connection if it does not
|
||||
// send server_max_window_bits but the server includes this in its response
|
||||
if (this._ownWindowBits < common.MAX_WINDOW_BITS && this._params.server_max_window_bits)
|
||||
response.server_max_window_bits = this._ownWindowBits;
|
||||
|
||||
// https://tools.ietf.org/html/rfc7692#section-7.1.2.2
|
||||
|
||||
// https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression#section-8.1.2.2
|
||||
var clientMax = this._params.client_max_window_bits, requestMax;
|
||||
if (clientMax) {
|
||||
if (clientMax === true) {
|
||||
if (this._requestMaxWindowBits) params.client_max_window_bits = this._requestMaxWindowBits;
|
||||
} else {
|
||||
requestMax = this._requestMaxWindowBits || common.DEFAULT_MAX_WINDOW_BITS;
|
||||
params.client_max_window_bits = Math.min(requestMax, clientMax);
|
||||
}
|
||||
if (clientMax === true) clientMax = common.MAX_WINDOW_BITS;
|
||||
this._peerWindowBits = Math.min(this._requestMaxWindowBits || common.MAX_WINDOW_BITS, clientMax);
|
||||
} else {
|
||||
this._peerWindowBits = common.MAX_WINDOW_BITS;
|
||||
}
|
||||
|
||||
this._ownContextTakeover = !params.server_no_context_takeover;
|
||||
this._ownWindowBits = params.server_max_window_bits || common.DEFAULT_MAX_WINDOW_BITS;
|
||||
if (this._peerWindowBits < common.MAX_WINDOW_BITS)
|
||||
response.client_max_window_bits = this._peerWindowBits;
|
||||
|
||||
this._peerContextTakeover = !params.client_no_context_takeover;
|
||||
this._peerWindowBits = params.client_max_window_bits || common.DEFAULT_MAX_WINDOW_BITS;
|
||||
|
||||
return params;
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports = ServerSession;
|
||||
|
||||
+54
-27
@@ -1,12 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
var zlib = require('zlib'),
|
||||
var Buffer = require('safe-buffer').Buffer,
|
||||
zlib = require('zlib'),
|
||||
common = require('./common');
|
||||
|
||||
var VERSION = process.version.match(/\d+/g).map(function(n) { return parseInt(n, 10) });
|
||||
|
||||
var Session = function(options) {
|
||||
this._level = common.fetch(options, 'level', zlib.Z_DEFAULT_LEVEL);
|
||||
this._level = common.fetch(options, 'level', zlib.Z_DEFAULT_COMPRESSION);
|
||||
this._memLevel = common.fetch(options, 'memLevel', zlib.Z_DEFAULT_MEMLEVEL);
|
||||
this._strategy = common.fetch(options, 'strategy', zlib.Z_DEFAULT_STRATEGY);
|
||||
|
||||
@@ -17,6 +16,8 @@ var Session = function(options) {
|
||||
|
||||
this._queueIn = [];
|
||||
this._queueOut = [];
|
||||
|
||||
this._zlib = common.fetch(options, 'zlib', zlib);
|
||||
};
|
||||
|
||||
Session.prototype.processIncomingMessage = function(message, callback) {
|
||||
@@ -35,7 +36,7 @@ Session.prototype.processIncomingMessage = function(message, callback) {
|
||||
|
||||
inflate.removeListener('data', onData);
|
||||
inflate.removeListener('error', onError);
|
||||
if (!self._inflate && inflate.close) inflate.close();
|
||||
if (!self._inflate) self._close(inflate);
|
||||
|
||||
self._lockIn = false;
|
||||
var next = self._queueIn.shift();
|
||||
@@ -57,10 +58,10 @@ Session.prototype.processIncomingMessage = function(message, callback) {
|
||||
inflate.on('error', onError);
|
||||
|
||||
inflate.write(message.data);
|
||||
inflate.write(new Buffer([0x00, 0x00, 0xff, 0xff]));
|
||||
inflate.write(Buffer.from([0x00, 0x00, 0xff, 0xff]));
|
||||
|
||||
inflate.flush(function() {
|
||||
message.data = common.concat(chunks, length);
|
||||
message.data = Buffer.concat(chunks, length);
|
||||
return_(null, message);
|
||||
});
|
||||
};
|
||||
@@ -80,7 +81,7 @@ Session.prototype.processOutgoingMessage = function(message, callback) {
|
||||
|
||||
deflate.removeListener('data', onData);
|
||||
deflate.removeListener('error', onError);
|
||||
if (!self._deflate && deflate.close) deflate.close();
|
||||
if (!self._deflate) self._close(deflate);
|
||||
|
||||
self._lockOut = false;
|
||||
var next = self._queueOut.shift();
|
||||
@@ -90,19 +91,8 @@ Session.prototype.processOutgoingMessage = function(message, callback) {
|
||||
};
|
||||
|
||||
var onData = function(data) {
|
||||
var tail = data.slice(Math.max(data.length - 4, 0), data.length),
|
||||
isEnd = (tail[0] === 0x00 && tail[1] === 0x00 && tail[2] === 0xff && tail[3] === 0xff);
|
||||
|
||||
if (isEnd) data = data.slice(0, data.length - 4);
|
||||
|
||||
chunks.push(data);
|
||||
length += data.length;
|
||||
|
||||
if (isEnd) {
|
||||
message.rsv1 = true;
|
||||
message.data = common.concat(chunks, length);
|
||||
return_(null, message);
|
||||
}
|
||||
};
|
||||
|
||||
var onError = function(error) {
|
||||
@@ -111,22 +101,35 @@ Session.prototype.processOutgoingMessage = function(message, callback) {
|
||||
|
||||
deflate.on('data', onData);
|
||||
deflate.on('error', onError);
|
||||
|
||||
deflate.write(message.data);
|
||||
if (VERSION[0] === 0 && VERSION[1] < 10) deflate.flush();
|
||||
|
||||
var onFlush = function() {
|
||||
var data = Buffer.concat(chunks, length);
|
||||
message.data = data.slice(0, data.length - 4);
|
||||
message.rsv1 = true;
|
||||
return_(null, message);
|
||||
};
|
||||
|
||||
if (deflate.params !== undefined)
|
||||
deflate.flush(zlib.Z_SYNC_FLUSH, onFlush);
|
||||
else
|
||||
deflate.flush(onFlush);
|
||||
};
|
||||
|
||||
Session.prototype.close = function() {
|
||||
if (this._inflate && this._inflate.close) this._inflate.close();
|
||||
this._close(this._inflate);
|
||||
this._inflate = null;
|
||||
|
||||
if (this._deflate && this._deflate.close) this._deflate.close();
|
||||
this._close(this._deflate);
|
||||
this._deflate = null;
|
||||
};
|
||||
|
||||
Session.prototype._getInflate = function() {
|
||||
if (this._inflate) return this._inflate;
|
||||
var inflate = zlib.createInflateRaw({windowBits: this._peerWindowBits});
|
||||
|
||||
var windowBits = Math.max(this._peerWindowBits, common.MIN_WINDOW_BITS),
|
||||
inflate = this._zlib.createInflateRaw({windowBits: windowBits});
|
||||
|
||||
if (this._peerContextTakeover) this._inflate = inflate;
|
||||
return inflate;
|
||||
};
|
||||
@@ -134,15 +137,39 @@ Session.prototype._getInflate = function() {
|
||||
Session.prototype._getDeflate = function() {
|
||||
if (this._deflate) return this._deflate;
|
||||
|
||||
var deflate = zlib.createDeflateRaw({
|
||||
flush: zlib.Z_SYNC_FLUSH,
|
||||
windowBits: this._ownWindowBits,
|
||||
var windowBits = Math.max(this._ownWindowBits, common.MIN_WINDOW_BITS);
|
||||
|
||||
var deflate = this._zlib.createDeflateRaw({
|
||||
windowBits: windowBits,
|
||||
level: this._level,
|
||||
memLevel: this._memLevel,
|
||||
strategy: this._strategy
|
||||
});
|
||||
|
||||
var flush = deflate.flush;
|
||||
|
||||
// This monkey-patch is needed to make Node 0.10 produce optimal output.
|
||||
// Without this it uses Z_FULL_FLUSH and effectively drops all its context
|
||||
// state on every flush.
|
||||
|
||||
if (deflate._flushFlag !== undefined && deflate.params === undefined)
|
||||
deflate.flush = function(callback) {
|
||||
var ws = this._writableState;
|
||||
if (ws.ended || ws.ending || ws.needDrain) {
|
||||
flush.call(this, callback);
|
||||
} else {
|
||||
this._flushFlag = zlib.Z_SYNC_FLUSH;
|
||||
this.write(Buffer.alloc(0), '', callback);
|
||||
}
|
||||
};
|
||||
|
||||
if (this._ownContextTakeover) this._deflate = deflate;
|
||||
return deflate;
|
||||
};
|
||||
|
||||
Session.prototype._close = function(codec) {
|
||||
if (!codec || !codec.close) return;
|
||||
try { codec.close() } catch (error) {}
|
||||
};
|
||||
|
||||
module.exports = Session;
|
||||
|
||||
+33
-19
@@ -1,20 +1,34 @@
|
||||
{ "name" : "permessage-deflate"
|
||||
, "description" : "Per-message DEFLATE compression extension for WebSocket connections"
|
||||
, "homepage" : "http://github.com/faye/permessage-deflate-node"
|
||||
, "author" : "James Coglan <jcoglan@gmail.com> (http://jcoglan.com/)"
|
||||
, "keywords" : ["websocket", "compression", "deflate"]
|
||||
, "license" : "MIT"
|
||||
|
||||
, "version" : "0.1.2"
|
||||
, "engines" : {"node": ">=0.6.0"}
|
||||
, "main" : "./lib/permessage_deflate"
|
||||
, "devDependencies" : {"jstest": ""}
|
||||
|
||||
, "scripts" : {"test": "jstest spec/runner.js"}
|
||||
|
||||
, "repository" : { "type" : "git"
|
||||
, "url" : "git://github.com/faye/permessage-deflate-node.git"
|
||||
}
|
||||
|
||||
, "bugs" : "http://github.com/faye/permessage-deflate-node/issues"
|
||||
{
|
||||
"name": "permessage-deflate",
|
||||
"description": "Per-message DEFLATE compression extension for WebSocket connections",
|
||||
"homepage": "https://github.com/faye/permessage-deflate-node",
|
||||
"author": "James Coglan <jcoglan@gmail.com> (http://jcoglan.com/)",
|
||||
"keywords": [
|
||||
"websocket",
|
||||
"compression",
|
||||
"deflate"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"version": "0.1.7",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"main": "./lib/permessage_deflate",
|
||||
"dependencies": {
|
||||
"safe-buffer": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jstest": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jstest spec/runner.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/faye/permessage-deflate-node.git"
|
||||
},
|
||||
"bugs": "https://github.com/faye/permessage-deflate-node/issues"
|
||||
}
|
||||
|
||||
+36
-24
@@ -1,18 +1,18 @@
|
||||
var PermessageDeflate = require('../lib/permessage_deflate'),
|
||||
zlib = require('zlib'),
|
||||
_zlib = require('zlib'),
|
||||
test = require('jstest').Test
|
||||
|
||||
test.describe("ClientSession", function() { with(this) {
|
||||
before(function() { with(this) {
|
||||
this.ext = PermessageDeflate.configure(options)
|
||||
this.session = ext.createClientSession()
|
||||
|
||||
this.deflate = zlibMock()
|
||||
this.inflate = zlibMock()
|
||||
this.flush = zlib.Z_SYNC_FLUSH
|
||||
this.level = zlib.Z_DEFAULT_LEVEL
|
||||
this.memLevel = zlib.Z_DEFAULT_MEMLEVEL
|
||||
this.strategy = zlib.Z_DEFAULT_STRATEGY
|
||||
this.level = _zlib.Z_DEFAULT_LEVEL
|
||||
this.memLevel = _zlib.Z_DEFAULT_MEMLEVEL
|
||||
this.strategy = _zlib.Z_DEFAULT_STRATEGY
|
||||
|
||||
this.ext = PermessageDeflate.configure(options)
|
||||
this.zlib = {}
|
||||
this.session = ext.configure({zlib: zlib}).createClientSession()
|
||||
|
||||
this.message = {data: "hello", rsv1: true}
|
||||
}})
|
||||
@@ -25,7 +25,7 @@ test.describe("ClientSession", function() { with(this) {
|
||||
this.stub(stream, "removeListener")
|
||||
|
||||
this.stub(stream, "write")
|
||||
this.stub(stream, "flush", function(cb) { if(cb) cb() });
|
||||
this.stub(stream, "flush").yields([])
|
||||
this.stub(stream, "close").raises(new Error("unexpected close()"))
|
||||
|
||||
return stream
|
||||
@@ -71,7 +71,7 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("uses context takeover and 15 window bits for deflating outgoing messages", function() { with(this) {
|
||||
activate()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
@@ -91,6 +91,12 @@ test.describe("ClientSession", function() { with(this) {
|
||||
processIncomingMessage()
|
||||
processIncomingMessage()
|
||||
}})
|
||||
|
||||
it("catches errors thrown by zlib", function() { with(this) {
|
||||
activate()
|
||||
stub(zlib, "createInflateRaw").returning(inflate)
|
||||
assertNothingThrown(function() { processIncomingMessage() })
|
||||
}})
|
||||
}})
|
||||
|
||||
describe("when the response includes client_no_context_takeover", function() { with(this) {
|
||||
@@ -102,11 +108,17 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("uses no context takeover and 15 window bits to deflate outgoing messages", function() { with(this) {
|
||||
activate()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(2).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(2).returning(deflate)
|
||||
expect(deflate, "close").exactly(2)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
|
||||
it("catches errors thrown by zlib", function() { with(this) {
|
||||
activate()
|
||||
stub(zlib, "createDeflateRaw").returning(deflate)
|
||||
assertNothingThrown(function() { processOutgoingMessage() })
|
||||
}})
|
||||
}})
|
||||
|
||||
describe("when the response includes server_max_window_bits", function() { with(this) {
|
||||
@@ -116,9 +128,9 @@ test.describe("ClientSession", function() { with(this) {
|
||||
assertEqual( true, activate() )
|
||||
}})
|
||||
|
||||
it("uses context takeover and 8 window bits for inflating incoming messages", function() { with(this) {
|
||||
it("uses context takeover and 9 window bits for inflating incoming messages", function() { with(this) {
|
||||
activate()
|
||||
expect(zlib, "createInflateRaw").given({windowBits: 8}).exactly(1).returning(inflate)
|
||||
expect(zlib, "createInflateRaw").given({windowBits: 9}).exactly(1).returning(inflate)
|
||||
processIncomingMessage()
|
||||
processIncomingMessage()
|
||||
}})
|
||||
@@ -139,9 +151,9 @@ test.describe("ClientSession", function() { with(this) {
|
||||
assertEqual( true, activate() )
|
||||
}})
|
||||
|
||||
it("uses context takeover and 8 window bits for deflating outgoing messages", function() { with(this) {
|
||||
it("uses context takeover and 9 window bits for deflating outgoing messages", function() { with(this) {
|
||||
activate()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 8, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 9, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
@@ -170,7 +182,7 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("uses no context takeover and 15 window bits for deflating outgoing messages", function() { with(this) {
|
||||
activate()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(2).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(2).returning(deflate)
|
||||
expect(deflate, "close").exactly(2)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
@@ -192,7 +204,7 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("uses context takeover and 9 window bits for deflating outgoing messages", function() { with(this) {
|
||||
activate()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 9, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 9, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
@@ -213,9 +225,9 @@ test.describe("ClientSession", function() { with(this) {
|
||||
assertEqual( true, activate() )
|
||||
}})
|
||||
|
||||
it("uses context takeover and 8 window bits for deflating outgoing messages", function() { with(this) {
|
||||
it("uses context takeover and 9 window bits for deflating outgoing messages", function() { with(this) {
|
||||
activate()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 8, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 9, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
@@ -306,11 +318,11 @@ test.describe("ClientSession", function() { with(this) {
|
||||
}})
|
||||
|
||||
describe("with level", function() { with(this) {
|
||||
define("options", {level: zlib.Z_BEST_SPEED})
|
||||
define("options", {level: _zlib.Z_BEST_SPEED})
|
||||
|
||||
it("sets the level of the deflate stream", function() { with(this) {
|
||||
activate()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: zlib.Z_BEST_SPEED, memLevel: memLevel, strategy: strategy}).returns(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: _zlib.Z_BEST_SPEED, memLevel: memLevel, strategy: strategy}).returns(deflate)
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
}})
|
||||
@@ -320,17 +332,17 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("sets the memLevel of the deflate stream", function() { with(this) {
|
||||
activate()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: zlib.Z_DEFAULT_LEVEL, memLevel: 5, strategy: strategy}).returns(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: _zlib.Z_DEFAULT_LEVEL, memLevel: 5, strategy: strategy}).returns(deflate)
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
}})
|
||||
|
||||
describe("with strategy", function() { with(this) {
|
||||
define("options", {strategy: zlib.Z_FILTERED})
|
||||
define("options", {strategy: _zlib.Z_FILTERED})
|
||||
|
||||
it("sets the strategy of the deflate stream", function() { with(this) {
|
||||
activate()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: zlib.Z_DEFAULT_LEVEL, memLevel: memLevel, strategy: zlib.Z_FILTERED}).returns(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: _zlib.Z_DEFAULT_LEVEL, memLevel: memLevel, strategy: _zlib.Z_FILTERED}).returns(deflate)
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
}})
|
||||
|
||||
+36
-24
@@ -1,18 +1,18 @@
|
||||
var PermessageDeflate = require('../lib/permessage_deflate'),
|
||||
zlib = require('zlib'),
|
||||
_zlib = require('zlib'),
|
||||
test = require('jstest').Test
|
||||
|
||||
test.describe("ClientSession", function() { with(this) {
|
||||
test.describe("ServerSession", function() { with(this) {
|
||||
before(function() { with(this) {
|
||||
this.ext = PermessageDeflate.configure(options)
|
||||
this.session = ext.createServerSession([offer])
|
||||
|
||||
this.deflate = zlibMock()
|
||||
this.inflate = zlibMock()
|
||||
this.flush = zlib.Z_SYNC_FLUSH
|
||||
this.level = zlib.Z_DEFAULT_LEVEL
|
||||
this.memLevel = zlib.Z_DEFAULT_MEMLEVEL
|
||||
this.strategy = zlib.Z_DEFAULT_STRATEGY
|
||||
this.level = _zlib.Z_DEFAULT_LEVEL
|
||||
this.memLevel = _zlib.Z_DEFAULT_MEMLEVEL
|
||||
this.strategy = _zlib.Z_DEFAULT_STRATEGY
|
||||
|
||||
this.ext = PermessageDeflate.configure(options)
|
||||
this.zlib = {}
|
||||
this.session = ext.configure({zlib: zlib}).createServerSession([offer])
|
||||
|
||||
this.message = {data: "hello", rsv1: true}
|
||||
}})
|
||||
@@ -25,7 +25,7 @@ test.describe("ClientSession", function() { with(this) {
|
||||
this.stub(stream, "removeListener")
|
||||
|
||||
this.stub(stream, "write")
|
||||
this.stub(stream, "flush", function(cb) { if(cb) cb() });
|
||||
this.stub(stream, "flush").yields([])
|
||||
this.stub(stream, "close").raises(new Error("unexpected close()"))
|
||||
|
||||
return stream
|
||||
@@ -62,7 +62,7 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("uses context takeover and 15 window bits for deflating outgoing messages", function() { with(this) {
|
||||
response()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
@@ -77,11 +77,17 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("uses no context takeover and 15 window bits for deflating outgoing messages", function() { with(this) {
|
||||
response()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(2).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(2).returning(deflate)
|
||||
expect(deflate, "close").exactly(2)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
|
||||
it("catches errors thrown by zlib", function() { with(this) {
|
||||
response()
|
||||
stub(zlib, "createDeflateRaw").returning(deflate)
|
||||
assertNothingThrown(function() { processOutgoingMessage() })
|
||||
}})
|
||||
}})
|
||||
|
||||
describe("when the offer includes client_no_context_takeover", function() { with(this) {
|
||||
@@ -98,6 +104,12 @@ test.describe("ClientSession", function() { with(this) {
|
||||
processIncomingMessage()
|
||||
processIncomingMessage()
|
||||
}})
|
||||
|
||||
it("catches errors thrown by zlib", function() { with(this) {
|
||||
response()
|
||||
stub(zlib, "createInflateRaw").returning(inflate)
|
||||
assertNothingThrown(function() { processIncomingMessage() })
|
||||
}})
|
||||
}})
|
||||
|
||||
describe("when the offer includes server_max_window_bits", function() { with(this) {
|
||||
@@ -109,7 +121,7 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("uses context takeover and 13 window bits for deflating outgoing messages", function() { with(this) {
|
||||
response()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 13, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 13, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
@@ -172,7 +184,7 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("uses no context takeover and 15 window bits for deflating outgoing messages", function() { with(this) {
|
||||
response()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(2).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: level, memLevel: memLevel, strategy: strategy}).exactly(2).returning(deflate)
|
||||
expect(deflate, "close").exactly(2)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
@@ -184,13 +196,13 @@ test.describe("ClientSession", function() { with(this) {
|
||||
define("options", {maxWindowBits: 12})
|
||||
|
||||
describe("with an empty offer", function() { with(this) {
|
||||
it("includes server_max_window_bits in the response", function() { with(this) {
|
||||
assertEqual( {server_max_window_bits: 12}, response() )
|
||||
it("does not include server_max_window_bits in the response", function() { with(this) {
|
||||
assertEqual( {}, response() )
|
||||
}})
|
||||
|
||||
it("uses context takeover and 12 window bits for deflating outgoing messages", function() { with(this) {
|
||||
response()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 12, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 12, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
@@ -205,7 +217,7 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("uses context takeover and 12 window bits for deflating outgoing messages", function() { with(this) {
|
||||
response()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 12, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 12, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
@@ -220,7 +232,7 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("uses context takeover and 11 window bits for deflating outgoing messages", function() { with(this) {
|
||||
response()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 11, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 11, level: level, memLevel: memLevel, strategy: strategy}).exactly(1).returning(deflate)
|
||||
processOutgoingMessage()
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
@@ -308,11 +320,11 @@ test.describe("ClientSession", function() { with(this) {
|
||||
}})
|
||||
|
||||
describe("with level", function() { with(this) {
|
||||
define("options", {level: zlib.Z_BEST_SPEED})
|
||||
define("options", {level: _zlib.Z_BEST_SPEED})
|
||||
|
||||
it("sets the level of the deflate stream", function() { with(this) {
|
||||
response()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: zlib.Z_BEST_SPEED, memLevel: memLevel, strategy: strategy}).returns(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: _zlib.Z_BEST_SPEED, memLevel: memLevel, strategy: strategy}).returns(deflate)
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
}})
|
||||
@@ -322,17 +334,17 @@ test.describe("ClientSession", function() { with(this) {
|
||||
|
||||
it("sets the memLevel of the deflate stream", function() { with(this) {
|
||||
response()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: zlib.Z_DEFAULT_LEVEL, memLevel: 5, strategy: strategy}).returns(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: _zlib.Z_DEFAULT_LEVEL, memLevel: 5, strategy: strategy}).returns(deflate)
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
}})
|
||||
|
||||
describe("with strategy", function() { with(this) {
|
||||
define("options", {strategy: zlib.Z_FILTERED})
|
||||
define("options", {strategy: _zlib.Z_FILTERED})
|
||||
|
||||
it("sets the strategy of the deflate stream", function() { with(this) {
|
||||
response()
|
||||
expect(zlib, "createDeflateRaw").given({flush: flush, windowBits: 15, level: zlib.Z_DEFAULT_LEVEL, memLevel: memLevel, strategy: zlib.Z_FILTERED}).returns(deflate)
|
||||
expect(zlib, "createDeflateRaw").given({windowBits: 15, level: _zlib.Z_DEFAULT_LEVEL, memLevel: memLevel, strategy: _zlib.Z_FILTERED}).returns(deflate)
|
||||
processOutgoingMessage()
|
||||
}})
|
||||
}})
|
||||
|
||||
Reference in New Issue
Block a user