Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
agent.js131 linesDownload Raw Back to dispatcher
1'use strict'
2
3const { InvalidArgumentError } = require('../core/errors')
4const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = require('../core/symbols')
5const DispatcherBase = require('./dispatcher-base')
6const Pool = require('./pool')
7const Client = require('./client')
8const util = require('../core/util')
9const createRedirectInterceptor = require('../interceptor/redirect-interceptor')
10
11const kOnConnect = Symbol('onConnect')
12const kOnDisconnect = Symbol('onDisconnect')
13const kOnConnectionError = Symbol('onConnectionError')
14const kMaxRedirections = Symbol('maxRedirections')
15const kOnDrain = Symbol('onDrain')
16const kFactory = Symbol('factory')
17const kOptions = Symbol('options')
18
19function defaultFactory (origin, opts) {
20  return opts && opts.connections === 1
21    ? new Client(origin, opts)
22    : new Pool(origin, opts)
23}
24
25class Agent extends DispatcherBase {
26  constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) {
27
28    if (typeof factory !== 'function') {
29      throw new InvalidArgumentError('factory must be a function.')
30    }
31
32    if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') {
33      throw new InvalidArgumentError('connect must be a function or an object')
34    }
35
36    if (!Number.isInteger(maxRedirections) || maxRedirections < 0) {
37      throw new InvalidArgumentError('maxRedirections must be a positive number')
38    }
39
40    super(options)
41
42    if (connect && typeof connect !== 'function') {
43      connect = { ...connect }
44    }
45
46    this[kInterceptors] = options.interceptors?.Agent && Array.isArray(options.interceptors.Agent)
47      ? options.interceptors.Agent
48      : [createRedirectInterceptor({ maxRedirections })]
49
50    this[kOptions] = { ...util.deepClone(options), connect }
51    this[kOptions].interceptors = options.interceptors
52      ? { ...options.interceptors }
53      : undefined
54    this[kMaxRedirections] = maxRedirections
55    this[kFactory] = factory
56    this[kClients] = new Map()
57
58    this[kOnDrain] = (origin, targets) => {
59      this.emit('drain', origin, [this, ...targets])
60    }
61
62    this[kOnConnect] = (origin, targets) => {
63      this.emit('connect', origin, [this, ...targets])
64    }
65
66    this[kOnDisconnect] = (origin, targets, err) => {
67      this.emit('disconnect', origin, [this, ...targets], err)
68    }
69
70    this[kOnConnectionError] = (origin, targets, err) => {
71      this.emit('connectionError', origin, [this, ...targets], err)
72    }
73  }
74
75  get [kRunning] () {
76    let ret = 0
77    for (const client of this[kClients].values()) {
78      ret += client[kRunning]
79    }
80    return ret
81  }
82
83  [kDispatch] (opts, handler) {
84    let key
85    if (opts.origin && (typeof opts.origin === 'string' || opts.origin instanceof URL)) {
86      key = String(opts.origin)
87    } else {
88      throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.')
89    }
90
91    let dispatcher = this[kClients].get(key)
92
93    if (!dispatcher) {
94      dispatcher = this[kFactory](opts.origin, this[kOptions])
95        .on('drain', this[kOnDrain])
96        .on('connect', this[kOnConnect])
97        .on('disconnect', this[kOnDisconnect])
98        .on('connectionError', this[kOnConnectionError])
99
100      // This introduces a tiny memory leak, as dispatchers are never removed from the map.
101      // TODO(mcollina): remove te timer when the client/pool do not have any more
102      // active connections.
103      this[kClients].set(key, dispatcher)
104    }
105
106    return dispatcher.dispatch(opts, handler)
107  }
108
109  async [kClose] () {
110    const closePromises = []
111    for (const client of this[kClients].values()) {
112      closePromises.push(client.close())
113    }
114    this[kClients].clear()
115
116    await Promise.all(closePromises)
117  }
118
119  async [kDestroy] (err) {
120    const destroyPromises = []
121    for (const client of this[kClients].values()) {
122      destroyPromises.push(client.destroy(err))
123    }
124    this[kClients].clear()
125
126    await Promise.all(destroyPromises)
127  }
128}
129
130module.exports = Agent
131 
codekingpro/portable-devtools · Team Ai