http: fast-forward teardown of unread messages - #65732

Open
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown
Open

http: fast-forward teardown of unread messages#65732
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown

Conversation

@mcollina

@mcollinamcollina commented Sep 2, 2026

Copy link
Copy Markdown
Member

When a response finishes and the incoming message was fully received but never read (every body-less request that the handler ignores), _dump() currently calls resume(), which schedules a resume_ tick, emits 'resume' to no listeners, and runs the flow() machinery just to reach EOF.

With this change _dump() calls read(0) instead when the message is already ended, undestroyed and fully drained: read(0) at EOF goes straight to endReadable() (idempotent since #65310), so the 'end'/'close' sequence is unchanged and still asynchronous. Incomplete messages (body still arriving when the response ends) keep the resume() path. Unread client responses dumped by _http_client benefit the same way.

For a hello-world HTTP server this removes one nextTick (7 → 6 per request) and one dead emit (9 → 8 per request).

Benchmark (i7-7700, server pinned to one core, wrk -t2 -c50 on separate physical cores, 8 interleaved rounds; CPU measured from /proc/<pid>/stat over the run):

CPU µs/requestuser µs/request
main26.616.7
this PR25.3 (−4.2%)15.8 (−5%)

The patched binary won all 8 rounds and served ~4% more requests per round.

test/parallel/test-http-*, test-stream-*, test-net-* and test-https-* all pass; test-http-dump-req-when-res-ends covers the mid-body dump case, which keeps the previous behavior.

——

This was generated with the assistance of AI. I reviewed all the code.


Second commit (http: destroy clean incoming messages in one tick): when the message was fully received and is destroyed with no error, _destroy completes synchronously instead of deferring the callback with process.nextTick() — the deferral only exists so 'error' listeners attached right after destroy(err) still receive the error, which cannot matter when there is no error. 'close' remains asynchronous. Per-request ticks drop from 6 to 5; same test suites pass (863/863).

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-botnodejs-github-bot added http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Sep 2, 2026
When a response finishes and the incoming message was fully received
but never read, _dump() no longer goes through resume(): read(0) at
EOF schedules the 'end' emission directly, skipping the resume_ tick,
the 'resume' emit and the flow() machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 7 to 6 and CPU per request by ~4%.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina
mcollinaforce-pushed the http-fast-request-teardown branch from e4f8389 to 803bd87CompareSeptember 2, 2026 07:04
@codecov

codecovBot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.99%. Comparing base (7b0de5e) to head (962c042).
⚠️ Report is 92 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #65732 +/- ##
==========================================
- Coverage 90.05% 89.99% -0.06% 
==========================================
Files 754 757 +3 Lines 255722 257753 +2031 Branches 48314 48885 +571 ==========================================
+ Hits 230281 231959 +1678 - Misses 16555 16895 +340 - Partials 8886 8899 +13 
Files with missing linesCoverage Δ
lib/_http_incoming.js97.99% <100.00%> (+0.05%)⬆️

... and 94 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

When an incoming message is destroyed after being fully received with
no error, complete the destroy synchronously instead of deferring the
callback with process.nextTick(). The deferral only exists so that
'error' listeners attached right after destroy(err) still receive the
error, which cannot matter when there is no error. 'close' is still
emitted asynchronously by the stream machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 6 to 5.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollinamcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

httpIssues and PRs related to the http subsystem.needs-ciPRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mcollina@nodejs-github-bot@jasnell@ronag
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

http: fast-forward teardown of unread messages - #65732

Open
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown
Open

http: fast-forward teardown of unread messages#65732
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown

Conversation

@mcollina

@mcollinamcollina commented Sep 2, 2026

Copy link
Copy Markdown
Member

When a response finishes and the incoming message was fully received but never read (every body-less request that the handler ignores), _dump() currently calls resume(), which schedules a resume_ tick, emits 'resume' to no listeners, and runs the flow() machinery just to reach EOF.

With this change _dump() calls read(0) instead when the message is already ended, undestroyed and fully drained: read(0) at EOF goes straight to endReadable() (idempotent since #65310), so the 'end'/'close' sequence is unchanged and still asynchronous. Incomplete messages (body still arriving when the response ends) keep the resume() path. Unread client responses dumped by _http_client benefit the same way.

For a hello-world HTTP server this removes one nextTick (7 → 6 per request) and one dead emit (9 → 8 per request).

Benchmark (i7-7700, server pinned to one core, wrk -t2 -c50 on separate physical cores, 8 interleaved rounds; CPU measured from /proc/<pid>/stat over the run):

CPU µs/requestuser µs/request
main26.616.7
this PR25.3 (−4.2%)15.8 (−5%)

The patched binary won all 8 rounds and served ~4% more requests per round.

test/parallel/test-http-*, test-stream-*, test-net-* and test-https-* all pass; test-http-dump-req-when-res-ends covers the mid-body dump case, which keeps the previous behavior.

——

This was generated with the assistance of AI. I reviewed all the code.


Second commit (http: destroy clean incoming messages in one tick): when the message was fully received and is destroyed with no error, _destroy completes synchronously instead of deferring the callback with process.nextTick() — the deferral only exists so 'error' listeners attached right after destroy(err) still receive the error, which cannot matter when there is no error. 'close' remains asynchronous. Per-request ticks drop from 6 to 5; same test suites pass (863/863).

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-botnodejs-github-bot added http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Sep 2, 2026
When a response finishes and the incoming message was fully received
but never read, _dump() no longer goes through resume(): read(0) at
EOF schedules the 'end' emission directly, skipping the resume_ tick,
the 'resume' emit and the flow() machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 7 to 6 and CPU per request by ~4%.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina
mcollinaforce-pushed the http-fast-request-teardown branch from e4f8389 to 803bd87CompareSeptember 2, 2026 07:04
@codecov

codecovBot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.99%. Comparing base (7b0de5e) to head (962c042).
⚠️ Report is 92 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #65732 +/- ##
==========================================
- Coverage 90.05% 89.99% -0.06% 
==========================================
Files 754 757 +3 Lines 255722 257753 +2031 Branches 48314 48885 +571 ==========================================
+ Hits 230281 231959 +1678 - Misses 16555 16895 +340 - Partials 8886 8899 +13 
Files with missing linesCoverage Δ
lib/_http_incoming.js97.99% <100.00%> (+0.05%)⬆️

... and 94 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

When an incoming message is destroyed after being fully received with
no error, complete the destroy synchronously instead of deferring the
callback with process.nextTick(). The deferral only exists so that
'error' listeners attached right after destroy(err) still receive the
error, which cannot matter when there is no error. 'close' is still
emitted asynchronously by the stream machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 6 to 5.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollinamcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

httpIssues and PRs related to the http subsystem.needs-ciPRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mcollina@nodejs-github-bot@jasnell@ronag
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

http: fast-forward teardown of unread messages - #65732

Open
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown
Open

http: fast-forward teardown of unread messages#65732
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown

Conversation

@mcollina

@mcollinamcollina commented Sep 2, 2026

Copy link
Copy Markdown
Member

When a response finishes and the incoming message was fully received but never read (every body-less request that the handler ignores), _dump() currently calls resume(), which schedules a resume_ tick, emits 'resume' to no listeners, and runs the flow() machinery just to reach EOF.

With this change _dump() calls read(0) instead when the message is already ended, undestroyed and fully drained: read(0) at EOF goes straight to endReadable() (idempotent since #65310), so the 'end'/'close' sequence is unchanged and still asynchronous. Incomplete messages (body still arriving when the response ends) keep the resume() path. Unread client responses dumped by _http_client benefit the same way.

For a hello-world HTTP server this removes one nextTick (7 → 6 per request) and one dead emit (9 → 8 per request).

Benchmark (i7-7700, server pinned to one core, wrk -t2 -c50 on separate physical cores, 8 interleaved rounds; CPU measured from /proc/<pid>/stat over the run):

CPU µs/requestuser µs/request
main26.616.7
this PR25.3 (−4.2%)15.8 (−5%)

The patched binary won all 8 rounds and served ~4% more requests per round.

test/parallel/test-http-*, test-stream-*, test-net-* and test-https-* all pass; test-http-dump-req-when-res-ends covers the mid-body dump case, which keeps the previous behavior.

——

This was generated with the assistance of AI. I reviewed all the code.


Second commit (http: destroy clean incoming messages in one tick): when the message was fully received and is destroyed with no error, _destroy completes synchronously instead of deferring the callback with process.nextTick() — the deferral only exists so 'error' listeners attached right after destroy(err) still receive the error, which cannot matter when there is no error. 'close' remains asynchronous. Per-request ticks drop from 6 to 5; same test suites pass (863/863).

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-botnodejs-github-bot added http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Sep 2, 2026
When a response finishes and the incoming message was fully received
but never read, _dump() no longer goes through resume(): read(0) at
EOF schedules the 'end' emission directly, skipping the resume_ tick,
the 'resume' emit and the flow() machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 7 to 6 and CPU per request by ~4%.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina
mcollinaforce-pushed the http-fast-request-teardown branch from e4f8389 to 803bd87CompareSeptember 2, 2026 07:04
@codecov

codecovBot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.99%. Comparing base (7b0de5e) to head (962c042).
⚠️ Report is 92 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #65732 +/- ##
==========================================
- Coverage 90.05% 89.99% -0.06% 
==========================================
Files 754 757 +3 Lines 255722 257753 +2031 Branches 48314 48885 +571 ==========================================
+ Hits 230281 231959 +1678 - Misses 16555 16895 +340 - Partials 8886 8899 +13 
Files with missing linesCoverage Δ
lib/_http_incoming.js97.99% <100.00%> (+0.05%)⬆️

... and 94 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

When an incoming message is destroyed after being fully received with
no error, complete the destroy synchronously instead of deferring the
callback with process.nextTick(). The deferral only exists so that
'error' listeners attached right after destroy(err) still receive the
error, which cannot matter when there is no error. 'close' is still
emitted asynchronously by the stream machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 6 to 5.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollinamcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

httpIssues and PRs related to the http subsystem.needs-ciPRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mcollina@nodejs-github-bot@jasnell@ronag
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

http: fast-forward teardown of unread messages - #65732

Open
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown
Open

http: fast-forward teardown of unread messages#65732
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown

Conversation

@mcollina

@mcollinamcollina commented Sep 2, 2026

Copy link
Copy Markdown
Member

When a response finishes and the incoming message was fully received but never read (every body-less request that the handler ignores), _dump() currently calls resume(), which schedules a resume_ tick, emits 'resume' to no listeners, and runs the flow() machinery just to reach EOF.

With this change _dump() calls read(0) instead when the message is already ended, undestroyed and fully drained: read(0) at EOF goes straight to endReadable() (idempotent since #65310), so the 'end'/'close' sequence is unchanged and still asynchronous. Incomplete messages (body still arriving when the response ends) keep the resume() path. Unread client responses dumped by _http_client benefit the same way.

For a hello-world HTTP server this removes one nextTick (7 → 6 per request) and one dead emit (9 → 8 per request).

Benchmark (i7-7700, server pinned to one core, wrk -t2 -c50 on separate physical cores, 8 interleaved rounds; CPU measured from /proc/<pid>/stat over the run):

CPU µs/requestuser µs/request
main26.616.7
this PR25.3 (−4.2%)15.8 (−5%)

The patched binary won all 8 rounds and served ~4% more requests per round.

test/parallel/test-http-*, test-stream-*, test-net-* and test-https-* all pass; test-http-dump-req-when-res-ends covers the mid-body dump case, which keeps the previous behavior.

——

This was generated with the assistance of AI. I reviewed all the code.


Second commit (http: destroy clean incoming messages in one tick): when the message was fully received and is destroyed with no error, _destroy completes synchronously instead of deferring the callback with process.nextTick() — the deferral only exists so 'error' listeners attached right after destroy(err) still receive the error, which cannot matter when there is no error. 'close' remains asynchronous. Per-request ticks drop from 6 to 5; same test suites pass (863/863).

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-botnodejs-github-bot added http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Sep 2, 2026
When a response finishes and the incoming message was fully received
but never read, _dump() no longer goes through resume(): read(0) at
EOF schedules the 'end' emission directly, skipping the resume_ tick,
the 'resume' emit and the flow() machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 7 to 6 and CPU per request by ~4%.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina
mcollinaforce-pushed the http-fast-request-teardown branch from e4f8389 to 803bd87CompareSeptember 2, 2026 07:04
@codecov

codecovBot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.99%. Comparing base (7b0de5e) to head (962c042).
⚠️ Report is 92 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #65732 +/- ##
==========================================
- Coverage 90.05% 89.99% -0.06% 
==========================================
Files 754 757 +3 Lines 255722 257753 +2031 Branches 48314 48885 +571 ==========================================
+ Hits 230281 231959 +1678 - Misses 16555 16895 +340 - Partials 8886 8899 +13 
Files with missing linesCoverage Δ
lib/_http_incoming.js97.99% <100.00%> (+0.05%)⬆️

... and 94 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

When an incoming message is destroyed after being fully received with
no error, complete the destroy synchronously instead of deferring the
callback with process.nextTick(). The deferral only exists so that
'error' listeners attached right after destroy(err) still receive the
error, which cannot matter when there is no error. 'close' is still
emitted asynchronously by the stream machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 6 to 5.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollinamcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

httpIssues and PRs related to the http subsystem.needs-ciPRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mcollina@nodejs-github-bot@jasnell@ronag
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

http: fast-forward teardown of unread messages - #65732

Open
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown
Open

http: fast-forward teardown of unread messages#65732
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown

Conversation

@mcollina

@mcollinamcollina commented Sep 2, 2026

Copy link
Copy Markdown
Member

When a response finishes and the incoming message was fully received but never read (every body-less request that the handler ignores), _dump() currently calls resume(), which schedules a resume_ tick, emits 'resume' to no listeners, and runs the flow() machinery just to reach EOF.

With this change _dump() calls read(0) instead when the message is already ended, undestroyed and fully drained: read(0) at EOF goes straight to endReadable() (idempotent since #65310), so the 'end'/'close' sequence is unchanged and still asynchronous. Incomplete messages (body still arriving when the response ends) keep the resume() path. Unread client responses dumped by _http_client benefit the same way.

For a hello-world HTTP server this removes one nextTick (7 → 6 per request) and one dead emit (9 → 8 per request).

Benchmark (i7-7700, server pinned to one core, wrk -t2 -c50 on separate physical cores, 8 interleaved rounds; CPU measured from /proc/<pid>/stat over the run):

CPU µs/requestuser µs/request
main26.616.7
this PR25.3 (−4.2%)15.8 (−5%)

The patched binary won all 8 rounds and served ~4% more requests per round.

test/parallel/test-http-*, test-stream-*, test-net-* and test-https-* all pass; test-http-dump-req-when-res-ends covers the mid-body dump case, which keeps the previous behavior.

——

This was generated with the assistance of AI. I reviewed all the code.


Second commit (http: destroy clean incoming messages in one tick): when the message was fully received and is destroyed with no error, _destroy completes synchronously instead of deferring the callback with process.nextTick() — the deferral only exists so 'error' listeners attached right after destroy(err) still receive the error, which cannot matter when there is no error. 'close' remains asynchronous. Per-request ticks drop from 6 to 5; same test suites pass (863/863).

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-botnodejs-github-bot added http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Sep 2, 2026
When a response finishes and the incoming message was fully received
but never read, _dump() no longer goes through resume(): read(0) at
EOF schedules the 'end' emission directly, skipping the resume_ tick,
the 'resume' emit and the flow() machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 7 to 6 and CPU per request by ~4%.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina
mcollinaforce-pushed the http-fast-request-teardown branch from e4f8389 to 803bd87CompareSeptember 2, 2026 07:04
@codecov

codecovBot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.99%. Comparing base (7b0de5e) to head (962c042).
⚠️ Report is 92 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #65732 +/- ##
==========================================
- Coverage 90.05% 89.99% -0.06% 
==========================================
Files 754 757 +3 Lines 255722 257753 +2031 Branches 48314 48885 +571 ==========================================
+ Hits 230281 231959 +1678 - Misses 16555 16895 +340 - Partials 8886 8899 +13 
Files with missing linesCoverage Δ
lib/_http_incoming.js97.99% <100.00%> (+0.05%)⬆️

... and 94 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

When an incoming message is destroyed after being fully received with
no error, complete the destroy synchronously instead of deferring the
callback with process.nextTick(). The deferral only exists so that
'error' listeners attached right after destroy(err) still receive the
error, which cannot matter when there is no error. 'close' is still
emitted asynchronously by the stream machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 6 to 5.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollinamcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

httpIssues and PRs related to the http subsystem.needs-ciPRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mcollina@nodejs-github-bot@jasnell@ronag
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

http: fast-forward teardown of unread messages - #65732

Open
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown
Open

http: fast-forward teardown of unread messages#65732
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown

Conversation

@mcollina

@mcollinamcollina commented Sep 2, 2026

Copy link
Copy Markdown
Member

When a response finishes and the incoming message was fully received but never read (every body-less request that the handler ignores), _dump() currently calls resume(), which schedules a resume_ tick, emits 'resume' to no listeners, and runs the flow() machinery just to reach EOF.

With this change _dump() calls read(0) instead when the message is already ended, undestroyed and fully drained: read(0) at EOF goes straight to endReadable() (idempotent since #65310), so the 'end'/'close' sequence is unchanged and still asynchronous. Incomplete messages (body still arriving when the response ends) keep the resume() path. Unread client responses dumped by _http_client benefit the same way.

For a hello-world HTTP server this removes one nextTick (7 → 6 per request) and one dead emit (9 → 8 per request).

Benchmark (i7-7700, server pinned to one core, wrk -t2 -c50 on separate physical cores, 8 interleaved rounds; CPU measured from /proc/<pid>/stat over the run):

CPU µs/requestuser µs/request
main26.616.7
this PR25.3 (−4.2%)15.8 (−5%)

The patched binary won all 8 rounds and served ~4% more requests per round.

test/parallel/test-http-*, test-stream-*, test-net-* and test-https-* all pass; test-http-dump-req-when-res-ends covers the mid-body dump case, which keeps the previous behavior.

——

This was generated with the assistance of AI. I reviewed all the code.


Second commit (http: destroy clean incoming messages in one tick): when the message was fully received and is destroyed with no error, _destroy completes synchronously instead of deferring the callback with process.nextTick() — the deferral only exists so 'error' listeners attached right after destroy(err) still receive the error, which cannot matter when there is no error. 'close' remains asynchronous. Per-request ticks drop from 6 to 5; same test suites pass (863/863).

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-botnodejs-github-bot added http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Sep 2, 2026
When a response finishes and the incoming message was fully received
but never read, _dump() no longer goes through resume(): read(0) at
EOF schedules the 'end' emission directly, skipping the resume_ tick,
the 'resume' emit and the flow() machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 7 to 6 and CPU per request by ~4%.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina
mcollinaforce-pushed the http-fast-request-teardown branch from e4f8389 to 803bd87CompareSeptember 2, 2026 07:04
@codecov

codecovBot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.99%. Comparing base (7b0de5e) to head (962c042).
⚠️ Report is 92 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #65732 +/- ##
==========================================
- Coverage 90.05% 89.99% -0.06% 
==========================================
Files 754 757 +3 Lines 255722 257753 +2031 Branches 48314 48885 +571 ==========================================
+ Hits 230281 231959 +1678 - Misses 16555 16895 +340 - Partials 8886 8899 +13 
Files with missing linesCoverage Δ
lib/_http_incoming.js97.99% <100.00%> (+0.05%)⬆️

... and 94 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

When an incoming message is destroyed after being fully received with
no error, complete the destroy synchronously instead of deferring the
callback with process.nextTick(). The deferral only exists so that
'error' listeners attached right after destroy(err) still receive the
error, which cannot matter when there is no error. 'close' is still
emitted asynchronously by the stream machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 6 to 5.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollinamcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

httpIssues and PRs related to the http subsystem.needs-ciPRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mcollina@nodejs-github-bot@jasnell@ronag
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

http: fast-forward teardown of unread messages - #65732

Open
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown
Open

http: fast-forward teardown of unread messages#65732
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown

Conversation

@mcollina

@mcollinamcollina commented Sep 2, 2026

Copy link
Copy Markdown
Member

When a response finishes and the incoming message was fully received but never read (every body-less request that the handler ignores), _dump() currently calls resume(), which schedules a resume_ tick, emits 'resume' to no listeners, and runs the flow() machinery just to reach EOF.

With this change _dump() calls read(0) instead when the message is already ended, undestroyed and fully drained: read(0) at EOF goes straight to endReadable() (idempotent since #65310), so the 'end'/'close' sequence is unchanged and still asynchronous. Incomplete messages (body still arriving when the response ends) keep the resume() path. Unread client responses dumped by _http_client benefit the same way.

For a hello-world HTTP server this removes one nextTick (7 → 6 per request) and one dead emit (9 → 8 per request).

Benchmark (i7-7700, server pinned to one core, wrk -t2 -c50 on separate physical cores, 8 interleaved rounds; CPU measured from /proc/<pid>/stat over the run):

CPU µs/requestuser µs/request
main26.616.7
this PR25.3 (−4.2%)15.8 (−5%)

The patched binary won all 8 rounds and served ~4% more requests per round.

test/parallel/test-http-*, test-stream-*, test-net-* and test-https-* all pass; test-http-dump-req-when-res-ends covers the mid-body dump case, which keeps the previous behavior.

——

This was generated with the assistance of AI. I reviewed all the code.


Second commit (http: destroy clean incoming messages in one tick): when the message was fully received and is destroyed with no error, _destroy completes synchronously instead of deferring the callback with process.nextTick() — the deferral only exists so 'error' listeners attached right after destroy(err) still receive the error, which cannot matter when there is no error. 'close' remains asynchronous. Per-request ticks drop from 6 to 5; same test suites pass (863/863).

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-botnodejs-github-bot added http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Sep 2, 2026
When a response finishes and the incoming message was fully received
but never read, _dump() no longer goes through resume(): read(0) at
EOF schedules the 'end' emission directly, skipping the resume_ tick,
the 'resume' emit and the flow() machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 7 to 6 and CPU per request by ~4%.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina
mcollinaforce-pushed the http-fast-request-teardown branch from e4f8389 to 803bd87CompareSeptember 2, 2026 07:04
@codecov

codecovBot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.99%. Comparing base (7b0de5e) to head (962c042).
⚠️ Report is 92 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #65732 +/- ##
==========================================
- Coverage 90.05% 89.99% -0.06% 
==========================================
Files 754 757 +3 Lines 255722 257753 +2031 Branches 48314 48885 +571 ==========================================
+ Hits 230281 231959 +1678 - Misses 16555 16895 +340 - Partials 8886 8899 +13 
Files with missing linesCoverage Δ
lib/_http_incoming.js97.99% <100.00%> (+0.05%)⬆️

... and 94 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

When an incoming message is destroyed after being fully received with
no error, complete the destroy synchronously instead of deferring the
callback with process.nextTick(). The deferral only exists so that
'error' listeners attached right after destroy(err) still receive the
error, which cannot matter when there is no error. 'close' is still
emitted asynchronously by the stream machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 6 to 5.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollinamcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

httpIssues and PRs related to the http subsystem.needs-ciPRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mcollina@nodejs-github-bot@jasnell@ronag
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

http: fast-forward teardown of unread messages - #65732

Open
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown
Open

http: fast-forward teardown of unread messages#65732
mcollina wants to merge 2 commits into
nodejs:mainfrom
mcollina:http-fast-request-teardown

Conversation

@mcollina

@mcollinamcollina commented Sep 2, 2026

Copy link
Copy Markdown
Member

When a response finishes and the incoming message was fully received but never read (every body-less request that the handler ignores), _dump() currently calls resume(), which schedules a resume_ tick, emits 'resume' to no listeners, and runs the flow() machinery just to reach EOF.

With this change _dump() calls read(0) instead when the message is already ended, undestroyed and fully drained: read(0) at EOF goes straight to endReadable() (idempotent since #65310), so the 'end'/'close' sequence is unchanged and still asynchronous. Incomplete messages (body still arriving when the response ends) keep the resume() path. Unread client responses dumped by _http_client benefit the same way.

For a hello-world HTTP server this removes one nextTick (7 → 6 per request) and one dead emit (9 → 8 per request).

Benchmark (i7-7700, server pinned to one core, wrk -t2 -c50 on separate physical cores, 8 interleaved rounds; CPU measured from /proc/<pid>/stat over the run):

CPU µs/requestuser µs/request
main26.616.7
this PR25.3 (−4.2%)15.8 (−5%)

The patched binary won all 8 rounds and served ~4% more requests per round.

test/parallel/test-http-*, test-stream-*, test-net-* and test-https-* all pass; test-http-dump-req-when-res-ends covers the mid-body dump case, which keeps the previous behavior.

——

This was generated with the assistance of AI. I reviewed all the code.


Second commit (http: destroy clean incoming messages in one tick): when the message was fully received and is destroyed with no error, _destroy completes synchronously instead of deferring the callback with process.nextTick() — the deferral only exists so 'error' listeners attached right after destroy(err) still receive the error, which cannot matter when there is no error. 'close' remains asynchronous. Per-request ticks drop from 6 to 5; same test suites pass (863/863).

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-botnodejs-github-bot added http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Sep 2, 2026
When a response finishes and the incoming message was fully received
but never read, _dump() no longer goes through resume(): read(0) at
EOF schedules the 'end' emission directly, skipping the resume_ tick,
the 'resume' emit and the flow() machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 7 to 6 and CPU per request by ~4%.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollina
mcollinaforce-pushed the http-fast-request-teardown branch from e4f8389 to 803bd87CompareSeptember 2, 2026 07:04
@codecov

codecovBot commented Sep 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.99%. Comparing base (7b0de5e) to head (962c042).
⚠️ Report is 92 commits behind head on main.

Additional details and impacted files
@@ Coverage Diff @@## main #65732 +/- ##
==========================================
- Coverage 90.05% 89.99% -0.06% 
==========================================
Files 754 757 +3 Lines 255722 257753 +2031 Branches 48314 48885 +571 ==========================================
+ Hits 230281 231959 +1678 - Misses 16555 16895 +340 - Partials 8886 8899 +13 
Files with missing linesCoverage Δ
lib/_http_incoming.js97.99% <100.00%> (+0.05%)⬆️

... and 94 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

When an incoming message is destroyed after being fully received with
no error, complete the destroy synchronously instead of deferring the
callback with process.nextTick(). The deferral only exists so that
'error' listeners attached right after destroy(err) still receive the
error, which cannot matter when there is no error. 'close' is still
emitted asynchronously by the stream machinery.
Reduces the per-request nextTick count of a hello-world HTTP server
from 6 to 5.
Signed-off-by: Matteo Collina <hello@matteocollina.com>
@mcollinamcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@github-actionsgithub-actionsBot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 3, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

httpIssues and PRs related to the http subsystem.needs-ciPRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@mcollina@nodejs-github-bot@jasnell@ronag