fix: dns interceptor with Pool - #3957

Closed
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor
Closed

fix: dns interceptor with Pool#3957
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor

Conversation

@luddd3

Copy link
Copy Markdown
Contributor

This relates to...

Rationale

The DNS interceptor didn't work with Pool/Client since it didn't get the origin unless included with each request.

Changes

Features

Bug Fixes

  • fix dns interceptor with Client/Pool

Breaking Changes and Deprecations

Status

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

The origin should always be in origDispatchOpts. This seems to be a hack.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The origin should always be in origDispatchOpts. This seems to be a hack.

I kinda agree, but didn't find where else to put it?

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@luddd3

Copy link
Copy Markdown
ContributorAuthor

I made an alternative solution. It currently breaks a test for test/interceptors/retry.js and I'm unsure if it is an actual error or not? Should I just change the expected value?

@metcoder95metcoder95 left a comment

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.

Just adjust the retry tests and should be ok to go.

The change is kind of expected as the origin is being explicitly passed and is the first origin the client is facing

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@ronag

Copy link
Copy Markdown
Member

This still looks weird... where/how does the origin get lost?

@luddd3

luddd3 commented Dec 19, 2024

Copy link
Copy Markdown
ContributorAuthor

The interceptor does only have access to the options called by .request(). The request method has access to all variables in this (e.g Pool) since it is bound to this here:

letdispatch=this.dispatch.bind(this)

It could probably be solved in the same manner for the interceptor by changing:

dispatch=interceptor(dispatch)

Into:

dispatch=interceptor.bind(this)(dispatch)

or something like this (which is a bit like my original approach):

dispatch=interceptor(dispatch,this)

The last method has the benefit of allowing the interceptors to return arrow functions as well as ordinary functions.

@mcollina

Copy link
Copy Markdown
Member

THis now conflicts, can you rebase?

@luddd3
luddd3force-pushed the fix-pool-dns-interceptor branch from 43a4023 to da237d9CompareDecember 20, 2024 05:59

@ronagronag left a comment

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.

This is wrong... not sure when I have time to look into it. Christmas...

@metcoder95

metcoder95 commented Dec 20, 2024

Copy link
Copy Markdown
Member

Let me try to check it during the weekend; if there's a workaround, let's take it, otherwise this should be ok for now and we can revisit later so dns is unblocked

ronag

This comment was marked as resolved.

@ronagronag left a comment

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.

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

If anything maybe the dns interceptor should check for origin and throw an invalid arg error. Alternatively, if no origin is passed then the dns interceptor is a noop.

@ronag

