Skip to content

feat(util): expose request header normalization - #5780

Open
GiHoon1123 wants to merge 3 commits into
nodejs:mainfrom
GiHoon1123:fix-dispatcher-normalize-headers
Open

feat(util): expose request header normalization#5780
GiHoon1123 wants to merge 3 commits into
nodejs:mainfrom
GiHoon1123:fix-dispatcher-normalize-headers

Conversation

@GiHoon1123

@GiHoon1123 GiHoon1123 commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Refs #4336.

Request headers can reach composed interceptors as an object, flat array, Headers instance, or another iterable. This adds util.normalizeHeaders() so interceptors that inspect headers can convert those inputs to a lower-case object while preserving repeated values.

The cache and deduplicate interceptors now use the same helper. compose() no longer normalizes every request, avoiding extra header work for interceptors that do not inspect them.

Tests:

  • npm run lint
  • npm run test:typescript
  • borp --timeout 180000 test/interceptors/interceptors-on-client.js test/interceptors/cache-query-params.js test/interceptors/redirect.js test/cache-interceptor/cache-utils.js test/node-test/util.js
  • node --test --test-name-pattern '#5522' test/interceptors/dns.js

@codecov-commenter

codecov-commenter commented Sep 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.55319% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.51%. Comparing base (b73952a) to head (058d912).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
lib/core/util.js 92.13% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #5780      +/-   ##
==========================================
- Coverage   93.51%   93.51%   -0.01%     
==========================================
  Files         110      110              
  Lines       39359    39552     +193     
==========================================
+ Hits        36808    36986     +178     
- Misses       2551     2566      +15     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 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.

@mcollina

mcollina commented Sep 6, 2026

Copy link
Copy Markdown
Member

I'm not convinced by this change, as it's significantly perf cost.

Wdyt @ronag?

@ronag

ronag commented Sep 6, 2026

Copy link
Copy Markdown
Member

Yea. This is probably not a good idea...

@GiHoon1123

Copy link
Copy Markdown
Contributor Author

That makes sense. The current implementation wraps every interceptor boundary, so it does more work than necessary for each request.

I’m thinking of changing it to normalize once at the composed dispatcher entry, skip plain objects that are already normalized, and keep the resulting object through interceptor redispatches. Would that address the performance concern, or would you prefer a different approach?

Composed interceptors received headers in several different formats depending on how a request was made. Normalize them at each interceptor boundary while retaining repeated values.

Signed-off-by: GiHoon1123 <rlaejrqo465@naver.com>
@GiHoon1123
GiHoon1123 force-pushed the fix-dispatcher-normalize-headers branch from 770da53 to 88e5463 Compare September 7, 2026 00:54
@GiHoon1123

Copy link
Copy Markdown
Contributor Author

I reworked this to avoid wrapping each interceptor boundary. Headers are now normalized once when the composed dispatcher is entered, and already-normalized plain objects are passed through without a copy. The existing DNS redispatch cases still pass with the normalized object.

The updated commit is 88e54638. The relevant interceptor tests and lint pass locally.

Comment thread lib/dispatcher/dispatcher.js Outdated

if (Array.isArray(headers)) {
if (headers.length > 0 && Array.isArray(headers[0])) {
if (headers.some(header => !Array.isArray(header) || header.length !== 2)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's try to prefer for-loops over array methods; this will sit in a hot-path if interceptors are used

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated this to use a loop.

Comment thread lib/dispatcher/dispatcher.js Outdated
}
}

function normalizeHeaders (headers) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit worried of all the branching, but unsure if it can be simplified

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took another look at the request path. Normalizing in compose() means walking the headers to build an object, then Request walks them again to build its internal array. It also adds that work for interceptors that never read request headers.

Would it be better to expose a shared normalization helper for interceptors that need it? If normalization should happen automatically, it seems like we’d need a shared internal representation that Request can reuse. What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea, go for it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented in 058d912. normalizeHeaders() is now exposed through util, and cache and deduplicate use the same helper. I removed eager normalization from compose(), so interceptors that do not inspect headers no longer pay the extra cost. Types, docs, and the relevant tests have been updated.

Signed-off-by: Gihoon1123 <rlaejrqo465@naver.com>
Let interceptors normalize supported header inputs only when they need to inspect them. Reuse the same helper in cache and deduplicate without adding work to every composed request.

Signed-off-by: GiHoon1123 <rlaejrqo465@naver.com>
@GiHoon1123 GiHoon1123 changed the title fix(dispatcher): normalize headers before composed interceptors feat(util): expose request header normalization Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants