diff --git a/lib/internal/webstreams/adapters.js b/lib/internal/webstreams/adapters.js index 6c2be59a24a..307c3f537ec 100644 --- a/lib/internal/webstreams/adapters.js +++ b/lib/internal/webstreams/adapters.js @@ -459,6 +459,7 @@ function newReadableStreamFromStreamReadable(streamReadable, options = kEmptyObj const strategy = evaluateStrategyOrFallback(options?.strategy); let controller; + let wasCanceled = false; function onData(chunk) { // Copy the Buffer to detach it from the pool. @@ -480,6 +481,10 @@ function newReadableStreamFromStreamReadable(streamReadable, options = kEmptyObj streamReadable.on('error', () => {}); if (error) return controller.error(error); + // Was already canceled + if (wasCanceled) { + return; + } controller.close(); }); @@ -491,6 +496,7 @@ function newReadableStreamFromStreamReadable(streamReadable, options = kEmptyObj pull() { streamReadable.resume(); }, cancel(reason) { + wasCanceled = true; destroy(streamReadable, reason); }, }, strategy); diff --git a/test/parallel/test-stream-readable-from-web-termination.js b/test/parallel/test-stream-readable-from-web-termination.js new file mode 100644 index 00000000000..68ed7d69694 --- /dev/null +++ b/test/parallel/test-stream-readable-from-web-termination.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); +const { Readable } = require('stream'); + +{ + const r = Readable.from(['data']); + + const wrapper = Readable.fromWeb(Readable.toWeb(r)); + + wrapper.on('data', () => { + // Destroying wrapper while emitting data should not cause uncaught + // exceptions + wrapper.destroy(); + }); +} diff --git a/test/parallel/test-stream-readable-to-web-termination.js b/test/parallel/test-stream-readable-to-web-termination.js new file mode 100644 index 00000000000..13fce9bc715 --- /dev/null +++ b/test/parallel/test-stream-readable-to-web-termination.js @@ -0,0 +1,12 @@ +'use strict'; +require('../common'); +const { Readable } = require('stream'); + +{ + const r = Readable.from([]); + // Cancelling reader while closing should not cause uncaught exceptions + r.on('close', () => reader.cancel()); + + const reader = Readable.toWeb(r).getReader(); + reader.read(); +}