Copy link
Copy Markdown
Member
diff --git a/lib/interceptor/dns.js b/lib/interceptor/dns.js
index c8d56c2c..c6a1a480 100644
--- a/lib/interceptor/dns.js
+++ b/lib/interceptor/dns.js
@@ -342,6 +342,10 @@ module.exports = interceptorOpts => {
return dispatch => {
return function dnsInterceptor (origDispatchOpts, handler) {
+ if (origDispatchOpts.origin == null) {
+ return dispatch(origDispatchOpts, handler)
+ }
+
const origin =

@mcollinamcollina left a comment

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.

lgtm

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

@ronag That was my assumption. Just to be clear, do you think it should be like this?

constclient=newPool('https://google.com',{connections: 10})awaitclient.request({origin: 'https://google.com',// origin must be included in each requestmethod: 'GET',path: '/'})

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

@ronag

ronag commented Dec 21, 2024

Copy link
Copy Markdown
Member

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

Possibly. Then let's just make the dns interceptor a noop when origin is missing as I suggested with the patch.

@metcoder95

Copy link
Copy Markdown
Member

I do agree that the documentation has some statements that are either not fully true or confusing for the reader that can lead to a wrong assumption; but that's at an overall take.

The last being said; I'm not 100% in sync with the statement that passing the origin should be mandated on every request call. I'm in sync when use through dynamic dispatchers like Agent or the BalancedPool, but not fully in sync for Client and Pool which are tight to a single origin.

For the latter use-cases, they should have the origin already stick to their state and do not ask the caller to pass it as its goal is to be tight to a single downstream.

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

Below is an example of how it works today when different origin is provided to Client during the constructor and request(). The caller might think that the request will be made to http://localhost:2000, which it won't. In this case a thrown error would be much clearer and avoid mistakes. It works similarly for Pool.

import{createServer}from'node:http'import{Client}from'undici'constserver=createServer()server.on('request',(req,res)=>{res.end('hello')})server.listen(0)constclient=newClient(`http://localhost:${server.address().port}`)constresponse=awaitclient.request({method: 'GET',origin: 'http://localhost:2000',path: '/'})console.log(awaitresponse.body.text())// will print 'hello'awaitserver.close()

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

I think that it should throw an error, since it should never be the case that origin is missing. Unless it can get origin directly from Client or Pool via one of the solutions I provided earlier.

@ronag

Copy link
Copy Markdown
Member

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

It should throw when a different origin is provided. Yes.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

It should throw when a different origin is provided. Yes.

I can understand from the viewpoint of backwards-compatibility, but are there other benefits for not throwing every time? I'm afraid that it encourages the wrong behavior to allow an origin, which isn't used,. I have however made a commit which does that.

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@ronag

  1. Why is it better to ignore requests without origin in the dns interceptor instead of throwing an error?
  2. Do you mean that I should remove all code I placed in Client and Pool, or just that I shouldn't modify the request options if origin was provided?

@ronag

ronag commented Dec 31, 2024

Copy link
Copy Markdown
Member

The only change here should be a condition in the dns interceptor and a test.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The only change here should be a condition in the dns interceptor and a test.

So basically remove all changes so far, apply the patch your provided earlier and then write a test?

@luddd3

Copy link
Copy Markdown
ContributorAuthor

IMHO that doesn't solve anything. I as a user would not expect the dns interceptor to be silently bypassed when origin wasn't provided again in my request.

Everything points to origin not being necessary with Client and Pool and it is also not possible to switch origin, so why not override request and make sure that the interceptors get it? Even the examples in the documentation https://undici.nodejs.org/#/docs/api/Client?id=example-client-connect-event shows that request can be called without providing origin again.

@metcoder95

Copy link
Copy Markdown
Member

Gentle ping

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Gentle ping

I don't know how to move forward since none of my proposed solutions have been accepted and I don't think the alternatives are any good. Do you have any ideas?

@metcoder95

Copy link
Copy Markdown
Member

cc: @ronag

@ronag

ronag commented Jun 2, 2025

Copy link
Copy Markdown
Member

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

I think it is wrong that:

  1. Client and Pool forces the user to pass origin for each request. It isn't documentet and wouldn't work for different origins. fix: dns interceptor with Pool #3957 (comment)
  2. the dns interceptor should be silently bypassed if a different origin is passed. I much rather it throws an error so that the user is informed that there is something wrong.

@marko1olomarko1olo mentioned this pull request Jun 7, 2026
3 tasks
@luddd3

Copy link
Copy Markdown
ContributorAuthor

Fixed with #5624

@luddd3luddd3 closed this Sep 2, 2026
Sign up for freeto 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.

4 participants

@luddd3@ronag@mcollina@metcoder95
, '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

fix: dns interceptor with Pool - #3957

Closed
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor
Closed

fix: dns interceptor with Pool#3957
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor

Conversation

@luddd3

Copy link
Copy Markdown
Contributor

This relates to...

Rationale

The DNS interceptor didn't work with Pool/Client since it didn't get the origin unless included with each request.

Changes

Features

Bug Fixes

  • fix dns interceptor with Client/Pool

Breaking Changes and Deprecations

Status

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

The origin should always be in origDispatchOpts. This seems to be a hack.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The origin should always be in origDispatchOpts. This seems to be a hack.

I kinda agree, but didn't find where else to put it?

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@luddd3

Copy link
Copy Markdown
ContributorAuthor

I made an alternative solution. It currently breaks a test for test/interceptors/retry.js and I'm unsure if it is an actual error or not? Should I just change the expected value?

@metcoder95metcoder95 left a comment

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.

Just adjust the retry tests and should be ok to go.

The change is kind of expected as the origin is being explicitly passed and is the first origin the client is facing

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@ronag

Copy link
Copy Markdown
Member

This still looks weird... where/how does the origin get lost?

@luddd3

luddd3 commented Dec 19, 2024

Copy link
Copy Markdown
ContributorAuthor

The interceptor does only have access to the options called by .request(). The request method has access to all variables in this (e.g Pool) since it is bound to this here:

letdispatch=this.dispatch.bind(this)

It could probably be solved in the same manner for the interceptor by changing:

dispatch=interceptor(dispatch)

Into:

dispatch=interceptor.bind(this)(dispatch)

or something like this (which is a bit like my original approach):

dispatch=interceptor(dispatch,this)

The last method has the benefit of allowing the interceptors to return arrow functions as well as ordinary functions.

@mcollina

Copy link
Copy Markdown
Member

THis now conflicts, can you rebase?

@luddd3
luddd3force-pushed the fix-pool-dns-interceptor branch from 43a4023 to da237d9CompareDecember 20, 2024 05:59

@ronagronag left a comment

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.

This is wrong... not sure when I have time to look into it. Christmas...

@metcoder95

metcoder95 commented Dec 20, 2024

Copy link
Copy Markdown
Member

Let me try to check it during the weekend; if there's a workaround, let's take it, otherwise this should be ok for now and we can revisit later so dns is unblocked

ronag

This comment was marked as resolved.

@ronagronag left a comment

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.

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

If anything maybe the dns interceptor should check for origin and throw an invalid arg error. Alternatively, if no origin is passed then the dns interceptor is a noop.

@ronag

Copy link
Copy Markdown
Member
diff --git a/lib/interceptor/dns.js b/lib/interceptor/dns.js
index c8d56c2c..c6a1a480 100644
--- a/lib/interceptor/dns.js
+++ b/lib/interceptor/dns.js
@@ -342,6 +342,10 @@ module.exports = interceptorOpts => {
return dispatch => {
return function dnsInterceptor (origDispatchOpts, handler) {
+ if (origDispatchOpts.origin == null) {
+ return dispatch(origDispatchOpts, handler)
+ }
+
const origin =

@mcollinamcollina left a comment

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.

lgtm

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

@ronag That was my assumption. Just to be clear, do you think it should be like this?

constclient=newPool('https://google.com',{connections: 10})awaitclient.request({origin: 'https://google.com',// origin must be included in each requestmethod: 'GET',path: '/'})

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

@ronag

ronag commented Dec 21, 2024

Copy link
Copy Markdown
Member

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

Possibly. Then let's just make the dns interceptor a noop when origin is missing as I suggested with the patch.

@metcoder95

Copy link
Copy Markdown
Member

I do agree that the documentation has some statements that are either not fully true or confusing for the reader that can lead to a wrong assumption; but that's at an overall take.

The last being said; I'm not 100% in sync with the statement that passing the origin should be mandated on every request call. I'm in sync when use through dynamic dispatchers like Agent or the BalancedPool, but not fully in sync for Client and Pool which are tight to a single origin.

For the latter use-cases, they should have the origin already stick to their state and do not ask the caller to pass it as its goal is to be tight to a single downstream.

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

Below is an example of how it works today when different origin is provided to Client during the constructor and request(). The caller might think that the request will be made to http://localhost:2000, which it won't. In this case a thrown error would be much clearer and avoid mistakes. It works similarly for Pool.

import{createServer}from'node:http'import{Client}from'undici'constserver=createServer()server.on('request',(req,res)=>{res.end('hello')})server.listen(0)constclient=newClient(`http://localhost:${server.address().port}`)constresponse=awaitclient.request({method: 'GET',origin: 'http://localhost:2000',path: '/'})console.log(awaitresponse.body.text())// will print 'hello'awaitserver.close()

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

I think that it should throw an error, since it should never be the case that origin is missing. Unless it can get origin directly from Client or Pool via one of the solutions I provided earlier.

@ronag

Copy link
Copy Markdown
Member

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

It should throw when a different origin is provided. Yes.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

It should throw when a different origin is provided. Yes.

I can understand from the viewpoint of backwards-compatibility, but are there other benefits for not throwing every time? I'm afraid that it encourages the wrong behavior to allow an origin, which isn't used,. I have however made a commit which does that.

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@ronag

  1. Why is it better to ignore requests without origin in the dns interceptor instead of throwing an error?
  2. Do you mean that I should remove all code I placed in Client and Pool, or just that I shouldn't modify the request options if origin was provided?

@ronag

ronag commented Dec 31, 2024

Copy link
Copy Markdown
Member

The only change here should be a condition in the dns interceptor and a test.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The only change here should be a condition in the dns interceptor and a test.

So basically remove all changes so far, apply the patch your provided earlier and then write a test?

@luddd3

Copy link
Copy Markdown
ContributorAuthor

IMHO that doesn't solve anything. I as a user would not expect the dns interceptor to be silently bypassed when origin wasn't provided again in my request.

Everything points to origin not being necessary with Client and Pool and it is also not possible to switch origin, so why not override request and make sure that the interceptors get it? Even the examples in the documentation https://undici.nodejs.org/#/docs/api/Client?id=example-client-connect-event shows that request can be called without providing origin again.

@metcoder95

Copy link
Copy Markdown
Member

Gentle ping

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Gentle ping

I don't know how to move forward since none of my proposed solutions have been accepted and I don't think the alternatives are any good. Do you have any ideas?

@metcoder95

Copy link
Copy Markdown
Member

cc: @ronag

@ronag

ronag commented Jun 2, 2025

Copy link
Copy Markdown
Member

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

I think it is wrong that:

  1. Client and Pool forces the user to pass origin for each request. It isn't documentet and wouldn't work for different origins. fix: dns interceptor with Pool #3957 (comment)
  2. the dns interceptor should be silently bypassed if a different origin is passed. I much rather it throws an error so that the user is informed that there is something wrong.

@marko1olomarko1olo mentioned this pull request Jun 7, 2026
3 tasks
@luddd3

Copy link
Copy Markdown
ContributorAuthor

Fixed with #5624

@luddd3luddd3 closed this Sep 2, 2026
Sign up for freeto 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.

4 participants

@luddd3@ronag@mcollina@metcoder95
, '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

fix: dns interceptor with Pool - #3957

Closed
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor
Closed

fix: dns interceptor with Pool#3957
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor

Conversation

@luddd3

Copy link
Copy Markdown
Contributor

This relates to...

Rationale

The DNS interceptor didn't work with Pool/Client since it didn't get the origin unless included with each request.

Changes

Features

Bug Fixes

  • fix dns interceptor with Client/Pool

Breaking Changes and Deprecations

Status

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

The origin should always be in origDispatchOpts. This seems to be a hack.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The origin should always be in origDispatchOpts. This seems to be a hack.

I kinda agree, but didn't find where else to put it?

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@luddd3

Copy link
Copy Markdown
ContributorAuthor

I made an alternative solution. It currently breaks a test for test/interceptors/retry.js and I'm unsure if it is an actual error or not? Should I just change the expected value?

@metcoder95metcoder95 left a comment

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.

Just adjust the retry tests and should be ok to go.

The change is kind of expected as the origin is being explicitly passed and is the first origin the client is facing

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@ronag

Copy link
Copy Markdown
Member

This still looks weird... where/how does the origin get lost?

@luddd3

luddd3 commented Dec 19, 2024

Copy link
Copy Markdown
ContributorAuthor

The interceptor does only have access to the options called by .request(). The request method has access to all variables in this (e.g Pool) since it is bound to this here:

letdispatch=this.dispatch.bind(this)

It could probably be solved in the same manner for the interceptor by changing:

dispatch=interceptor(dispatch)

Into:

dispatch=interceptor.bind(this)(dispatch)

or something like this (which is a bit like my original approach):

dispatch=interceptor(dispatch,this)

The last method has the benefit of allowing the interceptors to return arrow functions as well as ordinary functions.

@mcollina

Copy link
Copy Markdown
Member

THis now conflicts, can you rebase?

@luddd3
luddd3force-pushed the fix-pool-dns-interceptor branch from 43a4023 to da237d9CompareDecember 20, 2024 05:59

@ronagronag left a comment

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.

This is wrong... not sure when I have time to look into it. Christmas...

@metcoder95

metcoder95 commented Dec 20, 2024

Copy link
Copy Markdown
Member

Let me try to check it during the weekend; if there's a workaround, let's take it, otherwise this should be ok for now and we can revisit later so dns is unblocked

ronag

This comment was marked as resolved.

@ronagronag left a comment

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.

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

If anything maybe the dns interceptor should check for origin and throw an invalid arg error. Alternatively, if no origin is passed then the dns interceptor is a noop.

@ronag

Copy link
Copy Markdown
Member
diff --git a/lib/interceptor/dns.js b/lib/interceptor/dns.js
index c8d56c2c..c6a1a480 100644
--- a/lib/interceptor/dns.js
+++ b/lib/interceptor/dns.js
@@ -342,6 +342,10 @@ module.exports = interceptorOpts => {
return dispatch => {
return function dnsInterceptor (origDispatchOpts, handler) {
+ if (origDispatchOpts.origin == null) {
+ return dispatch(origDispatchOpts, handler)
+ }
+
const origin =

@mcollinamcollina left a comment

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.

lgtm

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

@ronag That was my assumption. Just to be clear, do you think it should be like this?

constclient=newPool('https://google.com',{connections: 10})awaitclient.request({origin: 'https://google.com',// origin must be included in each requestmethod: 'GET',path: '/'})

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

@ronag

ronag commented Dec 21, 2024

Copy link
Copy Markdown
Member

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

Possibly. Then let's just make the dns interceptor a noop when origin is missing as I suggested with the patch.

@metcoder95

Copy link
Copy Markdown
Member

I do agree that the documentation has some statements that are either not fully true or confusing for the reader that can lead to a wrong assumption; but that's at an overall take.

The last being said; I'm not 100% in sync with the statement that passing the origin should be mandated on every request call. I'm in sync when use through dynamic dispatchers like Agent or the BalancedPool, but not fully in sync for Client and Pool which are tight to a single origin.

For the latter use-cases, they should have the origin already stick to their state and do not ask the caller to pass it as its goal is to be tight to a single downstream.

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

Below is an example of how it works today when different origin is provided to Client during the constructor and request(). The caller might think that the request will be made to http://localhost:2000, which it won't. In this case a thrown error would be much clearer and avoid mistakes. It works similarly for Pool.

import{createServer}from'node:http'import{Client}from'undici'constserver=createServer()server.on('request',(req,res)=>{res.end('hello')})server.listen(0)constclient=newClient(`http://localhost:${server.address().port}`)constresponse=awaitclient.request({method: 'GET',origin: 'http://localhost:2000',path: '/'})console.log(awaitresponse.body.text())// will print 'hello'awaitserver.close()

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

I think that it should throw an error, since it should never be the case that origin is missing. Unless it can get origin directly from Client or Pool via one of the solutions I provided earlier.

@ronag

Copy link
Copy Markdown
Member

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

It should throw when a different origin is provided. Yes.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

It should throw when a different origin is provided. Yes.

I can understand from the viewpoint of backwards-compatibility, but are there other benefits for not throwing every time? I'm afraid that it encourages the wrong behavior to allow an origin, which isn't used,. I have however made a commit which does that.

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@ronag

  1. Why is it better to ignore requests without origin in the dns interceptor instead of throwing an error?
  2. Do you mean that I should remove all code I placed in Client and Pool, or just that I shouldn't modify the request options if origin was provided?

@ronag

ronag commented Dec 31, 2024

Copy link
Copy Markdown
Member

The only change here should be a condition in the dns interceptor and a test.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The only change here should be a condition in the dns interceptor and a test.

So basically remove all changes so far, apply the patch your provided earlier and then write a test?

@luddd3

Copy link
Copy Markdown
ContributorAuthor

IMHO that doesn't solve anything. I as a user would not expect the dns interceptor to be silently bypassed when origin wasn't provided again in my request.

Everything points to origin not being necessary with Client and Pool and it is also not possible to switch origin, so why not override request and make sure that the interceptors get it? Even the examples in the documentation https://undici.nodejs.org/#/docs/api/Client?id=example-client-connect-event shows that request can be called without providing origin again.

@metcoder95

Copy link
Copy Markdown
Member

Gentle ping

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Gentle ping

I don't know how to move forward since none of my proposed solutions have been accepted and I don't think the alternatives are any good. Do you have any ideas?

@metcoder95

Copy link
Copy Markdown
Member

cc: @ronag

@ronag

ronag commented Jun 2, 2025

Copy link
Copy Markdown
Member

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

I think it is wrong that:

  1. Client and Pool forces the user to pass origin for each request. It isn't documentet and wouldn't work for different origins. fix: dns interceptor with Pool #3957 (comment)
  2. the dns interceptor should be silently bypassed if a different origin is passed. I much rather it throws an error so that the user is informed that there is something wrong.

@marko1olomarko1olo mentioned this pull request Jun 7, 2026
3 tasks
@luddd3

Copy link
Copy Markdown
ContributorAuthor

Fixed with #5624

@luddd3luddd3 closed this Sep 2, 2026
Sign up for freeto 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.

4 participants

@luddd3@ronag@mcollina@metcoder95
, '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

fix: dns interceptor with Pool - #3957

Closed
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor
Closed

fix: dns interceptor with Pool#3957
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor

Conversation

@luddd3

Copy link
Copy Markdown
Contributor

This relates to...

Rationale

The DNS interceptor didn't work with Pool/Client since it didn't get the origin unless included with each request.

Changes

Features

Bug Fixes

  • fix dns interceptor with Client/Pool

Breaking Changes and Deprecations

Status

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

The origin should always be in origDispatchOpts. This seems to be a hack.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The origin should always be in origDispatchOpts. This seems to be a hack.

I kinda agree, but didn't find where else to put it?

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@luddd3

Copy link
Copy Markdown
ContributorAuthor

I made an alternative solution. It currently breaks a test for test/interceptors/retry.js and I'm unsure if it is an actual error or not? Should I just change the expected value?

@metcoder95metcoder95 left a comment

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.

Just adjust the retry tests and should be ok to go.

The change is kind of expected as the origin is being explicitly passed and is the first origin the client is facing

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@ronag

Copy link
Copy Markdown
Member

This still looks weird... where/how does the origin get lost?

@luddd3

luddd3 commented Dec 19, 2024

Copy link
Copy Markdown
ContributorAuthor

The interceptor does only have access to the options called by .request(). The request method has access to all variables in this (e.g Pool) since it is bound to this here:

letdispatch=this.dispatch.bind(this)

It could probably be solved in the same manner for the interceptor by changing:

dispatch=interceptor(dispatch)

Into:

dispatch=interceptor.bind(this)(dispatch)

or something like this (which is a bit like my original approach):

dispatch=interceptor(dispatch,this)

The last method has the benefit of allowing the interceptors to return arrow functions as well as ordinary functions.

@mcollina

Copy link
Copy Markdown
Member

THis now conflicts, can you rebase?

@luddd3
luddd3force-pushed the fix-pool-dns-interceptor branch from 43a4023 to da237d9CompareDecember 20, 2024 05:59

@ronagronag left a comment

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.

This is wrong... not sure when I have time to look into it. Christmas...

@metcoder95

metcoder95 commented Dec 20, 2024

Copy link
Copy Markdown
Member

Let me try to check it during the weekend; if there's a workaround, let's take it, otherwise this should be ok for now and we can revisit later so dns is unblocked

ronag

This comment was marked as resolved.

@ronagronag left a comment

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.

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

If anything maybe the dns interceptor should check for origin and throw an invalid arg error. Alternatively, if no origin is passed then the dns interceptor is a noop.

@ronag

Copy link
Copy Markdown
Member
diff --git a/lib/interceptor/dns.js b/lib/interceptor/dns.js
index c8d56c2c..c6a1a480 100644
--- a/lib/interceptor/dns.js
+++ b/lib/interceptor/dns.js
@@ -342,6 +342,10 @@ module.exports = interceptorOpts => {
return dispatch => {
return function dnsInterceptor (origDispatchOpts, handler) {
+ if (origDispatchOpts.origin == null) {
+ return dispatch(origDispatchOpts, handler)
+ }
+
const origin =

@mcollinamcollina left a comment

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.

lgtm

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

@ronag That was my assumption. Just to be clear, do you think it should be like this?

constclient=newPool('https://google.com',{connections: 10})awaitclient.request({origin: 'https://google.com',// origin must be included in each requestmethod: 'GET',path: '/'})

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

@ronag

ronag commented Dec 21, 2024

Copy link
Copy Markdown
Member

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

Possibly. Then let's just make the dns interceptor a noop when origin is missing as I suggested with the patch.

@metcoder95

Copy link
Copy Markdown
Member

I do agree that the documentation has some statements that are either not fully true or confusing for the reader that can lead to a wrong assumption; but that's at an overall take.

The last being said; I'm not 100% in sync with the statement that passing the origin should be mandated on every request call. I'm in sync when use through dynamic dispatchers like Agent or the BalancedPool, but not fully in sync for Client and Pool which are tight to a single origin.

For the latter use-cases, they should have the origin already stick to their state and do not ask the caller to pass it as its goal is to be tight to a single downstream.

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

Below is an example of how it works today when different origin is provided to Client during the constructor and request(). The caller might think that the request will be made to http://localhost:2000, which it won't. In this case a thrown error would be much clearer and avoid mistakes. It works similarly for Pool.

import{createServer}from'node:http'import{Client}from'undici'constserver=createServer()server.on('request',(req,res)=>{res.end('hello')})server.listen(0)constclient=newClient(`http://localhost:${server.address().port}`)constresponse=awaitclient.request({method: 'GET',origin: 'http://localhost:2000',path: '/'})console.log(awaitresponse.body.text())// will print 'hello'awaitserver.close()

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

I think that it should throw an error, since it should never be the case that origin is missing. Unless it can get origin directly from Client or Pool via one of the solutions I provided earlier.

@ronag

Copy link
Copy Markdown
Member

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

It should throw when a different origin is provided. Yes.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

It should throw when a different origin is provided. Yes.

I can understand from the viewpoint of backwards-compatibility, but are there other benefits for not throwing every time? I'm afraid that it encourages the wrong behavior to allow an origin, which isn't used,. I have however made a commit which does that.

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@ronag

  1. Why is it better to ignore requests without origin in the dns interceptor instead of throwing an error?
  2. Do you mean that I should remove all code I placed in Client and Pool, or just that I shouldn't modify the request options if origin was provided?

@ronag

ronag commented Dec 31, 2024

Copy link
Copy Markdown
Member

The only change here should be a condition in the dns interceptor and a test.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The only change here should be a condition in the dns interceptor and a test.

So basically remove all changes so far, apply the patch your provided earlier and then write a test?

@luddd3

Copy link
Copy Markdown
ContributorAuthor

IMHO that doesn't solve anything. I as a user would not expect the dns interceptor to be silently bypassed when origin wasn't provided again in my request.

Everything points to origin not being necessary with Client and Pool and it is also not possible to switch origin, so why not override request and make sure that the interceptors get it? Even the examples in the documentation https://undici.nodejs.org/#/docs/api/Client?id=example-client-connect-event shows that request can be called without providing origin again.

@metcoder95

Copy link
Copy Markdown
Member

Gentle ping

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Gentle ping

I don't know how to move forward since none of my proposed solutions have been accepted and I don't think the alternatives are any good. Do you have any ideas?

@metcoder95

Copy link
Copy Markdown
Member

cc: @ronag

@ronag

ronag commented Jun 2, 2025

Copy link
Copy Markdown
Member

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

I think it is wrong that:

  1. Client and Pool forces the user to pass origin for each request. It isn't documentet and wouldn't work for different origins. fix: dns interceptor with Pool #3957 (comment)
  2. the dns interceptor should be silently bypassed if a different origin is passed. I much rather it throws an error so that the user is informed that there is something wrong.

@marko1olomarko1olo mentioned this pull request Jun 7, 2026
3 tasks
@luddd3

Copy link
Copy Markdown
ContributorAuthor

Fixed with #5624

@luddd3luddd3 closed this Sep 2, 2026
Sign up for freeto 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.

4 participants

@luddd3@ronag@mcollina@metcoder95
, '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

fix: dns interceptor with Pool - #3957

Closed
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor
Closed

fix: dns interceptor with Pool#3957
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor

Conversation

@luddd3

Copy link
Copy Markdown
Contributor

This relates to...

Rationale

The DNS interceptor didn't work with Pool/Client since it didn't get the origin unless included with each request.

Changes

Features

Bug Fixes

  • fix dns interceptor with Client/Pool

Breaking Changes and Deprecations

Status

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

The origin should always be in origDispatchOpts. This seems to be a hack.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The origin should always be in origDispatchOpts. This seems to be a hack.

I kinda agree, but didn't find where else to put it?

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@luddd3

Copy link
Copy Markdown
ContributorAuthor

I made an alternative solution. It currently breaks a test for test/interceptors/retry.js and I'm unsure if it is an actual error or not? Should I just change the expected value?

@metcoder95metcoder95 left a comment

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.

Just adjust the retry tests and should be ok to go.

The change is kind of expected as the origin is being explicitly passed and is the first origin the client is facing

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@ronag

Copy link
Copy Markdown
Member

This still looks weird... where/how does the origin get lost?

@luddd3

luddd3 commented Dec 19, 2024

Copy link
Copy Markdown
ContributorAuthor

The interceptor does only have access to the options called by .request(). The request method has access to all variables in this (e.g Pool) since it is bound to this here:

letdispatch=this.dispatch.bind(this)

It could probably be solved in the same manner for the interceptor by changing:

dispatch=interceptor(dispatch)

Into:

dispatch=interceptor.bind(this)(dispatch)

or something like this (which is a bit like my original approach):

dispatch=interceptor(dispatch,this)

The last method has the benefit of allowing the interceptors to return arrow functions as well as ordinary functions.

@mcollina

Copy link
Copy Markdown
Member

THis now conflicts, can you rebase?

@luddd3
luddd3force-pushed the fix-pool-dns-interceptor branch from 43a4023 to da237d9CompareDecember 20, 2024 05:59

@ronagronag left a comment

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.

This is wrong... not sure when I have time to look into it. Christmas...

@metcoder95

metcoder95 commented Dec 20, 2024

Copy link
Copy Markdown
Member

Let me try to check it during the weekend; if there's a workaround, let's take it, otherwise this should be ok for now and we can revisit later so dns is unblocked

ronag

This comment was marked as resolved.

@ronagronag left a comment

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.

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

If anything maybe the dns interceptor should check for origin and throw an invalid arg error. Alternatively, if no origin is passed then the dns interceptor is a noop.

@ronag

Copy link
Copy Markdown
Member
diff --git a/lib/interceptor/dns.js b/lib/interceptor/dns.js
index c8d56c2c..c6a1a480 100644
--- a/lib/interceptor/dns.js
+++ b/lib/interceptor/dns.js
@@ -342,6 +342,10 @@ module.exports = interceptorOpts => {
return dispatch => {
return function dnsInterceptor (origDispatchOpts, handler) {
+ if (origDispatchOpts.origin == null) {
+ return dispatch(origDispatchOpts, handler)
+ }
+
const origin =

@mcollinamcollina left a comment

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.

lgtm

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

@ronag That was my assumption. Just to be clear, do you think it should be like this?

constclient=newPool('https://google.com',{connections: 10})awaitclient.request({origin: 'https://google.com',// origin must be included in each requestmethod: 'GET',path: '/'})

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

@ronag

ronag commented Dec 21, 2024

Copy link
Copy Markdown
Member

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

Possibly. Then let's just make the dns interceptor a noop when origin is missing as I suggested with the patch.

@metcoder95

Copy link
Copy Markdown
Member

I do agree that the documentation has some statements that are either not fully true or confusing for the reader that can lead to a wrong assumption; but that's at an overall take.

The last being said; I'm not 100% in sync with the statement that passing the origin should be mandated on every request call. I'm in sync when use through dynamic dispatchers like Agent or the BalancedPool, but not fully in sync for Client and Pool which are tight to a single origin.

For the latter use-cases, they should have the origin already stick to their state and do not ask the caller to pass it as its goal is to be tight to a single downstream.

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

Below is an example of how it works today when different origin is provided to Client during the constructor and request(). The caller might think that the request will be made to http://localhost:2000, which it won't. In this case a thrown error would be much clearer and avoid mistakes. It works similarly for Pool.

import{createServer}from'node:http'import{Client}from'undici'constserver=createServer()server.on('request',(req,res)=>{res.end('hello')})server.listen(0)constclient=newClient(`http://localhost:${server.address().port}`)constresponse=awaitclient.request({method: 'GET',origin: 'http://localhost:2000',path: '/'})console.log(awaitresponse.body.text())// will print 'hello'awaitserver.close()

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

I think that it should throw an error, since it should never be the case that origin is missing. Unless it can get origin directly from Client or Pool via one of the solutions I provided earlier.

@ronag

Copy link
Copy Markdown
Member

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

It should throw when a different origin is provided. Yes.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

It should throw when a different origin is provided. Yes.

I can understand from the viewpoint of backwards-compatibility, but are there other benefits for not throwing every time? I'm afraid that it encourages the wrong behavior to allow an origin, which isn't used,. I have however made a commit which does that.

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@ronag

  1. Why is it better to ignore requests without origin in the dns interceptor instead of throwing an error?
  2. Do you mean that I should remove all code I placed in Client and Pool, or just that I shouldn't modify the request options if origin was provided?

@ronag

ronag commented Dec 31, 2024

Copy link
Copy Markdown
Member

The only change here should be a condition in the dns interceptor and a test.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The only change here should be a condition in the dns interceptor and a test.

So basically remove all changes so far, apply the patch your provided earlier and then write a test?

@luddd3

Copy link
Copy Markdown
ContributorAuthor

IMHO that doesn't solve anything. I as a user would not expect the dns interceptor to be silently bypassed when origin wasn't provided again in my request.

Everything points to origin not being necessary with Client and Pool and it is also not possible to switch origin, so why not override request and make sure that the interceptors get it? Even the examples in the documentation https://undici.nodejs.org/#/docs/api/Client?id=example-client-connect-event shows that request can be called without providing origin again.

@metcoder95

Copy link
Copy Markdown
Member

Gentle ping

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Gentle ping

I don't know how to move forward since none of my proposed solutions have been accepted and I don't think the alternatives are any good. Do you have any ideas?

@metcoder95

Copy link
Copy Markdown
Member

cc: @ronag

@ronag

ronag commented Jun 2, 2025

Copy link
Copy Markdown
Member

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

I think it is wrong that:

  1. Client and Pool forces the user to pass origin for each request. It isn't documentet and wouldn't work for different origins. fix: dns interceptor with Pool #3957 (comment)
  2. the dns interceptor should be silently bypassed if a different origin is passed. I much rather it throws an error so that the user is informed that there is something wrong.

@marko1olomarko1olo mentioned this pull request Jun 7, 2026
3 tasks
@luddd3

Copy link
Copy Markdown
ContributorAuthor

Fixed with #5624

@luddd3luddd3 closed this Sep 2, 2026
Sign up for freeto 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.

4 participants

@luddd3@ronag@mcollina@metcoder95
, '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

fix: dns interceptor with Pool - #3957

Closed
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor
Closed

fix: dns interceptor with Pool#3957
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor

Conversation

@luddd3

Copy link
Copy Markdown
Contributor

This relates to...

Rationale

The DNS interceptor didn't work with Pool/Client since it didn't get the origin unless included with each request.

Changes

Features

Bug Fixes

  • fix dns interceptor with Client/Pool

Breaking Changes and Deprecations

Status

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

The origin should always be in origDispatchOpts. This seems to be a hack.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The origin should always be in origDispatchOpts. This seems to be a hack.

I kinda agree, but didn't find where else to put it?

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@luddd3

Copy link
Copy Markdown
ContributorAuthor

I made an alternative solution. It currently breaks a test for test/interceptors/retry.js and I'm unsure if it is an actual error or not? Should I just change the expected value?

@metcoder95metcoder95 left a comment

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.

Just adjust the retry tests and should be ok to go.

The change is kind of expected as the origin is being explicitly passed and is the first origin the client is facing

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@ronag

Copy link
Copy Markdown
Member

This still looks weird... where/how does the origin get lost?

@luddd3

luddd3 commented Dec 19, 2024

Copy link
Copy Markdown
ContributorAuthor

The interceptor does only have access to the options called by .request(). The request method has access to all variables in this (e.g Pool) since it is bound to this here:

letdispatch=this.dispatch.bind(this)

It could probably be solved in the same manner for the interceptor by changing:

dispatch=interceptor(dispatch)

Into:

dispatch=interceptor.bind(this)(dispatch)

or something like this (which is a bit like my original approach):

dispatch=interceptor(dispatch,this)

The last method has the benefit of allowing the interceptors to return arrow functions as well as ordinary functions.

@mcollina

Copy link
Copy Markdown
Member

THis now conflicts, can you rebase?

@luddd3
luddd3force-pushed the fix-pool-dns-interceptor branch from 43a4023 to da237d9CompareDecember 20, 2024 05:59

@ronagronag left a comment

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.

This is wrong... not sure when I have time to look into it. Christmas...

@metcoder95

metcoder95 commented Dec 20, 2024

Copy link
Copy Markdown
Member

Let me try to check it during the weekend; if there's a workaround, let's take it, otherwise this should be ok for now and we can revisit later so dns is unblocked

ronag

This comment was marked as resolved.

@ronagronag left a comment

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.

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

If anything maybe the dns interceptor should check for origin and throw an invalid arg error. Alternatively, if no origin is passed then the dns interceptor is a noop.

@ronag

Copy link
Copy Markdown
Member
diff --git a/lib/interceptor/dns.js b/lib/interceptor/dns.js
index c8d56c2c..c6a1a480 100644
--- a/lib/interceptor/dns.js
+++ b/lib/interceptor/dns.js
@@ -342,6 +342,10 @@ module.exports = interceptorOpts => {
return dispatch => {
return function dnsInterceptor (origDispatchOpts, handler) {
+ if (origDispatchOpts.origin == null) {
+ return dispatch(origDispatchOpts, handler)
+ }
+
const origin =

@mcollinamcollina left a comment

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.

lgtm

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

@ronag That was my assumption. Just to be clear, do you think it should be like this?

constclient=newPool('https://google.com',{connections: 10})awaitclient.request({origin: 'https://google.com',// origin must be included in each requestmethod: 'GET',path: '/'})

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

@ronag

ronag commented Dec 21, 2024

Copy link
Copy Markdown
Member

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

Possibly. Then let's just make the dns interceptor a noop when origin is missing as I suggested with the patch.

@metcoder95

Copy link
Copy Markdown
Member

I do agree that the documentation has some statements that are either not fully true or confusing for the reader that can lead to a wrong assumption; but that's at an overall take.

The last being said; I'm not 100% in sync with the statement that passing the origin should be mandated on every request call. I'm in sync when use through dynamic dispatchers like Agent or the BalancedPool, but not fully in sync for Client and Pool which are tight to a single origin.

For the latter use-cases, they should have the origin already stick to their state and do not ask the caller to pass it as its goal is to be tight to a single downstream.

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

Below is an example of how it works today when different origin is provided to Client during the constructor and request(). The caller might think that the request will be made to http://localhost:2000, which it won't. In this case a thrown error would be much clearer and avoid mistakes. It works similarly for Pool.

import{createServer}from'node:http'import{Client}from'undici'constserver=createServer()server.on('request',(req,res)=>{res.end('hello')})server.listen(0)constclient=newClient(`http://localhost:${server.address().port}`)constresponse=awaitclient.request({method: 'GET',origin: 'http://localhost:2000',path: '/'})console.log(awaitresponse.body.text())// will print 'hello'awaitserver.close()

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

I think that it should throw an error, since it should never be the case that origin is missing. Unless it can get origin directly from Client or Pool via one of the solutions I provided earlier.

@ronag

Copy link
Copy Markdown
Member

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

It should throw when a different origin is provided. Yes.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

It should throw when a different origin is provided. Yes.

I can understand from the viewpoint of backwards-compatibility, but are there other benefits for not throwing every time? I'm afraid that it encourages the wrong behavior to allow an origin, which isn't used,. I have however made a commit which does that.

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@ronag

  1. Why is it better to ignore requests without origin in the dns interceptor instead of throwing an error?
  2. Do you mean that I should remove all code I placed in Client and Pool, or just that I shouldn't modify the request options if origin was provided?

@ronag

ronag commented Dec 31, 2024

Copy link
Copy Markdown
Member

The only change here should be a condition in the dns interceptor and a test.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The only change here should be a condition in the dns interceptor and a test.

So basically remove all changes so far, apply the patch your provided earlier and then write a test?

@luddd3

Copy link
Copy Markdown
ContributorAuthor

IMHO that doesn't solve anything. I as a user would not expect the dns interceptor to be silently bypassed when origin wasn't provided again in my request.

Everything points to origin not being necessary with Client and Pool and it is also not possible to switch origin, so why not override request and make sure that the interceptors get it? Even the examples in the documentation https://undici.nodejs.org/#/docs/api/Client?id=example-client-connect-event shows that request can be called without providing origin again.

@metcoder95

Copy link
Copy Markdown
Member

Gentle ping

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Gentle ping

I don't know how to move forward since none of my proposed solutions have been accepted and I don't think the alternatives are any good. Do you have any ideas?

@metcoder95

Copy link
Copy Markdown
Member

cc: @ronag

@ronag

ronag commented Jun 2, 2025

Copy link
Copy Markdown
Member

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

I think it is wrong that:

  1. Client and Pool forces the user to pass origin for each request. It isn't documentet and wouldn't work for different origins. fix: dns interceptor with Pool #3957 (comment)
  2. the dns interceptor should be silently bypassed if a different origin is passed. I much rather it throws an error so that the user is informed that there is something wrong.

@marko1olomarko1olo mentioned this pull request Jun 7, 2026
3 tasks
@luddd3

Copy link
Copy Markdown
ContributorAuthor

Fixed with #5624

@luddd3luddd3 closed this Sep 2, 2026
Sign up for freeto 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.

4 participants

@luddd3@ronag@mcollina@metcoder95
, '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

fix: dns interceptor with Pool - #3957

Closed
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor
Closed

fix: dns interceptor with Pool#3957
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor

Conversation

@luddd3

Copy link
Copy Markdown
Contributor

This relates to...

Rationale

The DNS interceptor didn't work with Pool/Client since it didn't get the origin unless included with each request.

Changes

Features

Bug Fixes

  • fix dns interceptor with Client/Pool

Breaking Changes and Deprecations

Status

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

The origin should always be in origDispatchOpts. This seems to be a hack.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The origin should always be in origDispatchOpts. This seems to be a hack.

I kinda agree, but didn't find where else to put it?

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@luddd3

Copy link
Copy Markdown
ContributorAuthor

I made an alternative solution. It currently breaks a test for test/interceptors/retry.js and I'm unsure if it is an actual error or not? Should I just change the expected value?

@metcoder95metcoder95 left a comment

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.

Just adjust the retry tests and should be ok to go.

The change is kind of expected as the origin is being explicitly passed and is the first origin the client is facing

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@ronag

Copy link
Copy Markdown
Member

This still looks weird... where/how does the origin get lost?

@luddd3

luddd3 commented Dec 19, 2024

Copy link
Copy Markdown
ContributorAuthor

The interceptor does only have access to the options called by .request(). The request method has access to all variables in this (e.g Pool) since it is bound to this here:

letdispatch=this.dispatch.bind(this)

It could probably be solved in the same manner for the interceptor by changing:

dispatch=interceptor(dispatch)

Into:

dispatch=interceptor.bind(this)(dispatch)

or something like this (which is a bit like my original approach):

dispatch=interceptor(dispatch,this)

The last method has the benefit of allowing the interceptors to return arrow functions as well as ordinary functions.

@mcollina

Copy link
Copy Markdown
Member

THis now conflicts, can you rebase?

@luddd3
luddd3force-pushed the fix-pool-dns-interceptor branch from 43a4023 to da237d9CompareDecember 20, 2024 05:59

@ronagronag left a comment

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.

This is wrong... not sure when I have time to look into it. Christmas...

@metcoder95

metcoder95 commented Dec 20, 2024

Copy link
Copy Markdown
Member

Let me try to check it during the weekend; if there's a workaround, let's take it, otherwise this should be ok for now and we can revisit later so dns is unblocked

ronag

This comment was marked as resolved.

@ronagronag left a comment

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.

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

If anything maybe the dns interceptor should check for origin and throw an invalid arg error. Alternatively, if no origin is passed then the dns interceptor is a noop.

@ronag

Copy link
Copy Markdown
Member
diff --git a/lib/interceptor/dns.js b/lib/interceptor/dns.js
index c8d56c2c..c6a1a480 100644
--- a/lib/interceptor/dns.js
+++ b/lib/interceptor/dns.js
@@ -342,6 +342,10 @@ module.exports = interceptorOpts => {
return dispatch => {
return function dnsInterceptor (origDispatchOpts, handler) {
+ if (origDispatchOpts.origin == null) {
+ return dispatch(origDispatchOpts, handler)
+ }
+
const origin =

@mcollinamcollina left a comment

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.

lgtm

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

@ronag That was my assumption. Just to be clear, do you think it should be like this?

constclient=newPool('https://google.com',{connections: 10})awaitclient.request({origin: 'https://google.com',// origin must be included in each requestmethod: 'GET',path: '/'})

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

@ronag

ronag commented Dec 21, 2024

Copy link
Copy Markdown
Member

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

Possibly. Then let's just make the dns interceptor a noop when origin is missing as I suggested with the patch.

@metcoder95

Copy link
Copy Markdown
Member

I do agree that the documentation has some statements that are either not fully true or confusing for the reader that can lead to a wrong assumption; but that's at an overall take.

The last being said; I'm not 100% in sync with the statement that passing the origin should be mandated on every request call. I'm in sync when use through dynamic dispatchers like Agent or the BalancedPool, but not fully in sync for Client and Pool which are tight to a single origin.

For the latter use-cases, they should have the origin already stick to their state and do not ask the caller to pass it as its goal is to be tight to a single downstream.

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

Below is an example of how it works today when different origin is provided to Client during the constructor and request(). The caller might think that the request will be made to http://localhost:2000, which it won't. In this case a thrown error would be much clearer and avoid mistakes. It works similarly for Pool.

import{createServer}from'node:http'import{Client}from'undici'constserver=createServer()server.on('request',(req,res)=>{res.end('hello')})server.listen(0)constclient=newClient(`http://localhost:${server.address().port}`)constresponse=awaitclient.request({method: 'GET',origin: 'http://localhost:2000',path: '/'})console.log(awaitresponse.body.text())// will print 'hello'awaitserver.close()

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

I think that it should throw an error, since it should never be the case that origin is missing. Unless it can get origin directly from Client or Pool via one of the solutions I provided earlier.

@ronag

Copy link
Copy Markdown
Member

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

It should throw when a different origin is provided. Yes.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

It should throw when a different origin is provided. Yes.

I can understand from the viewpoint of backwards-compatibility, but are there other benefits for not throwing every time? I'm afraid that it encourages the wrong behavior to allow an origin, which isn't used,. I have however made a commit which does that.

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@ronag

  1. Why is it better to ignore requests without origin in the dns interceptor instead of throwing an error?
  2. Do you mean that I should remove all code I placed in Client and Pool, or just that I shouldn't modify the request options if origin was provided?

@ronag

ronag commented Dec 31, 2024

Copy link
Copy Markdown
Member

The only change here should be a condition in the dns interceptor and a test.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The only change here should be a condition in the dns interceptor and a test.

So basically remove all changes so far, apply the patch your provided earlier and then write a test?

@luddd3

Copy link
Copy Markdown
ContributorAuthor

IMHO that doesn't solve anything. I as a user would not expect the dns interceptor to be silently bypassed when origin wasn't provided again in my request.

Everything points to origin not being necessary with Client and Pool and it is also not possible to switch origin, so why not override request and make sure that the interceptors get it? Even the examples in the documentation https://undici.nodejs.org/#/docs/api/Client?id=example-client-connect-event shows that request can be called without providing origin again.

@metcoder95

Copy link
Copy Markdown
Member

Gentle ping

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Gentle ping

I don't know how to move forward since none of my proposed solutions have been accepted and I don't think the alternatives are any good. Do you have any ideas?

@metcoder95

Copy link
Copy Markdown
Member

cc: @ronag

@ronag

ronag commented Jun 2, 2025

Copy link
Copy Markdown
Member

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

I think it is wrong that:

  1. Client and Pool forces the user to pass origin for each request. It isn't documentet and wouldn't work for different origins. fix: dns interceptor with Pool #3957 (comment)
  2. the dns interceptor should be silently bypassed if a different origin is passed. I much rather it throws an error so that the user is informed that there is something wrong.

@marko1olomarko1olo mentioned this pull request Jun 7, 2026
3 tasks
@luddd3

Copy link
Copy Markdown
ContributorAuthor

Fixed with #5624

@luddd3luddd3 closed this Sep 2, 2026
Sign up for freeto 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.

4 participants

@luddd3@ronag@mcollina@metcoder95
, '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

fix: dns interceptor with Pool - #3957

Closed
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor
Closed

fix: dns interceptor with Pool#3957
luddd3 wants to merge 6 commits into
nodejs:mainfrom
luddd3:fix-pool-dns-interceptor

Conversation

@luddd3

Copy link
Copy Markdown
Contributor

This relates to...

Rationale

The DNS interceptor didn't work with Pool/Client since it didn't get the origin unless included with each request.

Changes

Features

Bug Fixes

  • fix dns interceptor with Client/Pool

Breaking Changes and Deprecations

Status

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

The origin should always be in origDispatchOpts. This seems to be a hack.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The origin should always be in origDispatchOpts. This seems to be a hack.

I kinda agree, but didn't find where else to put it?

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@luddd3

Copy link
Copy Markdown
ContributorAuthor

I made an alternative solution. It currently breaks a test for test/interceptors/retry.js and I'm unsure if it is an actual error or not? Should I just change the expected value?

@metcoder95metcoder95 left a comment

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.

Just adjust the retry tests and should be ok to go.

The change is kind of expected as the origin is being explicitly passed and is the first origin the client is facing

Comment threadlib/interceptor/dns.js Outdated
Comment threadlib/interceptor/dns.js Outdated
@ronag

Copy link
Copy Markdown
Member

This still looks weird... where/how does the origin get lost?

@luddd3

luddd3 commented Dec 19, 2024

Copy link
Copy Markdown
ContributorAuthor

The interceptor does only have access to the options called by .request(). The request method has access to all variables in this (e.g Pool) since it is bound to this here:

letdispatch=this.dispatch.bind(this)

It could probably be solved in the same manner for the interceptor by changing:

dispatch=interceptor(dispatch)

Into:

dispatch=interceptor.bind(this)(dispatch)

or something like this (which is a bit like my original approach):

dispatch=interceptor(dispatch,this)

The last method has the benefit of allowing the interceptors to return arrow functions as well as ordinary functions.

@mcollina

Copy link
Copy Markdown
Member

THis now conflicts, can you rebase?

@luddd3
luddd3force-pushed the fix-pool-dns-interceptor branch from 43a4023 to da237d9CompareDecember 20, 2024 05:59

@ronagronag left a comment

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.

This is wrong... not sure when I have time to look into it. Christmas...

@metcoder95

metcoder95 commented Dec 20, 2024

Copy link
Copy Markdown
Member

Let me try to check it during the weekend; if there's a workaround, let's take it, otherwise this should be ok for now and we can revisit later so dns is unblocked

ronag

This comment was marked as resolved.

@ronagronag left a comment

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.

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

If anything maybe the dns interceptor should check for origin and throw an invalid arg error. Alternatively, if no origin is passed then the dns interceptor is a noop.

@ronag

Copy link
Copy Markdown
Member
diff --git a/lib/interceptor/dns.js b/lib/interceptor/dns.js
index c8d56c2c..c6a1a480 100644
--- a/lib/interceptor/dns.js
+++ b/lib/interceptor/dns.js
@@ -342,6 +342,10 @@ module.exports = interceptorOpts => {
return dispatch => {
return function dnsInterceptor (origDispatchOpts, handler) {
+ if (origDispatchOpts.origin == null) {
+ return dispatch(origDispatchOpts, handler)
+ }
+
const origin =

@mcollinamcollina left a comment

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.

lgtm

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The problem here is the assumption that you don't have to pass origin in the request options if used with Client or Pool, which IMHO is incorrect and it's working as intended.

@ronag That was my assumption. Just to be clear, do you think it should be like this?

constclient=newPool('https://google.com',{connections: 10})awaitclient.request({origin: 'https://google.com',// origin must be included in each requestmethod: 'GET',path: '/'})

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

@ronag

ronag commented Dec 21, 2024

Copy link
Copy Markdown
Member

If that is the case, then I think there are a lot of inconsistencies and contradictions in both the code and documentation.

Possibly. Then let's just make the dns interceptor a noop when origin is missing as I suggested with the patch.

@metcoder95

Copy link
Copy Markdown
Member

I do agree that the documentation has some statements that are either not fully true or confusing for the reader that can lead to a wrong assumption; but that's at an overall take.

The last being said; I'm not 100% in sync with the statement that passing the origin should be mandated on every request call. I'm in sync when use through dynamic dispatchers like Agent or the BalancedPool, but not fully in sync for Client and Pool which are tight to a single origin.

For the latter use-cases, they should have the origin already stick to their state and do not ask the caller to pass it as its goal is to be tight to a single downstream.

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

Below is an example of how it works today when different origin is provided to Client during the constructor and request(). The caller might think that the request will be made to http://localhost:2000, which it won't. In this case a thrown error would be much clearer and avoid mistakes. It works similarly for Pool.

import{createServer}from'node:http'import{Client}from'undici'constserver=createServer()server.on('request',(req,res)=>{res.end('hello')})server.listen(0)constclient=newClient(`http://localhost:${server.address().port}`)constresponse=awaitclient.request({method: 'GET',origin: 'http://localhost:2000',path: '/'})console.log(awaitresponse.body.text())// will print 'hello'awaitserver.close()

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm ok with the dns interceptor doing a noop if no origin is passed or if it is already an IP address; but the fact that Pool and Client does not forward the origin seems a bit of miss alignment with its purpose

I think that it should throw an error, since it should never be the case that origin is missing. Unless it can get origin directly from Client or Pool via one of the solutions I provided earlier.

@ronag

Copy link
Copy Markdown
Member

I think that Client and Pool actually should throw an error when origin is provided to request(), so that the caller notices that it is wrong.

It should throw when a different origin is provided. Yes.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

It should throw when a different origin is provided. Yes.

I can understand from the viewpoint of backwards-compatibility, but are there other benefits for not throwing every time? I'm afraid that it encourages the wrong behavior to allow an origin, which isn't used,. I have however made a commit which does that.

@mcollinamcollina left a comment

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.

lgtm

@ronagronag left a comment

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.

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Still blocking. Please don't override request and instead ignore requests without origin in the dna interceptor

@ronag

  1. Why is it better to ignore requests without origin in the dns interceptor instead of throwing an error?
  2. Do you mean that I should remove all code I placed in Client and Pool, or just that I shouldn't modify the request options if origin was provided?

@ronag

ronag commented Dec 31, 2024

Copy link
Copy Markdown
Member

The only change here should be a condition in the dns interceptor and a test.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

The only change here should be a condition in the dns interceptor and a test.

So basically remove all changes so far, apply the patch your provided earlier and then write a test?

@luddd3

Copy link
Copy Markdown
ContributorAuthor

IMHO that doesn't solve anything. I as a user would not expect the dns interceptor to be silently bypassed when origin wasn't provided again in my request.

Everything points to origin not being necessary with Client and Pool and it is also not possible to switch origin, so why not override request and make sure that the interceptors get it? Even the examples in the documentation https://undici.nodejs.org/#/docs/api/Client?id=example-client-connect-event shows that request can be called without providing origin again.

@metcoder95

Copy link
Copy Markdown
Member

Gentle ping

@luddd3

Copy link
Copy Markdown
ContributorAuthor

Gentle ping

I don't know how to move forward since none of my proposed solutions have been accepted and I don't think the alternatives are any good. Do you have any ideas?

@metcoder95

Copy link
Copy Markdown
Member

cc: @ronag

@ronag

ronag commented Jun 2, 2025

Copy link
Copy Markdown
Member

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

@luddd3

Copy link
Copy Markdown
ContributorAuthor

I'm not sure what's wrong with my proposal?

Expect origin to be passed to request, if it is passed, make sure it's same as the Pool/Client and if not passed then it's a noop for the dns interceptor.

I think it is wrong that:

  1. Client and Pool forces the user to pass origin for each request. It isn't documentet and wouldn't work for different origins. fix: dns interceptor with Pool #3957 (comment)
  2. the dns interceptor should be silently bypassed if a different origin is passed. I much rather it throws an error so that the user is informed that there is something wrong.

@marko1olomarko1olo mentioned this pull request Jun 7, 2026
3 tasks
@luddd3

Copy link
Copy Markdown
ContributorAuthor

Fixed with #5624

@luddd3luddd3 closed this Sep 2, 2026
Sign up for freeto 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.

4 participants

@luddd3@ronag@mcollina@metcoder95