Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MR414N-ID
GitHub Repository: MR414N-ID/botku2
Path: blob/master/node_modules/agent-base/src/index.ts
1129 views
1
import net from 'net';
2
import http from 'http';
3
import https from 'https';
4
import { Duplex } from 'stream';
5
import { EventEmitter } from 'events';
6
import createDebug from 'debug';
7
import promisify from './promisify';
8
9
const debug = createDebug('agent-base');
10
11
function isAgent(v: any): v is createAgent.AgentLike {
12
return Boolean(v) && typeof v.addRequest === 'function';
13
}
14
15
function isSecureEndpoint(): boolean {
16
const { stack } = new Error();
17
if (typeof stack !== 'string') return false;
18
return stack.split('\n').some(l => l.indexOf('(https.js:') !== -1 || l.indexOf('node:https:') !== -1);
19
}
20
21
function createAgent(opts?: createAgent.AgentOptions): createAgent.Agent;
22
function createAgent(
23
callback: createAgent.AgentCallback,
24
opts?: createAgent.AgentOptions
25
): createAgent.Agent;
26
function createAgent(
27
callback?: createAgent.AgentCallback | createAgent.AgentOptions,
28
opts?: createAgent.AgentOptions
29
) {
30
return new createAgent.Agent(callback, opts);
31
}
32
33
namespace createAgent {
34
export interface ClientRequest extends http.ClientRequest {
35
_last?: boolean;
36
_hadError?: boolean;
37
method: string;
38
}
39
40
export interface AgentRequestOptions {
41
host?: string;
42
path?: string;
43
// `port` on `http.RequestOptions` can be a string or undefined,
44
// but `net.TcpNetConnectOpts` expects only a number
45
port: number;
46
}
47
48
export interface HttpRequestOptions
49
extends AgentRequestOptions,
50
Omit<http.RequestOptions, keyof AgentRequestOptions> {
51
secureEndpoint: false;
52
}
53
54
export interface HttpsRequestOptions
55
extends AgentRequestOptions,
56
Omit<https.RequestOptions, keyof AgentRequestOptions> {
57
secureEndpoint: true;
58
}
59
60
export type RequestOptions = HttpRequestOptions | HttpsRequestOptions;
61
62
export type AgentLike = Pick<createAgent.Agent, 'addRequest'> | http.Agent;
63
64
export type AgentCallbackReturn = Duplex | AgentLike;
65
66
export type AgentCallbackCallback = (
67
err?: Error | null,
68
socket?: createAgent.AgentCallbackReturn
69
) => void;
70
71
export type AgentCallbackPromise = (
72
req: createAgent.ClientRequest,
73
opts: createAgent.RequestOptions
74
) =>
75
| createAgent.AgentCallbackReturn
76
| Promise<createAgent.AgentCallbackReturn>;
77
78
export type AgentCallback = typeof Agent.prototype.callback;
79
80
export type AgentOptions = {
81
timeout?: number;
82
};
83
84
/**
85
* Base `http.Agent` implementation.
86
* No pooling/keep-alive is implemented by default.
87
*
88
* @param {Function} callback
89
* @api public
90
*/
91
export class Agent extends EventEmitter {
92
public timeout: number | null;
93
public maxFreeSockets: number;
94
public maxTotalSockets: number;
95
public maxSockets: number;
96
public sockets: {
97
[key: string]: net.Socket[];
98
};
99
public freeSockets: {
100
[key: string]: net.Socket[];
101
};
102
public requests: {
103
[key: string]: http.IncomingMessage[];
104
};
105
public options: https.AgentOptions;
106
private promisifiedCallback?: createAgent.AgentCallbackPromise;
107
private explicitDefaultPort?: number;
108
private explicitProtocol?: string;
109
110
constructor(
111
callback?: createAgent.AgentCallback | createAgent.AgentOptions,
112
_opts?: createAgent.AgentOptions
113
) {
114
super();
115
116
let opts = _opts;
117
if (typeof callback === 'function') {
118
this.callback = callback;
119
} else if (callback) {
120
opts = callback;
121
}
122
123
// Timeout for the socket to be returned from the callback
124
this.timeout = null;
125
if (opts && typeof opts.timeout === 'number') {
126
this.timeout = opts.timeout;
127
}
128
129
// These aren't actually used by `agent-base`, but are required
130
// for the TypeScript definition files in `@types/node` :/
131
this.maxFreeSockets = 1;
132
this.maxSockets = 1;
133
this.maxTotalSockets = Infinity;
134
this.sockets = {};
135
this.freeSockets = {};
136
this.requests = {};
137
this.options = {};
138
}
139
140
get defaultPort(): number {
141
if (typeof this.explicitDefaultPort === 'number') {
142
return this.explicitDefaultPort;
143
}
144
return isSecureEndpoint() ? 443 : 80;
145
}
146
147
set defaultPort(v: number) {
148
this.explicitDefaultPort = v;
149
}
150
151
get protocol(): string {
152
if (typeof this.explicitProtocol === 'string') {
153
return this.explicitProtocol;
154
}
155
return isSecureEndpoint() ? 'https:' : 'http:';
156
}
157
158
set protocol(v: string) {
159
this.explicitProtocol = v;
160
}
161
162
callback(
163
req: createAgent.ClientRequest,
164
opts: createAgent.RequestOptions,
165
fn: createAgent.AgentCallbackCallback
166
): void;
167
callback(
168
req: createAgent.ClientRequest,
169
opts: createAgent.RequestOptions
170
):
171
| createAgent.AgentCallbackReturn
172
| Promise<createAgent.AgentCallbackReturn>;
173
callback(
174
req: createAgent.ClientRequest,
175
opts: createAgent.AgentOptions,
176
fn?: createAgent.AgentCallbackCallback
177
):
178
| createAgent.AgentCallbackReturn
179
| Promise<createAgent.AgentCallbackReturn>
180
| void {
181
throw new Error(
182
'"agent-base" has no default implementation, you must subclass and override `callback()`'
183
);
184
}
185
186
/**
187
* Called by node-core's "_http_client.js" module when creating
188
* a new HTTP request with this Agent instance.
189
*
190
* @api public
191
*/
192
addRequest(req: ClientRequest, _opts: RequestOptions): void {
193
const opts: RequestOptions = { ..._opts };
194
195
if (typeof opts.secureEndpoint !== 'boolean') {
196
opts.secureEndpoint = isSecureEndpoint();
197
}
198
199
if (opts.host == null) {
200
opts.host = 'localhost';
201
}
202
203
if (opts.port == null) {
204
opts.port = opts.secureEndpoint ? 443 : 80;
205
}
206
207
if (opts.protocol == null) {
208
opts.protocol = opts.secureEndpoint ? 'https:' : 'http:';
209
}
210
211
if (opts.host && opts.path) {
212
// If both a `host` and `path` are specified then it's most
213
// likely the result of a `url.parse()` call... we need to
214
// remove the `path` portion so that `net.connect()` doesn't
215
// attempt to open that as a unix socket file.
216
delete opts.path;
217
}
218
219
delete opts.agent;
220
delete opts.hostname;
221
delete opts._defaultAgent;
222
delete opts.defaultPort;
223
delete opts.createConnection;
224
225
// Hint to use "Connection: close"
226
// XXX: non-documented `http` module API :(
227
req._last = true;
228
req.shouldKeepAlive = false;
229
230
let timedOut = false;
231
let timeoutId: ReturnType<typeof setTimeout> | null = null;
232
const timeoutMs = opts.timeout || this.timeout;
233
234
const onerror = (err: NodeJS.ErrnoException) => {
235
if (req._hadError) return;
236
req.emit('error', err);
237
// For Safety. Some additional errors might fire later on
238
// and we need to make sure we don't double-fire the error event.
239
req._hadError = true;
240
};
241
242
const ontimeout = () => {
243
timeoutId = null;
244
timedOut = true;
245
const err: NodeJS.ErrnoException = new Error(
246
`A "socket" was not created for HTTP request before ${timeoutMs}ms`
247
);
248
err.code = 'ETIMEOUT';
249
onerror(err);
250
};
251
252
const callbackError = (err: NodeJS.ErrnoException) => {
253
if (timedOut) return;
254
if (timeoutId !== null) {
255
clearTimeout(timeoutId);
256
timeoutId = null;
257
}
258
onerror(err);
259
};
260
261
const onsocket = (socket: AgentCallbackReturn) => {
262
if (timedOut) return;
263
if (timeoutId != null) {
264
clearTimeout(timeoutId);
265
timeoutId = null;
266
}
267
268
if (isAgent(socket)) {
269
// `socket` is actually an `http.Agent` instance, so
270
// relinquish responsibility for this `req` to the Agent
271
// from here on
272
debug(
273
'Callback returned another Agent instance %o',
274
socket.constructor.name
275
);
276
(socket as createAgent.Agent).addRequest(req, opts);
277
return;
278
}
279
280
if (socket) {
281
socket.once('free', () => {
282
this.freeSocket(socket as net.Socket, opts);
283
});
284
req.onSocket(socket as net.Socket);
285
return;
286
}
287
288
const err = new Error(
289
`no Duplex stream was returned to agent-base for \`${req.method} ${req.path}\``
290
);
291
onerror(err);
292
};
293
294
if (typeof this.callback !== 'function') {
295
onerror(new Error('`callback` is not defined'));
296
return;
297
}
298
299
if (!this.promisifiedCallback) {
300
if (this.callback.length >= 3) {
301
debug('Converting legacy callback function to promise');
302
this.promisifiedCallback = promisify(this.callback);
303
} else {
304
this.promisifiedCallback = this.callback;
305
}
306
}
307
308
if (typeof timeoutMs === 'number' && timeoutMs > 0) {
309
timeoutId = setTimeout(ontimeout, timeoutMs);
310
}
311
312
if ('port' in opts && typeof opts.port !== 'number') {
313
opts.port = Number(opts.port);
314
}
315
316
try {
317
debug(
318
'Resolving socket for %o request: %o',
319
opts.protocol,
320
`${req.method} ${req.path}`
321
);
322
Promise.resolve(this.promisifiedCallback(req, opts)).then(
323
onsocket,
324
callbackError
325
);
326
} catch (err) {
327
Promise.reject(err).catch(callbackError);
328
}
329
}
330
331
freeSocket(socket: net.Socket, opts: AgentOptions) {
332
debug('Freeing socket %o %o', socket.constructor.name, opts);
333
socket.destroy();
334
}
335
336
destroy() {
337
debug('Destroying agent %o', this.constructor.name);
338
}
339
}
340
341
// So that `instanceof` works correctly
342
createAgent.prototype = createAgent.Agent.prototype;
343
}
344
345
export = createAgent;
346
347