-
-
Notifications
You must be signed in to change notification settings - Fork 695
Open
Labels
bugSomething isn't workingSomething isn't working
Description
Bug Description
When attempting to write a custom dispatcher according to the documentation, it is natural to inherit from Dispatcher, or maybe DispatcherBase. Then one implements one's dispatch() method, and hopes that other methods like request() will work for free.
This is not the case, because request() only works if one uses the old, undocumented handlers API.
Reproducible By
"use strict";
const { Dispatcher } = require("undici");
const DispatcherBase = require("undici/lib/dispatcher/dispatcher-base");
class OldAPIDispatcher extends Dispatcher {
dispatch(opts, handler) {
console.log("Available methods:", Object.getOwnPropertyNames(Object.getPrototypeOf(handler)));
handler.onConnect(() => {});
handler.onHeaders(200, { "content-type": "text/plain" }, () => {}, "OK");
handler.onData(Buffer.from("Hello, world!"));
handler.onComplete({});
return true;
}
}
class NewAPIDispatcher extends Dispatcher {
dispatch(opts, handler) {
console.log("onResponseStart exists:", !!handler.onResponseStart);
handler.onRequestStart?.(null, {});
handler.onResponseStart?.(null, 200, { "content-type": "text/plain" }, "OK");
handler.onResponseData?.(null, Buffer.from("Hello, world!"));
handler.onResponseEnd?.(null, {});
return true;
}
}
class NewAPIDispatcherBase extends DispatcherBase {
dispatch(opts, handler) {
console.log("onResponseStart exists:", !!handler.onResponseStart);
handler.onRequestStart?.(null, {});
handler.onResponseStart?.(null, 200, { "content-type": "text/plain" }, "OK");
handler.onResponseData?.(null, Buffer.from("Hello, world!"));
handler.onResponseEnd?.(null, {});
return true;
}
}
async function test(name, dispatcher) {
console.log(`\n${name}:`);
const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("Timed out")), 500));
try {
const res = await Promise.race([
dispatcher.request({ origin: "http://example.com", path: "/", method: "GET" }),
timeout
]);
console.log(" Success:", await res.body.text());
} catch (e) {
console.log(" ", e.message);
}
}
(async () => {
await test("Old API (Dispatcher)", new OldAPIDispatcher());
await test("New API (Dispatcher)", new NewAPIDispatcher());
await test("New API (DispatcherBase)", new NewAPIDispatcherBase());
})();Expected Behavior
Available methods lists the new, documented handler API (onRequestStart, onRequestUpgrade, etc.)
At least the New API (DispatcherBase) case works, and ideally also the New API (Dispatcher) case.
Logs & Screenshots
Old API (Dispatcher):
Available methods: [
'constructor',
'onConnect',
'onHeaders',
'onData',
'onComplete',
'onError'
]
Success: Hello, world!
New API (Dispatcher):
onResponseStart exists: false
Timed out
New API (DispatcherBase):
onResponseStart exists: false
Timed out
Environment
- Node v25.3.0
- undici 7.19.2
Additional context
Somewhat related to #4771.
In #4753 (comment) I was told not to rely on the old handler API existing, so I'm unsure how to proceed here.
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working