Skip to content

Commit 0143e1b

Browse files
authored
fix(1270): throw descriptive error when opts dispatcher passed instance methods (#5007)
Signed-off-by: Roberto Bianchi <roberto.bianchi@spendesk.com>
1 parent 51a27b7 commit 0143e1b

3 files changed

Lines changed: 116 additions & 2 deletions

File tree

index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,14 @@ function makeDispatcher (fn) {
105105
url = util.parseURL(url)
106106
}
107107

108-
const { agent, dispatcher = getGlobalDispatcher() } = opts
108+
const { agent, dispatcher = getGlobalDispatcher(), ...restOpts } = opts
109109

110110
if (agent) {
111111
throw new InvalidArgumentError('unsupported opts.agent. Did you mean opts.client?')
112112
}
113113

114114
return fn.call(dispatcher, {
115-
...opts,
115+
...restOpts,
116116
origin: url.origin,
117117
path: url.search ? `${url.pathname}${url.search}` : url.pathname,
118118
method: opts.method || (opts.body ? 'PUT' : 'GET')

lib/dispatcher/dispatcher-base.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ class DispatcherBase extends Dispatcher {
138138
throw new InvalidArgumentError('opts must be an object.')
139139
}
140140

141+
if (opts.dispatcher) {
142+
throw new InvalidArgumentError('opts.dispatcher is not supported by instance methods. Pass opts.dispatcher to the top-level undici functions or call the dispatcher instance method directly.')
143+
}
144+
141145
if (this[kDestroyed] || this[kOnDestroyed]) {
142146
throw new ClientDestroyedError()
143147
}

test/issue-1270.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
'use strict'
2+
3+
const { tspl } = require('@matteo.collina/tspl')
4+
const { test, after } = require('node:test')
5+
const { createServer } = require('node:http')
6+
const { once } = require('node:events')
7+
const {
8+
Pool,
9+
Client,
10+
Agent,
11+
request,
12+
errors
13+
} = require('..')
14+
15+
// https://github.com/nodejs/undici/issues/1270
16+
17+
test('Pool.request() throws if opts.dispatcher is provided', async (t) => {
18+
t = tspl(t, { plan: 2 })
19+
20+
const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
21+
res.end('ok')
22+
})
23+
24+
server.listen(0)
25+
await once(server, 'listening')
26+
27+
after(async () => {
28+
server.close()
29+
await once(server, 'close')
30+
})
31+
32+
const pool = new Pool(`http://localhost:${server.address().port}`)
33+
after(() => pool.close())
34+
35+
const otherAgent = new Agent()
36+
after(() => otherAgent.close())
37+
38+
try {
39+
await pool.request({
40+
path: '/',
41+
method: 'GET',
42+
dispatcher: otherAgent
43+
})
44+
t.fail('should have thrown')
45+
} catch (err) {
46+
t.ok(err instanceof errors.InvalidArgumentError)
47+
t.ok(err.message.includes('opts.dispatcher is not supported'))
48+
}
49+
})
50+
51+
test('Client.request() throws if opts.dispatcher is provided', async (t) => {
52+
t = tspl(t, { plan: 2 })
53+
54+
const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
55+
res.end('ok')
56+
})
57+
58+
server.listen(0)
59+
await once(server, 'listening')
60+
61+
after(async () => {
62+
server.close()
63+
await once(server, 'close')
64+
})
65+
66+
const client = new Client(`http://localhost:${server.address().port}`)
67+
after(() => client.close())
68+
69+
const otherPool = new Pool(`http://localhost:${server.address().port}`)
70+
after(() => otherPool.close())
71+
72+
try {
73+
await client.request({
74+
path: '/',
75+
method: 'GET',
76+
dispatcher: otherPool
77+
})
78+
t.fail('should have thrown')
79+
} catch (err) {
80+
t.ok(err instanceof errors.InvalidArgumentError)
81+
t.ok(err.message.includes('opts.dispatcher is not supported'))
82+
}
83+
})
84+
85+
test('Top-level request() still works with opts.dispatcher', async (t) => {
86+
t = tspl(t, { plan: 2 })
87+
88+
const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
89+
res.end('hello')
90+
})
91+
92+
server.listen(0)
93+
await once(server, 'listening')
94+
95+
const pool = new Pool(`http://localhost:${server.address().port}`)
96+
97+
after(async () => {
98+
await pool.close()
99+
server.close()
100+
await once(server, 'close')
101+
})
102+
103+
const { statusCode, body } = await request(`http://localhost:${server.address().port}`, {
104+
method: 'GET',
105+
dispatcher: pool
106+
})
107+
108+
t.equal(statusCode, 200)
109+
t.equal(await body.text(), 'hello')
110+
})

0 commit comments

Comments
 (0)