Repository navigation
perf: avoid redundant request-part reads during validation - #7018
Merged
Merged
Conversation
Signed-off-by: Gürgün Dayıoğlu <[email protected]>
jean-michelet
approved these changes
Sep 10, 2026
This comment was marked as resolved.
This comment was marked as resolved.
Member
Yep, we push on main and backport to 5.x when non-breaking. |
climba03003
pushed a commit
that referenced
this pull request
Sep 11, 2026
Before the patch, validation could read headers twice despite having no
header validator. If application code had previously assigned
`request.headers = {...}`, those reads copied the merged headers twice
in this path. You can see why we copied in the headers getter here:
https://github.com/fastify/fastify/blob/af079bd4c60c3cbebedc7640517d7288468fb5eb/lib/request.js#L277-L286
The patch checks for a validator before reading the request part and
reads validated parts once. Query-only validation therefore reduces
header copies from two to zero; header validation reduces them from two
to one.
Header assignment may occur in onRequest, preParsing, a content-type
parser, or preValidation, including encapsulated hooks.
```text
Scenario Before After Change
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━ ━━━━━━━━
Query validation + enriched headers 51,456 req/s 94,349 req/s +83%
───────────────────────────────────────────────── ─────────────── ─────────────── ────────
Query + header validation with enriched headers 51,059 req/s 64,957 req/s +27%
───────────────────────────────────────────────── ─────────────── ─────────────── ────────
Query validation only 147,968 req/s 147,024 req/s −0.64% (flat)
```
Signed-off-by: Gürgün Dayıoğlu <[email protected]>
(cherry picked from commit d266f83)
gurgunday
added a commit
that referenced
this pull request
Sep 11, 2026
…tion (#7020) Backport d266f83 from #7018. Signed-off-by: Gürgün Dayıoğlu <[email protected]> Co-authored-by: Gürgün Dayıoğlu <[email protected]>
hktitof
added a commit
to hktitof/fastify
that referenced
this pull request
Sep 22, 2026
the trust proxy host getter reads request.headers twice per access, once for the x-forwarded-host check and once for the host fallback. the protocol getter does the same on its x-forwarded-proto branch. every read of that getter copies the full merged header set when request.headers was assigned in a hook, so one request.host or request.protocol read copied the headers twice. fastify#7018 fixed the same double read in the validation path the getters now read the merged headers once and reuse that object, so a read copies the headers at most once. the returned values are unchanged Signed-off-by: Abdel <[email protected]>
jean-michelet
pushed a commit
that referenced
this pull request
Sep 22, 2026
> **Context:** Sibling to #7018 (which stopped validation from reading request parts when no validator exists). This addresses the trust proxy host and protocol getters in lib/request.js (request.js:120-148), which still read the copying request.headers getter twice per access. the trust proxy request class defines its own host and protocol getters that honor x-forwarded-host and x-forwarded-proto when the proxy is trusted. the host getter reads this.headers twice per access, once for the x-forwarded-host check and once for the host fallback, and the protocol getter pays the same price on its x-forwarded-proto branch. every read of the request.headers getter copies the full merged header set when request.headers was assigned in a hook (onRequest, preParsing, a content-type parser or preValidation, same list #7018 documented) so with a hook like ```js fastify.addHook('onRequest', (request, reply, done) => { request.headers = { ...request.headers, 'x-request-id': id } done() }) ``` one request.host access copied all headers twice, request.hostname inherits it through this.host and request.protocol pays the same price on its own branch. #7018 already fixed the validation side of this, this PR applies the same rule to the getters themselves: read this.headers once and reuse that object, so an access copies the merged headers at most once the returned values are unchanged in every case, this is a pure allocation reduction, no default or precedence moves. the regular (non trust proxy) host getter already reads raw.headers directly and needs no change testing proof: - new tests in test/internals/request.test.js count the reads of the merged headers getter through an own accessor on the request and assert one copy per access, one test for the host getter and one for the protocol getter: each fails on main with 2 !== 1 and passes with the fix (16/16 in that file) - a live probe on untouched main shows one request.host read copies the merged headers twice, once with the fix, values identical in both cases - adjacent internals stay green: test/internals/handle-request.test.js + test/internals/request-validate.test.js → 51/51 - full `npm run unit` 2343 tests, 0 failures, `npm run test:types` 1282 assertions pass, `npx eslint lib/request.js test/internals/request.test.js` clean - one commit, Signed-off-by present (DCO) --- This change was developed with AI assistance. The analysis, reproduction, red/green test evidence, and final diff were reviewed by the branch author before opening this pull request. Signed-off-by: Abdel <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Before the patch, validation could read headers twice despite having no header validator. If application code had previously assigned
request.headers = {...}, those reads copied the merged headers twice in this path. You can see why we copied in the headers getter here:fastify/lib/request.js
Lines 277 to 286 in af079bd
The patch checks for a validator before reading the request part and reads validated parts once. Query-only validation therefore reduces header copies from two to zero; header validation reduces them from two to one.
Header assignment may occur in onRequest, preParsing, a content-type parser, or preValidation, including encapsulated hooks.