Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 25 additions & 16 deletions deps/undici/src/lib/global.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const { InvalidArgumentError } = require('./core/errors')
const Agent = require('./dispatcher/agent')
const Dispatcher1Wrapper = require('./dispatcher/dispatcher1-wrapper')

// Fallback storage used when globalThis is not extensible (e.g. Object.freeze(globalThis)).
let fallbackDispatcher

if (getGlobalDispatcher() === undefined) {
setGlobalDispatcher(new Agent())
}
Expand All @@ -17,25 +20,31 @@ function setGlobalDispatcher (agent) {
throw new InvalidArgumentError('Argument agent must implement Agent')
}

Object.defineProperty(globalThis, globalDispatcher, {
value: agent,
writable: true,
enumerable: false,
configurable: false
})

const legacyAgent = agent instanceof Dispatcher1Wrapper ? agent : new Dispatcher1Wrapper(agent)

Object.defineProperty(globalThis, legacyGlobalDispatcher, {
value: legacyAgent,
writable: true,
enumerable: false,
configurable: false
})
try {
Object.defineProperty(globalThis, globalDispatcher, {
value: agent,
writable: true,
enumerable: false,
configurable: false
})

const legacyAgent = agent instanceof Dispatcher1Wrapper ? agent : new Dispatcher1Wrapper(agent)

Object.defineProperty(globalThis, legacyGlobalDispatcher, {
value: legacyAgent,
writable: true,
enumerable: false,
configurable: false
})
} catch {
// globalThis is not extensible (e.g. frozen via Object.freeze(globalThis)).
// Fall back to module-level storage so that fetch, WebSocket, etc. still work.
fallbackDispatcher = agent
}
}

function getGlobalDispatcher () {
return globalThis[globalDispatcher]
return globalThis[globalDispatcher] ?? fallbackDispatcher
}

// These are the globals that can be installed by undici.install().
Expand Down
33 changes: 19 additions & 14 deletions deps/undici/undici.js
Original file line number Diff line number Diff line change
Expand Up @@ -10470,30 +10470,35 @@ var require_global2 = __commonJS({
var { InvalidArgumentError } = require_errors();
var Agent = require_agent();
var Dispatcher1Wrapper = require_dispatcher1_wrapper();
var fallbackDispatcher;
if (getGlobalDispatcher2() === void 0) {
setGlobalDispatcher2(new Agent());
}
function setGlobalDispatcher2(agent) {
if (!agent || typeof agent.dispatch !== "function") {
throw new InvalidArgumentError("Argument agent must implement Agent");
}
Object.defineProperty(globalThis, globalDispatcher, {
value: agent,
writable: true,
enumerable: false,
configurable: false
});
const legacyAgent = agent instanceof Dispatcher1Wrapper ? agent : new Dispatcher1Wrapper(agent);
Object.defineProperty(globalThis, legacyGlobalDispatcher, {
value: legacyAgent,
writable: true,
enumerable: false,
configurable: false
});
try {
Object.defineProperty(globalThis, globalDispatcher, {
value: agent,
writable: true,
enumerable: false,
configurable: false
});
const legacyAgent = agent instanceof Dispatcher1Wrapper ? agent : new Dispatcher1Wrapper(agent);
Object.defineProperty(globalThis, legacyGlobalDispatcher, {
value: legacyAgent,
writable: true,
enumerable: false,
configurable: false
});
} catch {
fallbackDispatcher = agent;
}
}
__name(setGlobalDispatcher2, "setGlobalDispatcher");
function getGlobalDispatcher2() {
return globalThis[globalDispatcher];
return globalThis[globalDispatcher] ?? fallbackDispatcher;
}
__name(getGlobalDispatcher2, "getGlobalDispatcher");
var installedExports = (
Expand Down
26 changes: 26 additions & 0 deletions test/parallel/test-undici-frozen-globalthis.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict';

// Test that globals provided by undici (fetch, WebSocket, etc.) do not throw
// when globalThis has been frozen via Object.freeze(globalThis).
//
// Refs: https://github.com/nodejs/node/issues/46788

require('../common');

// Freeze globalThis before any undici globals are accessed.
Object.freeze(globalThis);

const assert = require('node:assert');

// Accessing these globals triggers undici's lazy initialisation, which calls
// setGlobalDispatcher internally. With a frozen globalThis, the original code
// threw: "TypeError: Cannot define property Symbol(undici.globalDispatcher.2),
// object is not extensible". The fix uses a module-level fallback instead.
assert.strictEqual(typeof fetch, 'function', 'fetch must be a function');
assert.strictEqual(typeof WebSocket, 'function', 'WebSocket must be a function');
assert.strictEqual(typeof Response, 'function', 'Response must be a function');
assert.strictEqual(typeof Request, 'function', 'Request must be a function');
assert.strictEqual(typeof Headers, 'function', 'Headers must be a function');
assert.strictEqual(typeof FormData, 'function', 'FormData must be a function');

console.log('All undici globals accessible after Object.freeze(globalThis)');
Loading