Skip to content

Commit 23423ed

Browse files
authored
fix: better set a finalizer on cloned response (#4419)
1 parent 3abbea5 commit 23423ed

2 files changed

Lines changed: 28 additions & 9 deletions

File tree

lib/web/fetch/response.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ class Response {
244244
// 2. Let clonedResponse be the result of cloning this’s response.
245245
const clonedResponse = cloneResponse(this.#state)
246246

247+
// Note: To re-register because of a new stream.
248+
if (this.#state.body?.stream) {
249+
streamRegistry.register(this, new WeakRef(this.#state.body.stream))
250+
}
251+
247252
// 3. Return the result of creating a Response object, given
248253
// clonedResponse, this’s headers’s guard, and this’s relevant Realm.
249254
return fromInnerResponse(clonedResponse, getHeadersGuard(this.#headers))
@@ -354,8 +359,6 @@ function cloneResponse (response) {
354359
// result of cloning response’s body.
355360
if (response.body != null) {
356361
newResponse.body = cloneBody(response.body)
357-
358-
streamRegistry.register(newResponse, new WeakRef(response.body.stream))
359362
}
360363

361364
// 4. Return newResponse.

test/fetch/fire-and-forget.js

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
'use strict'
22

33
const { randomFillSync } = require('node:crypto')
4-
const { setTimeout: sleep } = require('node:timers/promises')
4+
const { setTimeout: sleep, setImmediate: nextTick } = require('node:timers/promises')
55
const { test } = require('node:test')
6-
const { fetch, Request, Agent, setGlobalDispatcher } = require('../..')
6+
const { fetch, Request, Response, Agent, setGlobalDispatcher } = require('../..')
77
const { createServer } = require('node:http')
88
const { closeServerAsPromise } = require('../utils/node-http')
9-
const assert = require('node:assert')
109

1110
const blob = randomFillSync(new Uint8Array(1024 * 512))
1211

@@ -20,14 +19,31 @@ test('test finalizer cloned request', async () => {
2019

2120
const request = new Request('http://localhost', { method: 'POST', body: 'Hello' })
2221

23-
for (let i = 0; i < 800; ++i) request.clone()
22+
request.clone()
2423

25-
await sleep(50)
24+
await nextTick()
25+
// eslint-disable-next-line no-undef
26+
gc()
27+
28+
await nextTick()
29+
await request.arrayBuffer() // check consume body
30+
})
31+
32+
test('test finalizer cloned response', async () => {
33+
if (!hasGC) {
34+
throw new Error('gc is not available. Run with \'--expose-gc\'.')
35+
}
36+
37+
const response = new Response('Hello')
38+
39+
response.clone()
2640

41+
await nextTick()
2742
// eslint-disable-next-line no-undef
28-
gc(true)
43+
gc()
2944

30-
assert.strictEqual(request.bodyUsed, false)
45+
await nextTick()
46+
await response.arrayBuffer() // check consume body
3147
})
3248

3349
test('does not need the body to be consumed to continue', { timeout: 180_000 }, async (t) => {

0 commit comments

Comments
 (0)