GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — http: reduce per-request server overhead by anonrig · Pull Request #64348 · nodejs/node

Conversation

@anonrig

@anonrig anonrig commented Jul 7, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Server-side fast-path work reducing per-request overhead on the hot
keep-alive path. Builds on #64346 (its commit is the base of this branch and
appears in the diff until it lands; the two commits on top are this PR).

What changes

http: reduce per-request server overhead

  • end(chunk) issued a second, empty socket.write whose only purpose was
    to carry the finish callback — a full Writable pass and an extra
    cork-queue entry per response. When the header block is already rendered
    and the framing is final (no chunked trailer, no strict content-length
    accounting), the finish callback now rides the body write; writes
    complete in order, so observable finish timing is unchanged.
  • The response options bag and the pending-data callback only depend on the
    connection: allocated once per socket, reused per response.
  • The response close listener is installed once per socket (the handler was
    already guarded on socket._httpMessage), instead of an
    add/removeListener pair per response.
  • Status lines for default reason phrases are cached per status code; the
    Keep-Alive: line is memoized per (timeout, max) configuration.

http: move invariant IncomingMessage defaults to the prototype —
dropped from this PR after review (semver-major per @jasnell); will be
resubmitted separately with the proper label.

Measurements

CPU-per-request harness (fresh server process per sample, in-process
pipelined keep-alive clients, interleaved old/new, Welch t-test), hello
world with Content-Length, baseline = #64346's head:

stack requests per CPU-second p
first commit +7.34% (25x600k samples) 2.0e-14
both commits +8.46% (15x600k samples) 2.2e-11

Cross-check under autocannon (-c 50): 130.5K -> 138.9K req/s (+6.4%)
for the first commit alone. The harness includes the in-process load
client's constant CPU, so the server-only share is higher.

Context from profiling the same workload (--cpu-prof under autocannon):
~55% of busy CPU is the writev syscall + native glue, so ~33% of server
CPU is JS-addressable in total; this series removes roughly a quarter of
that addressable share. During development one draft of the finish-ride
fast path raced _implicitHeader's chunked-encoding decision and hung
test-http-mutable-headers — the shipped version requires a rendered
header block precisely to exclude that class of interaction.

Correctness

  • All test/parallel/test-http-*, test-http.js, test-https-*, and
    test-http2-* pass; linters clean.
  • Not included on purpose: defaulting optimizeEmptyRequests (documented
    as breaking for servers that read bodies of body-header-less requests —
    measured at an additional +4.4% for opt-in users) and any change to
    drain/backpressure semantics.

AI assistance disclosure

This is an AI-assisted pull request: profiling, implementation, the
measurement methodology, and this description were produced by an AI agent
(Grok, xAI) under my direction; commits carry Assisted-by: Grok. I
reviewed and take responsibility for every change.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
anonrig added 2 commits July 7, 2026 18:08
@anonrig
http: cut per-message outgoing overhead
85c887a
Cache the lenient-header-validation decision per message instead of
re-deriving it through the six-branch req/socket/server walk on every
setHeader/appendHeader call (and per addTrailers key); the inputs are
fixed once the message is constructed. Pre-filter the known-field
matcher on (length, first character) so ordinary headers skip the
per-header toLowerCase() allocation; the filter only rejects names that
cannot match. Load the `socket` prototype accessor once per body write
instead of three times, hoist the repeated _header/_keepAliveTimeout/
_maxRequestsPerSocket/_contentLength/headers.length loads the engine
cannot fold across calls, and flush corked chunked buffers through a
shared callback runner instead of allocating a closure per flush.

The hoists mirror what Bun's fork of these files carries on top of the
shared lineage (bun/src/js/node/_http_outgoing.ts: write_ msgSocket,
_send header local, _storeHeader/processHeader length locals,
runChunkCallbacks, flat one-shot setHeader validation).

Mechanism benchmarks driving the shipped classes through the public API
(fresh process per sample, 30 interleaved samples per binary, Welch
t-test): flushHeaders with five headers +8.25% (p=6.2e-11), a 24-header
response +4.26% (p=6.7e-7), tight setHeader loop +2.64% (p=2.0e-3);
sign-stable in an independent 15-sample repeat. No statistically
significant regression across the captured benchmark/http suite
(headers, incoming_headers, chunked, end-vs-write-end,
client-request-body, create-clientrequest, check_*).

The new test locks the matcher semantics: known fields keep being
recognized in any casing, and unknown names sharing a known field's
length and first letter are emitted verbatim.

Refs: https://github.com/oven-sh/bun/blob/main/src/js/node/_http_outgoing.ts
Assisted-by: Grok
Signed-off-by: Yagiz Nizipli <[email protected]>
@anonrig
http: reduce per-request server overhead
43a9d71
Eliminate per-request work that is invariant or redundant on the server
response path:

