Repository navigation
extractBody fixes 2 - #5926
extractBody fixes 2#5926
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5926 +/- ##
==========================================
- Coverage 94.07% 94.07% -0.01%
==========================================
Files 110 110
Lines 40293 40302 +9
==========================================
+ Hits 37905 37913 +8
- Misses 2388 2389 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
This also fixes a crash that is on const { Request, FormData } = require('undici')
const form = new FormData()
form.append('file', new File([new Uint8Array(8000)], 'a.bin'))
new Request('http://localhost', { method: 'POST', body: form }).body.cancel()The detached loop only checks On this branch at 56c2336 the repro is clean on Node 24 and 26, since Nothing in test/fetch/formdata-body-cancel.js'use strict'
const { test } = require('node:test')
const { Request, Response, FormData } = require('../..')
function makeFormData () {
const form = new FormData()
form.append('title', 'a')
form.append('a', new File([new Uint8Array(8000)], 'a.bin'))
form.append('b', new File([new Uint8Array(8000)], 'b.bin'))
return form
}
// The parts of a FormData body are enqueued in the background. Cancelling the
// body closes the stream, and the next enqueue used to throw ERR_INVALID_STATE
// as an unhandled rejection.
async function cancelAfter (t, body, reads) {
const unhandled = []
const onUnhandledRejection = (err) => unhandled.push(err)
process.on('unhandledRejection', onUnhandledRejection)
t.after(() => process.off('unhandledRejection', onUnhandledRejection))
const reader = body.getReader()
for (let i = 0; i < reads; i++) {
const { done } = await reader.read()
t.assert.strictEqual(done, false)
}
await reader.cancel()
// Let the background enqueue run against the closed stream.
await new Promise((resolve) => setTimeout(resolve, 50))
t.assert.deepStrictEqual(unhandled, [])
}
test('cancelling an unread FormData request body does not reject', async (t) => {
const request = new Request('http://localhost', { method: 'POST', body: makeFormData() })
await cancelAfter(t, request.body, 0)
})
test('cancelling a partially read FormData request body does not reject', async (t) => {
const request = new Request('http://localhost', { method: 'POST', body: makeFormData() })
await cancelAfter(t, request.body, 2)
})
test('cancelling an unread FormData response body does not reject', async (t) => {
await cancelAfter(t, new Response(makeFormData()).body, 0)
})
test('cancelling a partially read FormData response body does not reject', async (t) => {
await cancelAfter(t, new Response(makeFormData()).body, 2)
})
test('a FormData body that is not cancelled is still read in full', async (t) => {
const request = new Request('http://localhost', { method: 'POST', body: makeFormData() })
const form = await request.formData()
t.assert.strictEqual(form.get('title'), 'a')
t.assert.strictEqual(form.get('a').size, 8000)
t.assert.strictEqual(form.get('b').size, 8000)
}) |
Closes #5923 which isn't a bug, but this approach is better than before, any ways
This relates to...
Rationale
Changes
Features
Bug Fixes
Breaking Changes and Deprecations
Status