diff --git a/lib/protocol/zlib.js b/lib/protocol/zlib.js index f68319a1..feb61e55 100644 --- a/lib/protocol/zlib.js +++ b/lib/protocol/zlib.js @@ -177,8 +177,10 @@ class ZlibPacketWriter { } cleanup() { - if (this._zlib) + if (this._zlib) { _close(this._zlib); + this._zlib = undefined; + } } alloc(payloadSize, force) { @@ -186,6 +188,13 @@ class ZlibPacketWriter { } finalize(payload, force) { + // A write may still be flushed after cleanup (e.g. a queued channel + // request being flushed while the connection is being torn down), in + // which case there is nothing left to compress or send + + if (this._zlib === undefined) + return; + if (this._protocol._kexinit === undefined || force) { const output = this._zlib.writeSync(payload, true); const packet = this._protocol._cipher.allocPacket(output.totalLen); diff --git a/test/test-misc-client-server.js b/test/test-misc-client-server.js index 2dd5a29d..63b30d45 100644 --- a/test/test-misc-client-server.js +++ b/test/test-misc-client-server.js @@ -1458,3 +1458,37 @@ const setup = setupSimple.bind(undefined, debug); })); })); } + +{ + const { client, server } = setup_( + 'Outbound data flushed after cleanup should not throw (compression)', + { + client: { + ...clientCfg, + algorithms: { compress: [ 'zlib' ] }, + }, + server: { + ...serverCfg, + algorithms: { compress: [ 'zlib' ] }, + }, + }, + ); + + server.on('connection', mustCall((conn) => { + conn.on('authentication', mustCall((ctx) => { + ctx.accept(); + })).on('ready', mustCall(() => {})); + })); + + client.on('handshake', mustCall((info) => { + assert(info.cs.compress === 'zlib', 'wrong compress value'); + })).on('ready', mustCall(() => { + client.end(); + })).on('close', mustCall(() => { + // The protocol instance (and with it the zlib instances) is cleaned up + // before this event is emitted. Data that was still queued when the + // connection went away can get flushed after that point, and doing so + // must not throw, since there is nothing that could catch it. + client._protocol.channelData(0, Buffer.from('foo')); + })); +}