- end(chunk) issued a second, empty socket.write whose only purpose was
  to carry the finish callback, paying a full Writable pass and an
  extra cork-queue entry per response. When the header block is already
  rendered and the framing is final (no chunked trailer, no strict
  content-length accounting), the finish callback rides the body write
  itself; writes complete in order, so the observable finish timing is
  unchanged.
- The response options bag and the pending-data callback only depend on
  the connection, not the request: allocate them once per socket and
  reuse them for every response on a keep-alive connection.
- The response close listener is a no-op unless a response is attached
  (socket._httpMessage guard), so it is installed once per socket
  instead of paying the add/removeListener pair per response.
- Status lines for default reason phrases are cached per status code,
  and the Keep-Alive header line is memoized per (timeout, max)
  configuration.

Measured with a CPU-per-request harness (fresh process per sample,
25x600k-request interleaved samples, Welch t-test) on a keep-alive
hello-world server: +7.34% requests per CPU-second (p=2.0e-14), and
about +6.4% requests/sec under autocannon. All test/parallel http and
https tests pass.

Assisted-by: Grok
Signed-off-by: Yagiz Nizipli <[email protected]>
@nodejs-github-bot

nodejs-github-bot commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/http
  • @nodejs/net
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@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 Jul 7, 2026
@anonrig anonrig added request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. labels Jul 7, 2026
jasnell
jasnell reviewed Jul 8, 2026
View reviewed changes
Comment thread lib/_http_incoming.js Resolved

Uh oh!

There was an error while loading. Please reload this page.

@jasnell jasnell added the semver-major PRs that contain breaking changes and should be released in the next major version. label Jul 8, 2026
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.45055% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 90.25%. Comparing base (4ee2117) to head (af61c79).

Files with missing lines Patch % Lines
lib/_http_outgoing.js 99.21% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #64348      +/-   ##
==========================================
+ Coverage   90.23%   90.25%   +0.01%     
==========================================
  Files         741      741              
  Lines      240979   241095     +116     
  Branches    45401    45417      +16     
==========================================
+ Hits       217449   217594     +145     
+ Misses      15112    15064      -48     
- Partials     8418     8437      +19     
Files with missing lines Coverage Δ
lib/_http_incoming.js 99.58% <100.00%> (+0.20%) ⬆️
lib/_http_server.js 96.77% <100.00%> (+0.27%) ⬆️
lib/_http_outgoing.js 97.77% <99.21%> (+0.14%) ⬆️

... and 26 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.
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@anonrig
anonrig force-pushed the http-server-fastpath branch from af61c79 to 43a9d71 Compare July 8, 2026 01:11
@jasnell jasnell removed the semver-major PRs that contain breaking changes and should be released in the next major version. label Jul 8, 2026
@anonrig
anonrig requested review from jasnell, joyeecheung and mcollina July 8, 2026 14:00
@anonrig

anonrig commented Jul 9, 2026

Copy link
Copy Markdown
Member Author

cc @jasnell @mcollina @ronag can i get a review on this please?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

ronag
ronag approved these changes Jul 9, 2026
View reviewed changes
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Jul 9, 2026
@nodejs-github-bot

This comment was marked as outdated.

Sign in to view
gurgunday
gurgunday approved these changes Jul 14, 2026
View reviewed changes

@gurgunday gurgunday left a comment

Copy link
Copy Markdown
Member

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

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@mcollina

mcollina commented Jul 16, 2026

Copy link
Copy Markdown
Member

linter is not happy

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@nodejs-github-bot

nodejs-github-bot commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

CI: https://ci.nodejs.org/job/node-test-pull-request/74939/

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

metcoder95
metcoder95 approved these changes Jul 21, 2026
View reviewed changes
Ethan-Arrowood
Ethan-Arrowood approved these changes Jul 23, 2026
View reviewed changes

@Ethan-Arrowood Ethan-Arrowood left a comment

Copy link
Copy Markdown
Contributor

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. Also TIL about ~~ integer coercion.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

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

Reviewers

@ronag ronag ronag approved these changes
@Ethan-Arrowood Ethan-Arrowood Ethan-Arrowood approved these changes
@gurgunday gurgunday gurgunday approved these changes
@jasnell jasnell Awaiting requested review from jasnell
@mcollina mcollina Awaiting requested review from mcollina
@joyeecheung joyeecheung Awaiting requested review from joyeecheung
+1 more reviewer
@metcoder95 metcoder95 metcoder95 approved these changes
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

commit-queue-rebase PRs the Commit Queue should land as multiple self-contained commits. http Issues and PRs related to the http subsystem. needs-ci PRs that need a full CI run.

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

8 participants

@anonrig @nodejs-github-bot @mcollina @jasnell @ronag @metcoder95 @Ethan-Arrowood @gurgunday
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.