import dns from 'node:dns'
import { fetch, Agent, type RequestInit as UndiciRequestInit, ProxyAgent, type Dispatcher, getGlobalDispatcher } from 'undici'
import { environment } from '../environment.js'
import { WebDriverRequest } from './request.js'
import type { RequestOptions } from './types.js'
dns.setDefaultResultOrder('ipv4first')
export const SESSION_DISPATCHERS: Map<string, Dispatcher> = new Map()
* Node implementation of WebDriverRequest using undici fetch
*/
export class FetchRequest extends WebDriverRequest {
async fetch (url: URL, opts: RequestInit) {
const response = await fetch(url, opts as UndiciRequestInit) as unknown as Promise<Response>
if (opts.method === 'DELETE') {
const match = url.pathname.match(/\/session\/([^/]+)$/)
const sessionId = match?.[1]
if (sessionId) {
this.cleanupSessionDispatcher(sessionId)
}
}
return response
}
private getDispatcher(url: URL, options: RequestOptions, sessionId?: string): Dispatcher {
if (sessionId && SESSION_DISPATCHERS.has(sessionId)) {
return SESSION_DISPATCHERS.get(sessionId)!
}
* First check if a global dispatcher has been set (e.g. via setGlobalDispatcher)
*/
try {
const globalDispatcher = getGlobalDispatcher()
if (globalDispatcher && (
globalDispatcher.constructor.name === 'ProxyAgent' ||
'proxy' in globalDispatcher ||
(globalDispatcher.constructor.name !== 'Agent' && globalDispatcher.constructor.name !== 'MockAgent')
)) {
return globalDispatcher
}
} catch {
}
* Fall back to creating a dispatcher based on environment variables
* Use a proxy agent if we have a proxy url set
*/
const { PROXY_URL, NO_PROXY } = environment.value.variables
const shouldUseProxy =
PROXY_URL && !NO_PROXY?.some((str) => url.hostname.endsWith(str))
const dispatcher = shouldUseProxy ? new ProxyAgent({
uri: PROXY_URL,
connectTimeout: options.connectionRetryTimeout,
headersTimeout: options.connectionRetryTimeout,
bodyTimeout: options.connectionRetryTimeout,
}) :
new Agent({
connectTimeout: options.connectionRetryTimeout,
headersTimeout: options.connectionRetryTimeout,
bodyTimeout: options.connectionRetryTimeout,
})
if (sessionId){
SESSION_DISPATCHERS.set(sessionId, dispatcher)
}
return dispatcher
}
private cleanupSessionDispatcher(sessionId: string) {
const dispatcher = SESSION_DISPATCHERS.get(sessionId)
if (dispatcher && typeof dispatcher.close === 'function') {
dispatcher.close()
}
SESSION_DISPATCHERS.delete(sessionId)
}
async createOptions (options: RequestOptions, sessionId?: string, isBrowser: boolean = false) {
const { url, requestOptions } = await super.createOptions(options, sessionId, isBrowser)
;(requestOptions as UndiciRequestInit).dispatcher = this.getDispatcher(url, options, sessionId)
return { url, requestOptions }
}
}