-
-
Notifications
You must be signed in to change notification settings - Fork 31.6k
http2: fix graceful session close #57808
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
http2: fix graceful session close #57808
Conversation
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback
Review requested:
|
Problem:When a client calls response.end() with an empty payload, Node.js sends header only frames to nghttp2 as expected, and the JavaScript layer assumes these frames will be successfully transmitted post that. If session.close() is called immediately afterward, the Http2Session class doesn't recognize any active streams (as it only tracks data frames and push promises as active streams), and begins terminating the underlying socket. However, when the C++ layer subsequently attempts to transmit the queued header frames, it fails because the socket has already been closed, resulting in transmission errors and clients never receiving essential GOAWAY frames and response header. Solution:The solution ensures proper graceful session closure by delaying socket termination until all data in the nghttp2 queue/buffer is fully processed. This is implemented by: |
I will be very happy to update the current solution in case of any scope of improvement and would love to take input to optimise this further. |
Optimization Proposal for the current fixCurrent Issue:The JavaScript layer is being called after each write operation to underlying socket. Proposed SolutionFlag-Based Approach: Responsibility Shift:When the flag is set, make the C++ layer responsible for notifying the JavaScript layer Benefits:Reduces repetitive calls to onstreamaftrwrite_string() Trade-off:Implementation will be more complex |
session[kMaybeDestroy](err); | ||
closeSession(session, NGHTTP2_NO_ERROR, err); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change is done as improvement. This is because when underlying socket is closed there is no need for looking at graceful closure of session by calling [kMaybeDestroy] instead we can immediately call closeSession which will handle all cleanup operation.
One of the simplest approach is to delay socket destruction to next iteration of event loop i.e call inside setImmediate but I don't think this will address underlying problem |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #57808 +/- ##
==========================================
+ Coverage 90.21% 90.23% +0.02%
==========================================
Files 630 630
Lines 185524 185737 +213
Branches 36387 36414 +27
==========================================
+ Hits 167378 167609 +231
+ Misses 11037 11009 -28
- Partials 7109 7119 +10
🚀 New features to boost your workflow:
|
Some tests/linting are failing. I think the proposed optimization would be useful. |
Fine, then I will be implementing more optimised solution as stated above.
And all tests are passed every time, so I am a bit confused why it is failing on CI. |
…allbacks between C++ and JavaScript layers. Added graceful_close_initiated_ flag which will ensure that JS layer will be notified only when session close is initiated and centralized notification logic in CheckAndNotifyJSAfterWrite() to only notify JavaScript when there's no pending data (nghttp2_session_want_write/read return 0). Previously, excessive callbac
hey @mcollina I have implemented following strategy for optimisation:
Both of the above things when combined, will ensure that the new callback Regarding Failing tests, I am able to reproduce this occasionally i.e once in 10 run but not able to find root cause yet. I will be investigating further about RC. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Commit Queue failed- Loading data for nodejs/node/pull/57808 ✔ Done loading data for nodejs/node/pull/57808 ----------------------------------- PR info ------------------------------------ Title http2: fix graceful session close (#57808) ⚠ Could not retrieve the email or name of the PR author's from user's GitHub profile! Branch pandeykushagra51:http2/fix-graceful-session-close -> nodejs:main Labels c++, lib / src, author ready, needs-ci Commits 7 - http2: fix graceful session close - test: fixed request-response handling - test: fix lint errors - http2: Reduces overhead during HTTP/2 session closure by minimizing c… - http2: fixed cpp lint error - test: fixed incorrect http2 stream close test failure - resolved PR comment: fixed names and added description Committers 1 - Kushagra Pandey <pandeykushagra51@gmail.com> PR-URL: https://github.com/nodejs/node/pull/57808 Fixes: https://github.com/nodejs/node/issues/57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> ------------------------------ Generated metadata ------------------------------ PR-URL: https://github.com/nodejs/node/pull/57808 Fixes: https://github.com/nodejs/node/issues/57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> -------------------------------------------------------------------------------- ℹ This PR was created on Wed, 09 Apr 2025 19:55:01 GMT ✔ Approvals: 3 ✔ - Matteo Collina (@mcollina) (TSC): https://github.com/nodejs/node/pull/57808#pullrequestreview-2763424555 ✔ - Tim Perry (@pimterry): https://github.com/nodejs/node/pull/57808#pullrequestreview-2772755449 ✔ - James M Snell (@jasnell) (TSC): https://github.com/nodejs/node/pull/57808#pullrequestreview-2779922468 ✔ Last GitHub CI successful ℹ Last Full PR CI on 2025-04-19T13:58:01Z: https://ci.nodejs.org/job/node-test-pull-request/66365/ - Querying data for job/node-test-pull-request/66365/ ✔ Last Jenkins CI successful -------------------------------------------------------------------------------- ✔ No git cherry-pick in progress ✔ No git am in progress ✔ No git rebase in progress -------------------------------------------------------------------------------- - Bringing origin/main up to date... From https://github.com/nodejs/node * branch main -> FETCH_HEAD ✔ origin/main is now up-to-date - Downloading patch for 57808 From https://github.com/nodejs/node * branch refs/pull/57808/merge -> FETCH_HEAD ✔ Fetched commits as 733e0fc2c4d0..d54f12c3e6ca -------------------------------------------------------------------------------- Auto-merging src/env_properties.h Auto-merging src/node_http2.cc [main 3ee6e4a2df] http2: fix graceful session close Author: Kushagra Pandey <pandeykushagra51@gmail.com> Date: Thu Apr 10 00:52:44 2025 +0530 5 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-http2-session-graceful-close.js [main b82d2b606a] test: fixed request-response handling Author: Kushagra Pandey <pandeykushagra51@gmail.com> Date: Thu Apr 10 02:04:51 2025 +0530 1 file changed, 13 insertions(+), 12 deletions(-) [main b1ce0f2f42] test: fix lint errors Author: Kushagra Pandey <pandeykushagra51@gmail.com> Date: Fri Apr 11 07:51:49 2025 +0530 1 file changed, 9 insertions(+), 9 deletions(-) Auto-merging src/node_http2.cc [main d5fe338f23] http2: Reduces overhead during HTTP/2 session closure by minimizing callbacks between C++ and JavaScript layers. Added graceful_close_initiated_ flag which will ensure that JS layer will be notified only when session close is initiated and centralized notification logic in CheckAndNotifyJSAfterWrite() to only notify JavaScript when there's no pending data (nghttp2_session_want_write/read return 0). Previously, excessive callbac Author: Kushagra Pandey <pandeykushagra51@gmail.com> Date: Fri Apr 11 10:22:13 2025 +0530 3 files changed, 57 insertions(+), 5 deletions(-) Auto-merging src/node_http2.cc [main 7b4f4af3eb] http2: fixed cpp lint error Author: Kushagra Pandey <pandeykushagra51@gmail.com> Date: Fri Apr 11 20:02:25 2025 +0530 2 files changed, 4 insertions(+), 5 deletions(-) [main 54ebdf9810] test: fixed incorrect http2 stream close test failure Author: Kushagra Pandey <pandeykushagra51@gmail.com> Date: Sun Apr 13 01:23:30 2025 +0530 1 file changed, 11 insertions(+), 6 deletions(-) Auto-merging src/env_properties.h Auto-merging src/node_http2.cc [main 91dd7af93e] resolved PR comment: fixed names and added description Author: Kushagra Pandey <pandeykushagra51@gmail.com> Date: Tue Apr 15 19:27:02 2025 +0530 4 files changed, 11 insertions(+), 10 deletions(-) ✔ Patches applied There are 7 commits in the PR. Attempting autorebase. Rebasing (2/14) Executing: git node land --amend --yes --------------------------------- New Message ---------------------------------- http2: fix graceful session closehttps://github.com/nodejs/node/actions/runs/14550420536 |
Hey guys could you please check the last commit-queue.
|
Landed in 2acc8bc |
Also please take a look at #57586. |
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec. Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback PR-URL: #57808 Fixes: #57809 Refs: https://nodejs.org/api/http2.html%5C#http2sessionclosecallback Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tim Perry <pimterry@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Fix issue where session.close() prematurely destroys the session when response.end() was called with an empty payload while active http2 streams still existed. This change ensures that sessions are closed gracefully only after all http2 streams complete and clients properly receive the GOAWAY frame as per the HTTP/2 spec.
Refs: https://nodejs.org/api/http2.html\#http2sessionclosecallback
Fixes: #57809
Update:
Please refer detailed explanation below to know the intention behind updating
test/parallel/test-http2-client-rststream-before-connect.js
test.#57808 (comment)