Skip to content

Commit

Permalink
fix(fetch): set content-length header for FormData body (nodejs#1785)
Browse files Browse the repository at this point in the history
* fix(fetch): set content-length header for FormData body

* fix: skip test on v16

* fix: remove test

* test: add test back
  • Loading branch information
KhafraDev authored and crysmags committed Feb 27, 2024
1 parent ac88800 commit aa75139
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 1 deletion.
32 changes: 31 additions & 1 deletion lib/fetch/body.js
Expand Up @@ -142,7 +142,37 @@ function extractBody (object, keepalive = false) {
source = object

// Set length to unclear, see html/6424 for improving this.
// TODO
length = (() => {
const prefixLength = prefix.length
const boundaryLength = boundary.length
let bodyLength = 0

for (const [name, value] of object) {
if (typeof value === 'string') {
bodyLength +=
prefixLength +
Buffer.byteLength(`; name="${escape(normalizeLinefeeds(name))}"`) +
Buffer.byteLength(`\r\n\r\n${normalizeLinefeeds(value)}\r\n`)
} else {
bodyLength +=
prefixLength +
Buffer.byteLength(`; name="${escape(normalizeLinefeeds(name))}"`) +
(value.name ? Buffer.byteLength(`; filename="${escape(value.name)}"`) : 0) +
2 + // \r\n
`Content-Type: ${
value.type || 'application/octet-stream'
}\r\n\r\n`.length

// value is a Blob or File
bodyLength += value.size

bodyLength += 2 // \r\n
}
}

bodyLength += boundaryLength + 4 // --boundary--
return bodyLength
})()

// Set type to `multipart/form-data; boundary=`,
// followed by the multipart/form-data boundary string generated
Expand Down
29 changes: 29 additions & 0 deletions test/fetch/content-length.js
@@ -0,0 +1,29 @@
'use strict'

const { test } = require('tap')
const { createServer } = require('http')
const { once } = require('events')
const { Blob } = require('buffer')
const { fetch, FormData } = require('../..')

// https://github.com/nodejs/undici/issues/1783
test('Content-Length is set when using a FormData body with fetch', async (t) => {
const server = createServer((req, res) => {
// TODO: check the length's value once the boundary has a fixed length
t.ok('content-length' in req.headers) // request has content-length header
t.ok(!Number.isNaN(Number(req.headers['content-length'])))
res.end()
}).listen(0)

await once(server, 'listening')
t.teardown(server.close.bind(server))

const fd = new FormData()
fd.set('file', new Blob(['hello world 👋'], { type: 'text/plain' }), 'readme.md')
fd.set('string', 'some string value')

await fetch(`http://localhost:${server.address().port}`, {
method: 'POST',
body: fd
})
})

0 comments on commit aa75139

Please sign in to comment.