Skip to content

Commit e49b575

Browse files
authored
fix: sending formdata bodies with http2 (#3863)
1 parent e90e128 commit e49b575

File tree

2 files changed

+66
-2
lines changed

2 files changed

+66
-2
lines changed

lib/dispatcher/client-h2.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ const {
3232

3333
const kOpenStreams = Symbol('open streams')
3434

35+
let extractBody
36+
3537
// Experimental
3638
let h2ExperimentalWarned = false
3739

@@ -279,7 +281,8 @@ function shouldSendContentLength (method) {
279281

280282
function writeH2 (client, request) {
281283
const session = client[kHTTP2Session]
282-
const { body, method, path, host, upgrade, expectContinue, signal, headers: reqHeaders } = request
284+
const { method, path, host, upgrade, expectContinue, signal, headers: reqHeaders } = request
285+
let { body } = request
283286

284287
if (upgrade) {
285288
util.errorRequest(client, request, new Error('Upgrade not supported for H2'))
@@ -407,6 +410,16 @@ function writeH2 (client, request) {
407410

408411
let contentLength = util.bodyLength(body)
409412

413+
if (util.isFormDataLike(body)) {
414+
extractBody ??= require('../web/fetch/body.js').extractBody
415+
416+
const [bodyStream, contentType] = extractBody(body)
417+
headers['content-type'] = contentType
418+
419+
body = bodyStream.stream
420+
contentLength = bodyStream.length
421+
}
422+
410423
if (contentLength == null) {
411424
contentLength = request.contentLength
412425
}

test/http2.js

+52-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const { Writable, pipeline, PassThrough, Readable } = require('node:stream')
1010

1111
const pem = require('https-pem')
1212

13-
const { Client, Agent } = require('..')
13+
const { Client, Agent, FormData } = require('..')
1414

1515
const isGreaterThanv20 = process.versions.node.split('.').map(Number)[0] >= 20
1616

@@ -1642,3 +1642,54 @@ test('#3753 - Handle GOAWAY Gracefully', async (t) => {
16421642

16431643
await t.completed
16441644
})
1645+
1646+
test('#3803 - sending FormData bodies works', async (t) => {
1647+
const assert = tspl(t, { plan: 4 })
1648+
1649+
const server = createSecureServer(pem).listen(0)
1650+
server.on('stream', async (stream, headers) => {
1651+
const contentLength = Number(headers['content-length'])
1652+
1653+
assert.ok(!Number.isNaN(contentLength))
1654+
assert.ok(headers['content-type']?.startsWith('multipart/form-data; boundary='))
1655+
1656+
stream.respond({ ':status': 200 })
1657+
1658+
const fd = await new Response(stream, {
1659+
headers: {
1660+
'content-type': headers['content-type']
1661+
}
1662+
}).formData()
1663+
1664+
assert.deepEqual(fd.get('a'), 'b')
1665+
assert.deepEqual(fd.get('c').name, 'e.fgh')
1666+
1667+
stream.end()
1668+
})
1669+
1670+
await once(server, 'listening')
1671+
1672+
const client = new Client(`https://localhost:${server.address().port}`, {
1673+
connect: {
1674+
rejectUnauthorized: false
1675+
},
1676+
allowH2: true
1677+
})
1678+
1679+
t.after(async () => {
1680+
server.close()
1681+
await client.close()
1682+
})
1683+
1684+
const fd = new FormData()
1685+
fd.set('a', 'b')
1686+
fd.set('c', new Blob(['d']), 'e.fgh')
1687+
1688+
await client.request({
1689+
path: '/',
1690+
method: 'POST',
1691+
body: fd
1692+
})
1693+
1694+
await assert.completed
1695+
})

0 commit comments

Comments
 (0)