diff --git a/lib/fetch/body.js b/lib/fetch/body.js index c291afa9368..db450ee6bd4 100644 --- a/lib/fetch/body.js +++ b/lib/fetch/body.js @@ -123,6 +123,7 @@ function extractBody (object, keepalive = false) { const blobParts = [] const rn = new Uint8Array([13, 10]) // '\r\n' length = 0 + let hasUnknownSizeValue = false for (const [name, value] of object) { if (typeof value === 'string') { @@ -138,13 +139,20 @@ function extractBody (object, keepalive = false) { value.type || 'application/octet-stream' }\r\n\r\n`) blobParts.push(chunk, value, rn) - length += chunk.byteLength + value.size + rn.byteLength + if (typeof value.size === 'number') { + length += chunk.byteLength + value.size + rn.byteLength + } else { + hasUnknownSizeValue = true + } } } const chunk = enc.encode(`--${boundary}--`) blobParts.push(chunk) length += chunk.byteLength + if (hasUnknownSizeValue) { + length = null + } // Set source to object. source = object diff --git a/test/issue-2065.js b/test/issue-2065.js index 84183f0fb7c..cc288c48a74 100644 --- a/test/issue-2065.js +++ b/test/issue-2065.js @@ -4,6 +4,7 @@ const { test, skip } = require('tap') const { nodeMajor, nodeMinor } = require('../lib/core/util') const { createServer } = require('http') const { once } = require('events') +const { createReadStream } = require('fs') const { File, FormData, request } = require('..') if (nodeMajor < 16 || (nodeMajor === 16 && nodeMinor < 8)) { @@ -28,3 +29,43 @@ test('undici.request with a FormData body should set content-length header', asy body }) }) + +test('undici.request with a FormData stream value should set transfer-encoding header', async (t) => { + const server = createServer((req, res) => { + t.equal(req.headers['transfer-encoding'], 'chunked') + res.end() + }).listen(0) + + t.teardown(server.close.bind(server)) + await once(server, 'listening') + + class BlobFromStream { + #stream + #type + constructor (stream, type) { + this.#stream = stream + this.#type = type + } + + stream () { + return this.#stream + } + + get type () { + return this.#type + } + + get [Symbol.toStringTag] () { + return 'Blob' + } + } + + const body = new FormData() + const fileReadable = createReadStream(__filename) + body.set('file', new BlobFromStream(fileReadable, '.js'), 'streamfile') + + await request(`http://localhost:${server.address().port}`, { + method: 'POST', + body + }) +})