diff --git a/lib/web/fetch/response.js b/lib/web/fetch/response.js index ab5e699cb41..5f11f449477 100644 --- a/lib/web/fetch/response.js +++ b/lib/web/fetch/response.js @@ -244,6 +244,11 @@ class Response { // 2. Let clonedResponse be the result of cloning this’s response. const clonedResponse = cloneResponse(this.#state) + // Note: To re-register because of a new stream. + if (this.#state.body?.stream) { + streamRegistry.register(this, new WeakRef(this.#state.body.stream)) + } + // 3. Return the result of creating a Response object, given // clonedResponse, this’s headers’s guard, and this’s relevant Realm. return fromInnerResponse(clonedResponse, getHeadersGuard(this.#headers)) @@ -354,8 +359,6 @@ function cloneResponse (response) { // result of cloning response’s body. if (response.body != null) { newResponse.body = cloneBody(response.body) - - streamRegistry.register(newResponse, new WeakRef(response.body.stream)) } // 4. Return newResponse. diff --git a/test/fetch/fire-and-forget.js b/test/fetch/fire-and-forget.js index b2c2b198ddd..8e0df453fca 100644 --- a/test/fetch/fire-and-forget.js +++ b/test/fetch/fire-and-forget.js @@ -1,12 +1,11 @@ 'use strict' const { randomFillSync } = require('node:crypto') -const { setTimeout: sleep } = require('node:timers/promises') +const { setTimeout: sleep, setImmediate: nextTick } = require('node:timers/promises') const { test } = require('node:test') -const { fetch, Request, Agent, setGlobalDispatcher } = require('../..') +const { fetch, Request, Response, Agent, setGlobalDispatcher } = require('../..') const { createServer } = require('node:http') const { closeServerAsPromise } = require('../utils/node-http') -const assert = require('node:assert') const blob = randomFillSync(new Uint8Array(1024 * 512)) @@ -20,14 +19,31 @@ test('test finalizer cloned request', async () => { const request = new Request('http://localhost', { method: 'POST', body: 'Hello' }) - for (let i = 0; i < 800; ++i) request.clone() + request.clone() - await sleep(50) + await nextTick() + // eslint-disable-next-line no-undef + gc() + + await nextTick() + await request.arrayBuffer() // check consume body +}) + +test('test finalizer cloned response', async () => { + if (!hasGC) { + throw new Error('gc is not available. Run with \'--expose-gc\'.') + } + + const response = new Response('Hello') + + response.clone() + await nextTick() // eslint-disable-next-line no-undef - gc(true) + gc() - assert.strictEqual(request.bodyUsed, false) + await nextTick() + await response.arrayBuffer() // check consume body }) test('does not need the body to be consumed to continue', { timeout: 180_000 }, async (t) => {