Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: sending formdata bodies with http2 #3863

Merged
merged 1 commit into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion lib/dispatcher/client-h2.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const {

const kOpenStreams = Symbol('open streams')

let extractBody

// Experimental
let h2ExperimentalWarned = false

Expand Down Expand Up @@ -279,7 +281,8 @@ function shouldSendContentLength (method) {

function writeH2 (client, request) {
const session = client[kHTTP2Session]
const { body, method, path, host, upgrade, expectContinue, signal, headers: reqHeaders } = request
const { method, path, host, upgrade, expectContinue, signal, headers: reqHeaders } = request
let { body } = request

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

let contentLength = util.bodyLength(body)

if (util.isFormDataLike(body)) {
extractBody ??= require('../web/fetch/body.js').extractBody

const [bodyStream, contentType] = extractBody(body)
headers['content-type'] = contentType

body = bodyStream.stream
contentLength = bodyStream.length
}

if (contentLength == null) {
contentLength = request.contentLength
}
Expand Down
53 changes: 52 additions & 1 deletion test/http2.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const { Writable, pipeline, PassThrough, Readable } = require('node:stream')

const pem = require('https-pem')

const { Client, Agent } = require('..')
const { Client, Agent, FormData } = require('..')

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

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

await t.completed
})

test('#3803 - sending FormData bodies works', async (t) => {
const assert = tspl(t, { plan: 4 })

const server = createSecureServer(pem).listen(0)
server.on('stream', async (stream, headers) => {
const contentLength = Number(headers['content-length'])

assert.ok(!Number.isNaN(contentLength))
assert.ok(headers['content-type']?.startsWith('multipart/form-data; boundary='))

stream.respond({ ':status': 200 })

const fd = await new Response(stream, {
headers: {
'content-type': headers['content-type']
}
}).formData()

assert.deepEqual(fd.get('a'), 'b')
assert.deepEqual(fd.get('c').name, 'e.fgh')

stream.end()
})

await once(server, 'listening')

const client = new Client(`https://localhost:${server.address().port}`, {
connect: {
rejectUnauthorized: false
},
allowH2: true
})

t.after(async () => {
server.close()
await client.close()
})

const fd = new FormData()
fd.set('a', 'b')
fd.set('c', new Blob(['d']), 'e.fgh')

await client.request({
path: '/',
method: 'POST',
body: fd
})

await assert.completed
})
Loading