diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2789b6b5c810b0..4b4e64ae788cc1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,7 +36,8 @@ release.
+21.6.1
21.6.0
21.5.0
21.4.0
@@ -42,6 +43,19 @@
* [io.js](CHANGELOG_IOJS.md)
* [Archive](CHANGELOG_ARCHIVE.md)
+
+
+## 2024-01-22, Version 21.6.1 (Current), @RafaelGSS
+
+### Notable Changes
+
+This release fixes a bug in `undici` using WebStreams
+
+### Commits
+
+* \[[`662ac95729`](https://github.com/nodejs/node/commit/662ac95729)] - _**Revert**_ "**stream**: fix cloned webstreams not being unref'd" (Matteo Collina) [#51491](https://github.com/nodejs/node/pull/51491)
+* \[[`1b8bba8aee`](https://github.com/nodejs/node/commit/1b8bba8aee)] - **test**: add regression test for 51586 (Matteo Collina) [#51491](https://github.com/nodejs/node/pull/51491)
+
## 2024-01-15, Version 21.6.0 (Current), @RafaelGSS
diff --git a/lib/internal/webstreams/readablestream.js b/lib/internal/webstreams/readablestream.js
index c7f492033ceaf8..35656216680875 100644
--- a/lib/internal/webstreams/readablestream.js
+++ b/lib/internal/webstreams/readablestream.js
@@ -596,8 +596,6 @@ class ReadableStream {
[kTransferList]() {
const { port1, port2 } = new MessageChannel();
- port1.unref();
- port2.unref();
this[kState].transfer.port1 = port1;
this[kState].transfer.port2 = port2;
return [ port2 ];
diff --git a/lib/internal/webstreams/transfer.js b/lib/internal/webstreams/transfer.js
index c4cb4077f88403..136b0d81a99464 100644
--- a/lib/internal/webstreams/transfer.js
+++ b/lib/internal/webstreams/transfer.js
@@ -143,8 +143,6 @@ class CrossRealmTransformReadableSource {
error);
port.close();
};
-
- port.unref();
}
start(controller) {
@@ -212,7 +210,7 @@ class CrossRealmTransformWritableSink {
error);
port.close();
};
- port.unref();
+
}
start(controller) {
diff --git a/lib/internal/webstreams/writablestream.js b/lib/internal/webstreams/writablestream.js
index c6f5c22bc7a95e..954bc6c2cc93d8 100644
--- a/lib/internal/webstreams/writablestream.js
+++ b/lib/internal/webstreams/writablestream.js
@@ -266,8 +266,6 @@ class WritableStream {
[kTransferList]() {
const { port1, port2 } = new MessageChannel();
- port1.unref();
- port2.unref();
this[kState].transfer.port1 = port1;
this[kState].transfer.port2 = port2;
return [ port2 ];
diff --git a/src/node_version.h b/src/node_version.h
index 9bd5780a565438..e9c4f77782ab75 100644
--- a/src/node_version.h
+++ b/src/node_version.h
@@ -29,7 +29,7 @@
#define NODE_VERSION_IS_LTS 0
#define NODE_VERSION_LTS_CODENAME ""
-#define NODE_VERSION_IS_RELEASE 0
+#define NODE_VERSION_IS_RELEASE 1
#ifndef NODE_STRINGIFY
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
diff --git a/test/parallel/test-webstream-structured-clone-no-leftovers.mjs b/test/parallel/test-webstream-structured-clone-no-leftovers.mjs
new file mode 100644
index 00000000000000..e8af095cce18d5
--- /dev/null
+++ b/test/parallel/test-webstream-structured-clone-no-leftovers.mjs
@@ -0,0 +1,28 @@
+import '../common/index.mjs';
+import { test } from 'node:test';
+import assert from 'node:assert';
+
+test('do not leak promises', async () => {
+ const buf = new Uint8Array(1);
+ const readable = new ReadableStream({
+ start(controller) {
+ controller.enqueue(buf);
+ controller.close();
+ }
+ });
+
+ const [out1, out2] = readable.tee();
+ const cloned = structuredClone(out2, { transfer: [out2] });
+
+ for await (const chunk of cloned) {
+ assert.deepStrictEqual(chunk, buf);
+ }
+
+ for await (const chunk of out2) {
+ assert.deepStrictEqual(chunk, buf);
+ }
+
+ for await (const chunk of out1) {
+ assert.deepStrictEqual(chunk, buf);
+ }
+});
diff --git a/test/parallel/test-webstreams-clone-unref.js b/test/parallel/test-webstreams-clone-unref.js
deleted file mode 100644
index 88a9cebd9c3046..00000000000000
--- a/test/parallel/test-webstreams-clone-unref.js
+++ /dev/null
@@ -1,16 +0,0 @@
-'use strict';
-
-require('../common');
-const { ok } = require('node:assert');
-
-// This test verifies that cloned ReadableStream and WritableStream instances
-// do not keep the process alive. The test fails if it timesout (it should just
-// exit immediately)
-
-const rs1 = new ReadableStream();
-const ws1 = new WritableStream();
-
-const [rs2, ws2] = structuredClone([rs1, ws1], { transfer: [rs1, ws1] });
-
-ok(rs2 instanceof ReadableStream);
-ok(ws2 instanceof WritableStream);
diff --git a/test/parallel/test-whatwg-webstreams-transfer.js b/test/parallel/test-whatwg-webstreams-transfer.js
index 7be01c339652c0..01cfaa02ad075e 100644
--- a/test/parallel/test-whatwg-webstreams-transfer.js
+++ b/test/parallel/test-whatwg-webstreams-transfer.js
@@ -464,23 +464,12 @@ const theData = 'hello';
tracker.verify();
});
- // We create an interval to keep the event loop alive while
- // we wait for the stream read to complete. The reason this is needed is because there's
- // otherwise nothing to keep the worker thread event loop alive long enough to actually
- // complete the read from the stream. Under the covers the ReadableStream uses an
- // unref'd MessagePort to communicate with the main thread. Because the MessagePort
- // is unref'd, it's existence would not keep the thread alive on its own. There was previously
- // a bug where this MessagePort was ref'd which would block the thread and main thread
- // from terminating at all unless the stream was consumed/closed.
- const i = setInterval(() => {}, 1000);
-
parentPort.onmessage = tracker.calls(({ data }) => {
assert(isReadableStream(data));
const reader = data.getReader();
reader.read().then(tracker.calls((result) => {
assert(!result.done);
assert(result.value instanceof Uint8Array);
- clearInterval(i);
}));
parentPort.close();
});
|