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

http: defer reentrant execution of Parser::Execute #43369

Closed
wants to merge 6 commits into from

Conversation

ShogunPanda
Copy link
Contributor

This PR fixes an edge case in which Parser::Execute is called within the callback of one of its events.

Fixes: #39671

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. http_parser Issues and PRs related to the HTTP Parser dependency or the http_parser binding. needs-ci PRs that need a full CI run. labels Jun 10, 2022
src/node_http_parser.cc Outdated Show resolved Hide resolved
src/node_http_parser.cc Outdated Show resolved Hide resolved
src/node_http_parser.cc Outdated Show resolved Hide resolved
test/parallel/test-http-parser-multiple-execute.js Outdated Show resolved Hide resolved
@mcollina
Copy link
Member

Good work!

@bnoordhuis
Copy link
Member

The problematic parser->current_buffer_.IsEmpty() check is a speed hack; it's explicitly documented as such. Adding a hack to work around a hack is not a good idea.

My suggestion: remove current_buffer_. That simplifies the parser and makes it re-entrant again. Worry about performance afterwards.

I speculate the hit won't be so bad, and can probably be fixed by doing the bookkeeping in JS land.

@ShogunPanda
Copy link
Contributor Author

ShogunPanda commented Jun 13, 2022

@bnoordhuis Are you suggesting to remove that just in Execute, isn't it?

@bnoordhuis
Copy link
Member

current_buffer_ is used to thread through the buffer from a parser.execute(buffer) call from JS through C++ to the JS callback parser[kOnBody] = parserOnBody(buffer, start, len) { /* ... */ }.

That threading through isn't necessary. Just set parser.buffer = buffer before calling parser.execute(buffer) and the callback can access it as this.buffer.

Node can short-circuit and pass data directly from libuv to the callback without going through parser.execute(buffer) first but the callback can detect that by doing buffer = this.buffer ?? buffer.

OnStreamRead()should be modified to create a new buffer instead of reusing current_buffer_. In theory that's a performance hit but I expect it's a wash because the callback no longer needs to call buffer.slice().


If I had infinite free time, I'd rewrite the parser to get rid of C++ -> JS callbacks and just return parse info. It'd get rid of so much complexity and inefficiency.

@ShogunPanda
Copy link
Contributor Author

Actually I was able to remove the current_buffer_ at all and apparently nothing changes.
The only relevant change is in on_body where I always do:

Local<Object> current_buffer_ = scope.Escape(Buffer::Copy(
        env()->isolate(),
        current_buffer_data_,
        current_buffer_len_).ToLocalChecked());

Do you see any problem in this?

@ShogunPanda
Copy link
Contributor Author

@addaleax @bnoordhuis I've sent my latest version which removes current_buffer_ and thus also removes needs for all the suggestion Anna asked about. Can you please take a look again?

src/node_http_parser.cc Outdated Show resolved Hide resolved
@ShogunPanda ShogunPanda force-pushed the http-defer-execute branch 2 times, most recently from 97fbe67 to 5c2884b Compare June 15, 2022 08:41
@ShogunPanda
Copy link
Contributor Author

@bnoordhuis I applied your changes (plus few additional required changes). How does it look now?

@bnoordhuis
Copy link
Member

Is there a reason you didn't update lib/_http_common.js? You don't need to slice anymore, or pass the start and len arguments.

@ShogunPanda
Copy link
Contributor Author

@bnoordhuis The answer is obvious: I forgot. X°°°D Done now!

src/node_http_parser.cc Outdated Show resolved Hide resolved
src/node_http_parser.cc Outdated Show resolved Hide resolved
Copy link
Member

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

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

Nice, good work!

@addaleax I'll go ahead and dismiss your review. Looks like all actionable feedback's been addressed.

@ShogunPanda
Copy link
Contributor Author

ShogunPanda commented Jun 17, 2022

@bnoordhuis The last item would be to try to test it without using the parser directly. I'll give it a try today but this can go out without as well.

Done!

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

wow, when a PR fixes a bug and reduces code, it's doing something right.

@ShogunPanda
Copy link
Contributor Author

