diff --git a/lib/internal/webstreams/readablestream.js b/lib/internal/webstreams/readablestream.js index 78313bd03c1a..160e91a60bce 100644 --- a/lib/internal/webstreams/readablestream.js +++ b/lib/internal/webstreams/readablestream.js @@ -1760,6 +1760,7 @@ function readableStreamPipeTo( function forwardChunk() { const chunk = pendingChunk; pendingChunk = undefined; + if (writer[kState].stream === undefined) return; writableStreamDefaultWriterWriteWithRequest(writer, chunk, writeTracker); pump(); } diff --git a/test/parallel/test-webstreams-pipeto-writer-released-race.js b/test/parallel/test-webstreams-pipeto-writer-released-race.js new file mode 100644 index 000000000000..20e0933bd60b --- /dev/null +++ b/test/parallel/test-webstreams-pipeto-writer-released-race.js @@ -0,0 +1,63 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { ReadableStream, WritableStream } = require('stream/web'); + +{ + let sourceController; + let destController; + + const source = new ReadableStream({ + start(controller) { + sourceController = controller; + }, + }); + + const dest = new WritableStream({ + start(controller) { + destController = controller; + }, + write() {}, + }); + + assert.rejects( + source.pipeTo(dest, { preventCancel: true }), + { message: 'destination errored' }, + ).then(common.mustCall()); + + setImmediate(common.mustCall(() => { + destController.error(new Error('destination errored')); + sourceController.enqueue('chunk'); + })); +} + +{ + const ac = new AbortController(); + let sourceController; + const chunks = []; + + const source = new ReadableStream({ + start(controller) { + sourceController = controller; + }, + }, { highWaterMark: 0 }); + + const dest = new WritableStream({ + write: common.mustCall((chunk) => { + chunks.push(chunk); + }), + }, { highWaterMark: 1 }); + + assert.rejects( + source.pipeTo(dest, { signal: ac.signal }), + { name: 'AbortError' }, + ).then(common.mustCall(() => { + assert.deepStrictEqual(chunks, ['chunk']); + })); + + setImmediate(common.mustCall(() => { + sourceController.enqueue('chunk'); + ac.abort(); + })); +}