Spaces:
Runtime error
Runtime error
const { InvalidArgumentError } = require('./core/errors') | |
const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = require('./core/symbols') | |
const DispatcherBase = require('./dispatcher-base') | |
const Pool = require('./pool') | |
const Client = require('./client') | |
const util = require('./core/util') | |
const createRedirectInterceptor = require('./interceptor/redirectInterceptor') | |
const { WeakRef, FinalizationRegistry } = require('./compat/dispatcher-weakref')() | |
const kOnConnect = Symbol('onConnect') | |
const kOnDisconnect = Symbol('onDisconnect') | |
const kOnConnectionError = Symbol('onConnectionError') | |
const kMaxRedirections = Symbol('maxRedirections') | |
const kOnDrain = Symbol('onDrain') | |
const kFactory = Symbol('factory') | |
const kFinalizer = Symbol('finalizer') | |
const kOptions = Symbol('options') | |
function defaultFactory (origin, opts) { | |
return opts && opts.connections === 1 | |
? new Client(origin, opts) | |
: new Pool(origin, opts) | |
} | |
class Agent extends DispatcherBase { | |
constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) { | |
super() | |
if (typeof factory !== 'function') { | |
throw new InvalidArgumentError('factory must be a function.') | |
} | |
if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') { | |
throw new InvalidArgumentError('connect must be a function or an object') | |
} | |
if (!Number.isInteger(maxRedirections) || maxRedirections < 0) { | |
throw new InvalidArgumentError('maxRedirections must be a positive number') | |
} | |
if (connect && typeof connect !== 'function') { | |
connect = { ...connect } | |
} | |
this[kInterceptors] = options.interceptors && options.interceptors.Agent && Array.isArray(options.interceptors.Agent) | |
? options.interceptors.Agent | |
: [createRedirectInterceptor({ maxRedirections })] | |
this[kOptions] = { ...util.deepClone(options), connect } | |
this[kOptions].interceptors = options.interceptors | |
? { ...options.interceptors } | |
: undefined | |
this[kMaxRedirections] = maxRedirections | |
this[kFactory] = factory | |
this[kClients] = new Map() | |
this[kFinalizer] = new FinalizationRegistry(/* istanbul ignore next: gc is undeterministic */ key => { | |
const ref = this[kClients].get(key) | |
if (ref !== undefined && ref.deref() === undefined) { | |
this[kClients].delete(key) | |
} | |
}) | |
const agent = this | |
this[kOnDrain] = (origin, targets) => { | |
agent.emit('drain', origin, [agent, ...targets]) | |
} | |
this[kOnConnect] = (origin, targets) => { | |
agent.emit('connect', origin, [agent, ...targets]) | |
} | |
this[kOnDisconnect] = (origin, targets, err) => { | |
agent.emit('disconnect', origin, [agent, ...targets], err) | |
} | |
this[kOnConnectionError] = (origin, targets, err) => { | |
agent.emit('connectionError', origin, [agent, ...targets], err) | |
} | |
} | |
get [kRunning] () { | |
let ret = 0 | |
for (const ref of this[kClients].values()) { | |
const client = ref.deref() | |
/* istanbul ignore next: gc is undeterministic */ | |
if (client) { | |
ret += client[kRunning] | |
} | |
} | |
return ret | |
} | |
[kDispatch] (opts, handler) { | |
let key | |
if (opts.origin && (typeof opts.origin === 'string' || opts.origin instanceof URL)) { | |
key = String(opts.origin) | |
} else { | |
throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.') | |
} | |
const ref = this[kClients].get(key) | |
let dispatcher = ref ? ref.deref() : null | |
if (!dispatcher) { | |
dispatcher = this[kFactory](opts.origin, this[kOptions]) | |
.on('drain', this[kOnDrain]) | |
.on('connect', this[kOnConnect]) | |
.on('disconnect', this[kOnDisconnect]) | |
.on('connectionError', this[kOnConnectionError]) | |
this[kClients].set(key, new WeakRef(dispatcher)) | |
this[kFinalizer].register(dispatcher, key) | |
} | |
return dispatcher.dispatch(opts, handler) | |
} | |
async [kClose] () { | |
const closePromises = [] | |
for (const ref of this[kClients].values()) { | |
const client = ref.deref() | |
/* istanbul ignore else: gc is undeterministic */ | |
if (client) { | |
closePromises.push(client.close()) | |
} | |
} | |
await Promise.all(closePromises) | |
} | |
async [kDestroy] (err) { | |
const destroyPromises = [] | |
for (const ref of this[kClients].values()) { | |
const client = ref.deref() | |
/* istanbul ignore else: gc is undeterministic */ | |
if (client) { | |
destroyPromises.push(client.destroy(err)) | |
} | |
} | |
await Promise.all(destroyPromises) | |
} | |
} | |
module.exports = Agent | |