Skip to content

Commit

Permalink
Write after end fix (#97)
Browse files Browse the repository at this point in the history
* add failing test

* fix write-after-end edgecase
  • Loading branch information
mafintosh authored Nov 13, 2024
1 parent 15d19e8 commit 3ce8b9b
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 3 deletions.
8 changes: 5 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const WRITE_CORKED = 0b10000000000 << 18

const WRITE_NOT_ACTIVE = MAX ^ (WRITE_ACTIVE | WRITE_WRITING)
const WRITE_NON_PRIMARY = MAX ^ WRITE_PRIMARY
const WRITE_NOT_FINISHING = MAX ^ WRITE_FINISHING
const WRITE_NOT_FINISHING = MAX ^ (WRITE_ACTIVE | WRITE_FINISHING)
const WRITE_DRAINED = MAX ^ WRITE_UNDRAINED
const WRITE_NOT_QUEUED = MAX ^ WRITE_QUEUED
const WRITE_NOT_NEXT_TICK = MAX ^ WRITE_NEXT_TICK
Expand Down Expand Up @@ -110,6 +110,7 @@ const WRITE_ACTIVE_AND_WRITING = WRITE_ACTIVE | WRITE_WRITING
const WRITE_FINISHING_STATUS = OPEN_STATUS | WRITE_FINISHING | WRITE_QUEUED_AND_ACTIVE | WRITE_DONE
const WRITE_BACKPRESSURE_STATUS = WRITE_UNDRAINED | DESTROY_STATUS | WRITE_FINISHING | WRITE_DONE
const WRITE_UPDATE_SYNC_STATUS = WRITE_UPDATING | OPEN_STATUS | WRITE_NEXT_TICK | WRITE_PRIMARY
const WRITE_DROP_DATA = WRITE_FINISHING | WRITE_DONE | DESTROY_STATUS

const asyncIterator = Symbol.asyncIterator || Symbol('asyncIterator')

Expand All @@ -133,6 +134,7 @@ class WritableState {
}

push (data) {
if ((this.stream._duplexState & WRITE_DROP_DATA) !== 0) return false
if (this.map !== null) data = this.map(data)

this.buffered += this.byteLength(data)
Expand Down Expand Up @@ -197,7 +199,7 @@ class WritableState {
const stream = this.stream

if ((stream._duplexState & WRITE_FINISHING_STATUS) === WRITE_FINISHING) {
stream._duplexState = (stream._duplexState | WRITE_ACTIVE) & WRITE_NOT_FINISHING
stream._duplexState = stream._duplexState | WRITE_ACTIVE
stream._final(afterFinal.bind(this))
return
}
Expand Down Expand Up @@ -489,7 +491,7 @@ function afterFinal (err) {
stream._duplexState |= DESTROYING
}

stream._duplexState &= WRITE_NOT_ACTIVE
stream._duplexState &= WRITE_NOT_FINISHING

// no need to wait the extra tick here, so we short circuit that
if ((stream._duplexState & WRITE_UPDATING) === 0) this.update()
Expand Down
23 changes: 23 additions & 0 deletions test/duplex.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,26 @@ test('wait for readable', function (t) {
t.is(d.read(), 'ok')
})
})

test('write during end', function (t) {
t.plan(3)

const expected = ['a', 'b']

const w = new Duplex({
write (data, cb) {
t.is(data, expected.shift())
cb(null)
},
final (cb) {
w.write('bad')
cb(null)
}
})

w.write('a')
w.write('b')
w.end()
w.on('finish', () => w.push(null))
w.on('close', () => t.pass('closed'))
})

0 comments on commit 3ce8b9b

Please sign in to comment.