Skip to content

feat: add support for multipart/form-data#1606

Merged
ronag merged 10 commits intonodejs:mainfrom
cameron-robey:multipart-formdata
Sep 28, 2022
Merged

feat: add support for multipart/form-data#1606
ronag merged 10 commits intonodejs:mainfrom
cameron-robey:multipart-formdata

Conversation

@cameron-robey
Copy link
Copy Markdown
Contributor

  • Add support for multipart/form-data

Closes #974

@JacobMGEvans
Copy link
Copy Markdown

Exciting!

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Aug 17, 2022

Codecov Report

Merging #1606 (b079acb) into main (fa9fd90) will decrease coverage by 0.64%.
The diff coverage is 92.59%.

@@            Coverage Diff             @@
##             main    #1606      +/-   ##
==========================================
- Coverage   95.21%   94.57%   -0.65%     
==========================================
  Files          50       53       +3     
  Lines        4748     4828      +80     
==========================================
+ Hits         4521     4566      +45     
- Misses        227      262      +35     
Impacted Files Coverage Δ
lib/fetch/body.js 96.46% <92.59%> (-0.65%) ⬇️
lib/dispatcher-base.js 91.48% <0.00%> (-7.23%) ⬇️
lib/core/request.js 96.89% <0.00%> (-3.11%) ⬇️
lib/fetch/file.js 91.76% <0.00%> (-1.10%) ⬇️
lib/client.js 96.98% <0.00%> (-0.78%) ⬇️
lib/core/util.js 97.46% <0.00%> (-0.64%) ⬇️
lib/proxy-agent.js 93.15% <0.00%> (-0.51%) ⬇️
index.js 100.00% <0.00%> (ø)
lib/pool.js 100.00% <0.00%> (ø)
lib/agent.js 100.00% <0.00%> (ø)
... and 14 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

Copy link
Copy Markdown
Member

@ronag ronag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about errors?

Comment thread lib/fetch/body.js
Copy link
Copy Markdown
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread lib/fetch/body.js Outdated
Copy link
Copy Markdown
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copy link
Copy Markdown
Member

@ronag ronag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI failure

Comment thread lib/fetch/body.js Outdated
})
})
busboy.on('error', (err) => {
throw Object.assign(new TypeError(), { cause: err })
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't just throw here... this will crash the process. Is there tests for this?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't just throw here... this will crash the process.

Do you have a suggestion?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mcollina
mcollina previously approved these changes Aug 23, 2022
Copy link
Copy Markdown
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Comment thread lib/fetch/body.js
@mcollina mcollina dismissed their stale review August 23, 2022 21:29

test is missing

Comment thread lib/fetch/body.js
@ronag
Copy link
Copy Markdown
Member

ronag commented Aug 26, 2022

@mcollina I think @KhafraDev is working on a multipart parser without any deps. Should we maybe wait and see how that go before landing this?

@kibertoad
Copy link
Copy Markdown
Contributor

kibertoad commented Aug 26, 2022

@ronag Do we expect new custom solution to work faster/smoother than a solution that was used across entire Node ecosystem for years?

What would be the criteria for comparison after it is implemented?

@mcollina
Copy link
Copy Markdown
Member

I would prefer we use busboy at this stage.

Comment thread lib/fetch/body.js
@KhafraDev
Copy link
Copy Markdown
Member

KhafraDev commented Aug 26, 2022

I would prefer we use busboy at this stage.

Yes, this was my sentiment from my comment in #974.

What would be the criteria for comparison after it is implemented?

At minimum it would need to pass all of busboy's tests (similar to what we do with node-fetch tests).

Copy link
Copy Markdown
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work, there are a couple of comments to fix and we'd be good for landing.

Comment thread lib/fetch/body.js
const base64 = encoding.toLowerCase() === 'base64'
const chunks = []
value.on('data', (chunk) => {
if (base64) chunk = Buffer.from(chunk.toString(), 'base64')
Copy link
Copy Markdown
Contributor

@repsac-by repsac-by Sep 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't just decrypt a base64 chunk, because the length of the base64 chunk must be a multiple of 4 characters.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand.

Copy link
Copy Markdown
Contributor

@repsac-by repsac-by Sep 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const data = Buffer.from(Buffer.from('hello world').toString('base64'));

const chunks1 = [];
const chunks2 = [];
for (let offset = 0, step = 6; offset < data.length; offset += step) {
	const chunk = data.subarray(offset, offset + step);

	// Decode each chunk
	chunks1.push(Buffer.from(chunk.toString(), 'base64'));

	// Here we collect as is
	chunks2.push(chunk);
}

// Decode after the transfer is completed
const buffer = Buffer.from(Buffer.concat(chunks2).toString(), 'base64');

// hell�v�ld
console.log(await new Blob(chunks1).text());

// hello world
console.log(await new Blob([buffer]).text());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the https://nodejs.org/api/string_decoder.html for this purpose.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mcollina does string decoder need to be applied somewhere in this code explicitly, or it will be called down the line automatically?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cameron-robey see what @repsac-by is suggesting, plus test to cover that case. inlining the code should be fine

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the problem exactly? You need 4 bytes to decode base64 at minimum, so why don't you accumulate until you have 4 bytes of multiples?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mcollina I'm assuming that you mean: push chunks until the total length of unprocessed data is divisible by 4, then decode and clear the chunk array, and repeat with the next pieces of data. Accumulating like that seems unreliable, since it cannot be known when that multiple of 4 will be reached. It might happen only after a huge number of chunks. The streaming method suggested above is superior because the number of unprocessed bytes that are kept in memory never exceeds 3.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The one in the comment also looks good.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cameron-robey Do you need any help with the remaining part?

@cameron-robey
Copy link
Copy Markdown
Contributor Author

Will get to finishing the last couple of things by the end of the week 👍

Comment thread lib/fetch/body.js
Copy link
Copy Markdown
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is mostly good to go.

Comment thread test/fetch/client-fetch.js Outdated
Comment thread test/fetch/client-fetch.js Outdated
Copy link
Copy Markdown
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@ronag ronag merged commit 2d38b7e into nodejs:main Sep 28, 2022
@cameron-robey cameron-robey deleted the multipart-formdata branch September 28, 2022 08:53
@kibertoad
Copy link
Copy Markdown
Contributor

@ronag why did we merge this without addressing all comments? should I create a follow-up PR?

@ronag
Copy link
Copy Markdown
Member

ronag commented Sep 28, 2022

@kibertoad Please open a follow-up PR.

@RangerMauve
Copy link
Copy Markdown

Is there somewhere we can track the inclusion of this update into Node?

@kibertoad
Copy link
Copy Markdown
Contributor

https://github.com/nodejs/node/pulls?q=is%3Apr+undici+is%3Aclosed

metcoder95 pushed a commit to metcoder95/undici that referenced this pull request Dec 26, 2022
* add support for multipart/form-data

* Handle busboy errors

* linting

* Catch emitted error

* reject promise instead of throwing error

* Add test for base64 encoded multipart/form-data

Thanks for the help @mrbbot !

* Move busboy from devDependencies to dependencies

* Add test for busboy emitting error

* Rewrite tests

* Update tests to avoid promises and callbacks
crysmags pushed a commit to crysmags/undici that referenced this pull request Feb 27, 2024
* add support for multipart/form-data

* Handle busboy errors

* linting

* Catch emitted error

* reject promise instead of throwing error

* Add test for base64 encoded multipart/form-data

Thanks for the help @mrbbot !

* Move busboy from devDependencies to dependencies

* Add test for busboy emitting error

* Rewrite tests

* Update tests to avoid promises and callbacks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement multipart parsing