Thanks sir :)

@nodejs-github-bot
Copy link
Collaborator

@ShogunPanda
Copy link
Contributor Author

@nodejs/http Can anybody reapprove this so I can land it?

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

@ShogunPanda
Copy link
Contributor Author

Landed in b970634

ShogunPanda added a commit that referenced this pull request Jun 22, 2022
PR-URL: #43369
Fixes: #39671
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
F3n67u pushed a commit to F3n67u/node that referenced this pull request Jun 24, 2022
PR-URL: nodejs#43369
Fixes: nodejs#39671
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
@j-catania
Copy link

j-catania commented Jun 27, 2022

Hello @ShogunPanda

Thanks for your work !
Can you please tell me in which version of NodeJS is it fixed please ?

@ShogunPanda
Copy link
Contributor Author

@j-catania It was merged in main, which means it will be available in the next 18 release, I think 18.5.0. Not sure it will be backported.

@ShogunPanda ShogunPanda deleted the http-defer-execute branch June 27, 2022 07:26
@MasterOdin
Copy link
Contributor

Is there a way to help see this backported to 16.x? This bug is really biting us as we are doing file uploads, and the 18 LTS release is still ~4 months off so upgrading isn't an option for us, and that we expect surrounding infra will take a few months to add 18 support after it releases.

@ShogunPanda
Copy link
Contributor Author

Is there a way to help see this backported to 16.x? This bug is really biting us as we are doing file uploads, and the 18 LTS release is still ~4 months off so upgrading isn't an option for us, and that we expect surrounding infra will take a few months to add 18 support after it releases.

@nodejs/tsc Can we do this? What is the policy about backporting?

@aduh95
Copy link
Contributor

aduh95 commented Jun 28, 2022

## What can be backported?
The "Current" release line is much more lenient than the LTS release lines in
what can be landed. Our LTS release lines (see the [Release Plan][])
require that commits mature in the Current release for at least 2 weeks before
they can be landed in an LTS staging branch. Only after "maturation" will those
commits be cherry-picked or backported.

@richardlau
Copy link
Member

Is there a way to help see this backported to 16.x? This bug is really biting us as we are doing file uploads, and the 18 LTS release is still ~4 months off so upgrading isn't an option for us, and that we expect surrounding infra will take a few months to add 18 support after it releases.

@nodejs/tsc Can we do this? What is the policy about backporting?

Standard policy is:

@ShogunPanda
Copy link
Contributor Author

ShogunPanda commented Jun 28, 2022

Thanks both.
In other words, in this particular case, I think the change is semver-patch. So I will be able to send a backport PR in about two weeks from the release of 18.5.0. Am I right?

@aduh95
Copy link
Contributor

aduh95 commented Jun 28, 2022

If the commit lands cleanly on v16.x-staging, there's no need for a backport PR:

## What needs to be backported?
If a cherry-pick from `main` does not land cleanly on a staging branch, the
releaser will mark the pull request with a particular label for that release
line (e.g. `backport-requested-vN.x`), specifying to our tooling that this
pull request should not be included. The releaser will then add a comment
requesting that a backport pull request be made.

If a backport PR is necessary, you don't need to wait two weeks for opening it, but the LTS team won't land it until it's been on a Current version for at least two weeks.

@ShogunPanda
Copy link
Contributor Author

I see.
Given that you added the label now, the tooling will try to perform the landing automatically, isn't it?

@aduh95
Copy link
Contributor

aduh95 commented Jun 28, 2022

No, someone from @nodejs/lts will (try to) do it when preparing the next release, or leave a comment here if there's a conflict.

targos pushed a commit that referenced this pull request Jul 12, 2022
PR-URL: #43369
Fixes: #39671
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
targos pushed a commit that referenced this pull request Jul 31, 2022
PR-URL: #43369
Fixes: #39671
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
guangwong pushed a commit to noslate-project/node that referenced this pull request Oct 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. http_parser Issues and PRs related to the HTTP Parser dependency or the http_parser binding.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Assertion `parser->current_buffer_.IsEmpty()' failed