mirror of
https://github.com/MichMich/MagicMirror.git
synced 2026-04-21 21:40:55 +00:00
fix(http_fetcher): use undici.fetch when dispatcher is present (#4097)
### What's the problem? The `selfSignedCert` option passes an undici `Agent` as `dispatcher` to `fetch()`. But Node's built-in `fetch()` and undici@8's `Agent` use different internal handler APIs - passing them together throws: ``` invalid onRequestStart method ``` ### What's the fix? When `selfSignedCert` is enabled (i.e. a `dispatcher` is set), use undici's own `fetch()` instead of the global one. For all other requests, keep using `globalThis.fetch`. ```js const fetchFn = requestOptions.dispatcher ? undiciFetch : globalThis.fetch; ``` ### Why not just always use undici's fetch? That would fix the crash - but it would break some tests. MSW (Mock Service Worker), which is used in our test suite to intercept HTTP requests, only hooks into `globalThis.fetch`. Undici's fetch bypasses those interceptors entirely, so tests would start making real network requests instead of getting the mocked responses. We could rewrite all tests to use undici-compatible mocking instead - but that would be a massive change for no real benefit. ---- Fixes #4093
This commit is contained in:
committed by
GitHub
parent
d8c29d5ec3
commit
2e97e29ab5
@@ -1,5 +1,5 @@
|
||||
const { EventEmitter } = require("node:events");
|
||||
const { Agent } = require("undici");
|
||||
const { fetch: undiciFetch, Agent } = require("undici");
|
||||
const Log = require("logger");
|
||||
const { getUserAgent } = require("#server_functions");
|
||||
|
||||
@@ -263,8 +263,13 @@ class HTTPFetcher extends EventEmitter {
|
||||
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
||||
|
||||
try {
|
||||
const response = await fetch(this.url, {
|
||||
...this.getRequestOptions(),
|
||||
const requestOptions = this.getRequestOptions();
|
||||
// Use undici.fetch when a custom dispatcher is present (e.g. selfSignedCert),
|
||||
// because Node's global fetch and npm undici@8 Agents are incompatible.
|
||||
// For regular requests, use globalThis.fetch so MSW and other interceptors work.
|
||||
const fetchFn = requestOptions.dispatcher ? undiciFetch : globalThis.fetch;
|
||||
const response = await fetchFn(this.url, {
|
||||
...requestOptions,
|
||||
signal: controller.signal
|
||||
});
|
||||
|
||||
|
||||
@@ -440,3 +440,32 @@ describe("fetch() method", () => {
|
||||
expect(errorInfo.errorType).toBe("NETWORK_ERROR");
|
||||
});
|
||||
});
|
||||
|
||||
describe("selfSignedCert dispatcher", () => {
|
||||
const { Agent } = require("undici");
|
||||
|
||||
it("should set rejectUnauthorized=false when selfSignedCert is true", () => {
|
||||
fetcher = new HTTPFetcher(TEST_URL, {
|
||||
reloadInterval: 60000,
|
||||
selfSignedCert: true
|
||||
});
|
||||
|
||||
const options = fetcher.getRequestOptions();
|
||||
|
||||
expect(options.dispatcher).toBeInstanceOf(Agent);
|
||||
const agentOptionsSymbol = Object.getOwnPropertySymbols(options.dispatcher).find((s) => s.description === "options");
|
||||
const dispatcherOptions = options.dispatcher[agentOptionsSymbol];
|
||||
expect(dispatcherOptions.connect.rejectUnauthorized).toBe(false);
|
||||
});
|
||||
|
||||
it("should not set a dispatcher when selfSignedCert is false", () => {
|
||||
fetcher = new HTTPFetcher(TEST_URL, {
|
||||
reloadInterval: 60000,
|
||||
selfSignedCert: false
|
||||
});
|
||||
|
||||
const options = fetcher.getRequestOptions();
|
||||
|
||||
expect(options.dispatcher).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user