176 lines
4.7 KiB
JavaScript
176 lines
4.7 KiB
JavaScript
'use strict';
|
|
|
|
var Buffer = require('safe-buffer').Buffer,
|
|
zlib = require('zlib'),
|
|
common = require('./common');
|
|
|
|
var Session = function(options) {
|
|
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);
|
|
|
|
this._acceptNoContextTakeover = common.fetch(options, 'noContextTakeover', false);
|
|
this._acceptMaxWindowBits = common.fetch(options, 'maxWindowBits', undefined);
|
|
this._requestNoContextTakeover = common.fetch(options, 'requestNoContextTakeover', false);
|
|
this._requestMaxWindowBits = common.fetch(options, 'requestMaxWindowBits', undefined);
|
|
|
|
this._queueIn = [];
|
|
this._queueOut = [];
|
|
|
|
this._zlib = common.fetch(options, 'zlib', zlib);
|
|
};
|
|
|
|
Session.prototype.processIncomingMessage = function(message, callback) {
|
|
if (!message.rsv1) return callback(null, message);
|
|
if (this._lockIn) return this._queueIn.push([message, callback]);
|
|
|
|
var inflate = this._getInflate(),
|
|
chunks = [],
|
|
length = 0,
|
|
self = this;
|
|
|
|
if (this._inflate) this._lockIn = true;
|
|
|
|
var return_ = function(error, message) {
|
|
return_ = function() {};
|
|
|
|
inflate.removeListener('data', onData);
|
|
inflate.removeListener('error', onError);
|
|
if (!self._inflate) self._close(inflate);
|
|
|
|
self._lockIn = false;
|
|
var next = self._queueIn.shift();
|
|
if (next) self.processIncomingMessage.apply(self, next);
|
|
|
|
callback(error, message);
|
|
};
|
|
|
|
var onData = function(data) {
|
|
chunks.push(data);
|
|
length += data.length;
|
|
};
|
|
|
|
var onError = function(error) {
|
|
return_(error, null);
|
|
};
|
|
|
|
inflate.on('data', onData);
|
|
inflate.on('error', onError);
|
|
|
|
inflate.write(message.data);
|
|
inflate.write(Buffer.from([0x00, 0x00, 0xff, 0xff]));
|
|
|
|
inflate.flush(function() {
|
|
message.data = Buffer.concat(chunks, length);
|
|
return_(null, message);
|
|
});
|
|
};
|
|
|
|
Session.prototype.processOutgoingMessage = function(message, callback) {
|
|
if (this._lockOut) return this._queueOut.push([message, callback]);
|
|
|
|
var deflate = this._getDeflate(),
|
|
chunks = [],
|
|
length = 0,
|
|
self = this;
|
|
|
|
if (this._deflate) this._lockOut = true;
|
|
|
|
var return_ = function(error, message) {
|
|
return_ = function() {};
|
|
|
|
deflate.removeListener('data', onData);
|
|
deflate.removeListener('error', onError);
|
|
if (!self._deflate) self._close(deflate);
|
|
|
|
self._lockOut = false;
|
|
var next = self._queueOut.shift();
|
|
if (next) self.processOutgoingMessage.apply(self, next);
|
|
|
|
callback(error, message);
|
|
};
|
|
|
|
var onData = function(data) {
|
|
chunks.push(data);
|
|
length += data.length;
|
|
};
|
|
|
|
var onError = function(error) {
|
|
return_(error, null);
|
|
};
|
|
|
|
deflate.on('data', onData);
|
|
deflate.on('error', onError);
|
|
deflate.write(message.data);
|
|
|
|
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() {
|
|
this._close(this._inflate);
|
|
this._inflate = null;
|
|
|
|
this._close(this._deflate);
|
|
this._deflate = null;
|
|
};
|
|
|
|
Session.prototype._getInflate = function() {
|
|
if (this._inflate) return this._inflate;
|
|
|
|
var windowBits = Math.max(this._peerWindowBits, common.MIN_WINDOW_BITS),
|
|
inflate = this._zlib.createInflateRaw({ windowBits: windowBits });
|
|
|
|
if (this._peerContextTakeover) this._inflate = inflate;
|
|
return inflate;
|
|
};
|
|
|
|
Session.prototype._getDeflate = function() {
|
|
if (this._deflate) return this._deflate;
|
|
|
|
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;
|