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

feature: add support for request iterator body #848

Merged
merged 25 commits into from
Jun 29, 2021

Conversation

Linkgoron
Copy link
Member

@Linkgoron Linkgoron commented Jun 22, 2021

continuation of #363

closes #198

lib/client.js Outdated Show resolved Hide resolved
lib/client.js Outdated Show resolved Hide resolved
@codecov-commenter
Copy link

codecov-commenter commented Jun 22, 2021

Codecov Report

Merging #848 (7731088) into main (9a7d068) will decrease coverage by 0.18%.
The diff coverage is 99.19%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #848      +/-   ##
==========================================
- Coverage   99.90%   99.71%   -0.19%     
==========================================
  Files          26       26              
  Lines        2050     2129      +79     
==========================================
+ Hits         2048     2123      +75     
- Misses          2        6       +4     
Impacted Files Coverage Δ
lib/client.js 99.36% <99.14%> (-0.36%) ⬇️
lib/core/request.js 100.00% <100.00%> (ø)
lib/core/util.js 100.00% <100.00%> (ø)
lib/handler/redirect.js 98.18% <0.00%> (-1.82%) ⬇️
lib/agent.js 100.00% <0.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9a7d068...7731088. Read the comment docs.

lib/client.js Outdated Show resolved Hide resolved
@Linkgoron
Copy link
Member Author

Linkgoron commented Jun 25, 2021

@mcollina Does it make sense to call body.return() on a body Iterator in places that util.destroy(body) is called on a stream (e.g. when throwing in the request constructor etc)? I'm not sure if it makes sense to add the logic to util.destroy, or somewhere else.

@mcollina
Copy link
Member

@mcollina Does it make sense call body.return() on a body Iterator in places that util.destroy(body) is called on a stream (e.g. when throwing in the request constructor etc)? I'm not sure if it makes sense to add the logic to util.destroy, or somewhere else.

I think so, yes. wdyt @ronag?

@Linkgoron
Copy link
Member Author

Should the should not follow redirects when using Readable request bodies behaviour (which I assume exists because of "backpressure" or missing some data) also be ported to async-iterators? It's simple enough, I just wasn't sure if it's needed for async iterators as well.

@Linkgoron
Copy link
Member Author

@mcollina Does it make sense call body.return() on a body Iterator in places that util.destroy(body) is called on a stream (e.g. when throwing in the request constructor etc)? I'm not sure if it makes sense to add the logic to util.destroy, or somewhere else.

I think so, yes. wdyt @ronag?

After looking at this a bit more, I'm not 100% sure that it's needed. As Undici gets an async-iterable, the iterable supposedly should only create the resources when iteration begins (i.e. symbol.Iterator or symbol.asyncIterator) are called. The other option is to create an iterator/asyncIterator from the body and immediately call return on it.

Copy link
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.

Can you keep the existing stream code and make a separate path for iterables?

@Linkgoron
Copy link
Member Author

Linkgoron commented Jun 26, 2021

I broke up the for await...of to manual iteration, so that the iterator could be closed on socket error (which is essentially the same behaviour as the stream body path that destroys the body on socket error). The code is a bit more complicated, as correct handling of return is now needed, so pointers to simplify it are welcome.

lib/client.js Outdated Show resolved Hide resolved
@Linkgoron
Copy link
Member Author

Linkgoron commented Jun 27, 2021

I think that I've essentially done what's needed with the implementation. There are some open outstanding issues that I opened that I wasn't sure how to solve, if they're correct or if they're needed (e.g. redirect), and I tried finding and porting all of the stream body tests that I could find and seemed relevant (I looked for createReadStream, new Readable and createReadable), and I also added a few extra tests.

PTAL and tell me if there are any outstanding issues, if something I did was way off in general, or if I should add benchmarks etc.

lib/client.js Show resolved Hide resolved
lib/client.js Outdated Show resolved Hide resolved
lib/client.js Outdated Show resolved Hide resolved
lib/client.js Outdated Show resolved Hide resolved
@ronag ronag marked this pull request as ready for review June 28, 2021 11:12
@ronag
Copy link
Member

ronag commented Jun 28, 2021

@Linkgoron I helped out a little

@ronag ronag force-pushed the feature/iterable-body-impl branch from dbfa839 to 8486390 Compare June 28, 2021 11:40
lib/client.js Outdated Show resolved Hide resolved
@ronag ronag requested a review from mcollina June 28, 2021 12:15
@@ -1587,6 +1587,8 @@ async function writeIterable ({ client, request, socket, contentLength, header,

let bytesWritten = 0
try {
// TODO (fix): What if socket errors while waiting for body?
Copy link
Member Author

Choose a reason for hiding this comment

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

Technically, once the next yield happens, the for...await of will take care of it (as it throws socket[kError])

Copy link
Member

Choose a reason for hiding this comment

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

Yes, but body could be doing a lot of work before the next yield happens.

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried doing something smarter, but calling return doesn't affect the iterator at all until it yields anyway, so it somehow has to be in userland.

Copy link
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

@mcollina mcollina merged commit a31b373 into nodejs:main Jun 29, 2021
@ronag ronag mentioned this pull request Jun 29, 2021
crysmags pushed a commit to crysmags/undici that referenced this pull request Feb 27, 2024
* feature: add support for request iterator body

* fix no-strict-content-length

* port createReadStream tests

* port client-errors tests, create async-iterator test utils

* create more async utils

* fix pipelining

* add timeout, tls, get-head-body tests

* add content-length tests

* add http-req-destroy tests

* remove console.log

* break up for await...of

* return for await...of

* update docs

* emulate pump

* add drain listener

* fixup

* fixup

* fixup

* fixup

* fixuP

* fixup

* fixup

* avoid extra loop if drain was actually close

* add bad input tests

* fix node 12 bug

Co-authored-by: Robert Nagy <ronagy@icloud.com>
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.

Request Body as Iterable/Iterator
4 participants