diff --git a/lib/web/cache/cache.js b/lib/web/cache/cache.js index acbd6c7d0f7..80d9e2f15f3 100644 --- a/lib/web/cache/cache.js +++ b/lib/web/cache/cache.js @@ -10,7 +10,6 @@ const { kState } = require('../fetch/symbols') const { fetching } = require('../fetch/index') const { urlIsHttpHttpsScheme, createDeferredPromise, readAllBytes } = require('../fetch/util') const assert = require('node:assert') -const { getGlobalDispatcher } = require('../../global') /** * @see https://w3c.github.io/ServiceWorker/#dfn-cache-batch-operation @@ -150,7 +149,6 @@ class Cache { // 5.7 fetchControllers.push(fetching({ request: r, - dispatcher: getGlobalDispatcher(), processResponse (response) { // 1. if (response.type === 'error' || response.status === 206 || response.status < 200 || response.status > 299) { diff --git a/lib/web/eventsource/eventsource.js b/lib/web/eventsource/eventsource.js index ad6ea26dcd1..6b34976e31b 100644 --- a/lib/web/eventsource/eventsource.js +++ b/lib/web/eventsource/eventsource.js @@ -9,7 +9,6 @@ const { EventSourceStream } = require('./eventsource-stream') const { parseMIMEType } = require('../fetch/dataURL') const { MessageEvent } = require('../websocket/events') const { isNetworkError } = require('../fetch/response') -const { getGlobalDispatcher } = require('../../global') const { delay } = require('./util') let experimentalWarned = false @@ -316,10 +315,7 @@ class EventSource extends EventTarget { }) } - this.#controller = fetching({ - ...fetchParam, - dispatcher: getGlobalDispatcher() - }) + this.#controller = fetching(fetchParam) } /** diff --git a/lib/web/fetch/index.js b/lib/web/fetch/index.js index df2f442c647..e9733362e95 100644 --- a/lib/web/fetch/index.js +++ b/lib/web/fetch/index.js @@ -47,7 +47,7 @@ const { createInflate, extractMimeType } = require('./util') -const { kState } = require('./symbols') +const { kState, kDispatcher } = require('./symbols') const assert = require('node:assert') const { safelyExtractBody, extractBody } = require('./body') const { @@ -239,7 +239,7 @@ function fetch (input, init = undefined) { request, processResponseEndOfBody: handleFetchDone, processResponse, - dispatcher: init?.dispatcher ?? getGlobalDispatcher() // undici + dispatcher: requestObject[kDispatcher] // undici }) // 14. Return p. @@ -361,7 +361,7 @@ function fetching ({ processResponseEndOfBody, processResponseConsumeBody, useParallelQueue = false, - dispatcher // undici + dispatcher = getGlobalDispatcher() // undici }) { // Ensure that the dispatcher is set accordingly assert(dispatcher) diff --git a/lib/web/fetch/request.js b/lib/web/fetch/request.js index ef2f3ff0b14..f8759626a1e 100644 --- a/lib/web/fetch/request.js +++ b/lib/web/fetch/request.js @@ -24,7 +24,7 @@ const { requestDuplex } = require('./constants') const { kEnumerableProperty } = util -const { kHeaders, kSignal, kState, kGuard, kRealm } = require('./symbols') +const { kHeaders, kSignal, kState, kGuard, kRealm, kDispatcher } = require('./symbols') const { webidl } = require('./webidl') const { getGlobalOrigin } = require('./global') const { URLSerializer } = require('./dataURL') @@ -78,6 +78,8 @@ class Request { // 5. If input is a string, then: if (typeof input === 'string') { + this[kDispatcher] = init.dispatcher + // 1. Let parsedURL be the result of parsing input with baseURL. // 2. If parsedURL is failure, then throw a TypeError. let parsedURL @@ -101,6 +103,8 @@ class Request { // 5. Set fallbackMode to "cors". fallbackMode = 'cors' } else { + this[kDispatcher] = input[kDispatcher] + // 6. Otherwise: // 7. Assert: input is a Request object. @@ -979,6 +983,10 @@ webidl.converters.RequestInit = webidl.dictionaryConverter([ key: 'duplex', converter: webidl.converters.DOMString, allowedValues: requestDuplex + }, + { + key: 'dispatcher', // undici specific option + converter: webidl.converters.any } ]) diff --git a/lib/web/fetch/symbols.js b/lib/web/fetch/symbols.js index 0b947d55bad..e98e78080b6 100644 --- a/lib/web/fetch/symbols.js +++ b/lib/web/fetch/symbols.js @@ -6,5 +6,6 @@ module.exports = { kSignal: Symbol('signal'), kState: Symbol('state'), kGuard: Symbol('guard'), - kRealm: Symbol('realm') + kRealm: Symbol('realm'), + kDispatcher: Symbol('dispatcher') } diff --git a/lib/web/fetch/webidl.js b/lib/web/fetch/webidl.js index da5df4a362f..41f5813db69 100644 --- a/lib/web/fetch/webidl.js +++ b/lib/web/fetch/webidl.js @@ -3,7 +3,7 @@ const { types } = require('node:util') const { toUSVString } = require('../../core/util') -/** @type {import('../../types/webidl').Webidl} */ +/** @type {import('../../../types/webidl').Webidl} */ const webidl = {} webidl.converters = {} webidl.util = {} diff --git a/lib/web/websocket/connection.js b/lib/web/websocket/connection.js index 33905404833..8f12933c959 100644 --- a/lib/web/websocket/connection.js +++ b/lib/web/websocket/connection.js @@ -13,7 +13,6 @@ const { CloseEvent } = require('./events') const { makeRequest } = require('../fetch/request') const { fetching } = require('../fetch/index') const { Headers } = require('../fetch/headers') -const { getGlobalDispatcher } = require('../../global') const { kHeadersList } = require('../../core/symbols') /** @type {import('crypto')} */ @@ -100,7 +99,7 @@ function establishWebSocketConnection (url, protocols, ws, onEstablish, options) const controller = fetching({ request, useParallelQueue: true, - dispatcher: options.dispatcher ?? getGlobalDispatcher(), + dispatcher: options.dispatcher, processResponse (response) { // 1. If response is a network error or its status is not 101, // fail the WebSocket connection. diff --git a/test/fetch/issue-2828.js b/test/fetch/issue-2828.js new file mode 100644 index 00000000000..08e8ea714ca --- /dev/null +++ b/test/fetch/issue-2828.js @@ -0,0 +1,32 @@ +'use strict' + +const { once } = require('node:events') +const { createServer } = require('node:http') +const { test } = require('node:test') +const { Agent, Request, fetch } = require('../..') +const { tspl } = require('@matteo.collina/tspl') + +test('issue #2828, dispatcher is allowed in RequestInit options', async (t) => { + const { deepStrictEqual } = tspl(t, { plan: 1 }) + + class CustomAgent extends Agent { + dispatch (options, handler) { + options.headers['x-my-header'] = 'hello' + return super.dispatch(...arguments) + } + } + + const server = createServer((req, res) => { + deepStrictEqual(req.headers['x-my-header'], 'hello') + res.end() + }).listen(0) + + t.after(server.close.bind(server)) + await once(server, 'listening') + + const request = new Request(`http://localhost:${server.address().port}`, { + dispatcher: new CustomAgent() + }) + + await fetch(request) +})