Skip to content

http: reduce per-request allocations in the server - #65802

Open
mcollina wants to merge 1 commit into
nodejs:mainfrom
mcollina:http-reduce-request-allocations
Open

http: reduce per-request allocations in the server#65802
mcollina wants to merge 1 commit into
nodejs:mainfrom
mcollina:http-reduce-request-allocations

Conversation

@mcollina

@mcollina mcollina commented Sep 4, 2026

Copy link
Copy Markdown
Member

Removes allocations and repeated work the HTTP server performs for every request:

  • Per-request bound functions: updateOutgoingData.bind() becomes one closure per connection reused by every response, and the per-request resOnFinish.bind() becomes a shared 'finish' listener that reaches everything through the connection state stored on the response.
  • req.headers materialization: the requireHostHeader check (req.headers.host), the Expect check, and optimizeEmptyRequests' body-header check now scan rawHeaders (over the same range the headers getter exposes) instead of building the whole headers object — with a toLowerCase() per header name — for every request even when the application never reads req.headers.
  • ServerResponse options object: cached per server; custom response classes keep receiving a fresh object since they may retain or mutate it.
  • Status line: cached per status code when the reason phrase is the default (also skipping its character validation, which cannot fail for the built-in phrases).
  • Date: header line: the complete line is cached alongside the existing one-second utcDate() cache.
  • Keep-alive header pair: Connection: keep-alive\r\nKeep-Alive: timeout=N is rendered once per server settings instead of per response.
  • Lenient-validation lookup: the 4-level option chain is computed once per message.

Responses are byte-identical to main (verified by diffing raw responses).

Benchmarks (i7-7700, server pinned to one core, wrk -t2 -c50 on separate physical cores, interleaved rounds, CPU from /proc/<pid>/stat):

workload main this PR Δ
hello world, 1 request header 26.0 µs/req 24.2 µs/req −6.9% CPU, +7.5% req/s (8/8 rounds)
hello world, 8 request headers 28.7 µs/req 25.3 µs/req −11.8% CPU, +13% req/s (5/5 rounds)

Sampled heap allocation drops from 5206 to 4531 B/request (−13%); the req.headers build (~200 B + per-name toLowerCase per request) disappears from the profile entirely.

test/parallel/test-http-*, test-http2-*, test-stream-*, test-net-*, test-https-* and the http diagnostics-channel suites all pass (1171 tests).

Follow-up to #65732 and #65749.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TV8WVjiHJPR2hW4D6h1Xpr

Remove allocations and repeated work performed for every request:

- reuse a per-connection updateOutgoingData closure and a shared
  'finish' listener instead of binding two functions per request,
  reaching resOnFinish through the connection state stored on the
  response
- cache the ServerResponse options object per server (custom response
  classes keep receiving a fresh object)
- check for Host, Expect, Content-Length and Transfer-Encoding by
  scanning rawHeaders instead of materializing req.headers, which was
  built (with per-name toLowerCase calls) for every request even when
  the application never reads it
- cache the rendered status line per status code when the reason
  phrase is the default, skipping its character validation
- cache the complete 'Date: ...' header line in the utcDate cache and
  the keep-alive header pair for the current server settings
- compute the lenient-validation option chain once per message

Signed-off-by: Matteo Collina <[email protected]>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net

@nodejs-github-bot nodejs-github-bot added http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run. labels Sep 4, 2026
@mcollina mcollina added the request-ci Add this label to start a Jenkins CI on a PR. label Sep 4, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Sep 4, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@codecov

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.15%. Comparing base (452c8c5) to head (ed051a3).
⚠️ Report is 133 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65802      +/-   ##
==========================================
+ Coverage   90.05%   90.15%   +0.09%     
==========================================
  Files         754      769      +15     
  Lines      255727   261721    +5994     
  Branches    48312    49688    +1376     
==========================================
+ Hits       230286   235944    +5658     
- Misses      16564    16795     +231     
- Partials     8877     8982     +105     
Files with missing lines Coverage Δ
lib/_http_incoming.js 97.99% <100.00%> (+0.05%) ⬆️
lib/_http_outgoing.js 97.93% <100.00%> (+0.02%) ⬆️
lib/_http_server.js 96.82% <100.00%> (+0.20%) ⬆️
lib/internal/http.js 92.59% <100.00%> (+0.25%) ⬆️

... and 158 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.

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

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants