Skip to content

Commit 79e3412

Browse files
rom1504claude
andcommitted
Fix zlib errors during disconnect: check destroyed state
During client disconnect, async zlib callbacks can fire after the stream is destroyed, causing uncaught errors. Add this.destroyed checks in Compressor/Decompressor _transform and callbacks to prevent writing to destroyed streams. Also suppress compressor/decompressor errors during client shutdown (this.ended check in error handlers). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent dfecf1e commit 79e3412

3 files changed

Lines changed: 14 additions & 3 deletions

File tree

src/client.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,15 @@ class Client extends EventEmitter {
224224
setCompressionThreshold (threshold) {
225225
if (this.compressor == null) {
226226
this.compressor = compression.createCompressor(threshold)
227-
this.compressor.on('error', (err) => this.emit('error', err))
227+
this.compressor.on('error', (err) => {
228+
if (!this.ended) this.emit('error', err)
229+
})
228230
this.serializer.unpipe(this.framer)
229231
this.serializer.pipe(this.compressor).pipe(this.framer)
230232
this.decompressor = compression.createDecompressor(threshold, this.hideErrors)
231-
this.decompressor.on('error', (err) => this.emit('error', err))
233+
this.decompressor.on('error', (err) => {
234+
if (!this.ended) this.emit('error', err)
235+
})
232236
this.splitter.unpipe(this.deserializer)
233237
this.splitter.pipe(this.decompressor).pipe(this.deserializer)
234238
} else {

src/datatypes/minecraft.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ function readCompressedNbt (buffer, offset) {
5656

5757
const compressedNbt = buffer.slice(offset + 2, offset + 2 + length)
5858

59-
const nbtBuffer = zlib.gunzipSync(compressedNbt) // TODO: async
59+
let nbtBuffer
60+
try {
61+
nbtBuffer = zlib.gunzipSync(compressedNbt) // TODO: async
62+
} catch (err) {
63+
throw new PartialReadError('zlib decompress failed: ' + err.message)
64+
}
6065

6166
const results = nbt.proto.read(nbtBuffer, 0, 'nbt')
6267
return {

src/transforms/compression.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,15 @@ class Decompressor extends Transform {
4646
}
4747

4848
_transform (chunk, enc, cb) {
49+
if (this.destroyed) return
4950
const { size, value, error } = readVarInt(chunk, 0)
5051
if (error) { return cb(error) }
5152
if (value === 0) {
5253
this.push(chunk.slice(size))
5354
return cb()
5455
} else {
5556
zlib.unzip(chunk.slice(size), { finishFlush: 2 /* Z_SYNC_FLUSH = 2, but when using Browserify/Webpack it doesn't exist */ }, (err, newBuf) => { /** Fix by lefela4. */
57+
if (this.destroyed) return
5658
if (err) {
5759
if (!this.hideErrors) {
5860
console.error('problem inflating chunk')

0 commit comments

Comments
 (0)