@putuofc/fastp 0.0.1 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +133 -24
- package/dist/index.js +1260 -113
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33,27 +33,589 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.
|
|
36
|
+
exports.ProfileRegistry = exports.AdvancedTLSClient = void 0;
|
|
37
|
+
const tls = __importStar(require("tls"));
|
|
38
|
+
const net = __importStar(require("net"));
|
|
37
39
|
const https = __importStar(require("https"));
|
|
40
|
+
const http2 = __importStar(require("http2"));
|
|
41
|
+
const crypto = __importStar(require("crypto"));
|
|
42
|
+
const events_1 = require("events");
|
|
38
43
|
const zlib = __importStar(require("zlib"));
|
|
39
44
|
const util_1 = require("util");
|
|
40
45
|
const stream_1 = require("stream");
|
|
41
|
-
const
|
|
42
|
-
const
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const url = __importStar(require("url"));
|
|
43
49
|
const gunzip = (0, util_1.promisify)(zlib.gunzip);
|
|
44
50
|
const brotliDecompress = (0, util_1.promisify)(zlib.brotliDecompress);
|
|
45
51
|
const inflate = (0, util_1.promisify)(zlib.inflate);
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
const GREASE_VALUES = [0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a, 0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa];
|
|
53
|
+
class ProfileRegistry {
|
|
54
|
+
static getGrease() {
|
|
55
|
+
return GREASE_VALUES[Math.floor(Math.random() * GREASE_VALUES.length)];
|
|
56
|
+
}
|
|
57
|
+
static chromeMobile143Android() {
|
|
58
|
+
const grease1 = this.getGrease();
|
|
59
|
+
const grease2 = this.getGrease();
|
|
60
|
+
const grease3 = this.getGrease();
|
|
61
|
+
const grease4 = this.getGrease();
|
|
62
|
+
return {
|
|
63
|
+
name: 'chrome_mobile_143_android',
|
|
64
|
+
version: '143.0.0.0',
|
|
65
|
+
tls: {
|
|
66
|
+
cipherSuites: [
|
|
67
|
+
grease1,
|
|
68
|
+
4865,
|
|
69
|
+
4866,
|
|
70
|
+
4867,
|
|
71
|
+
49195,
|
|
72
|
+
49199,
|
|
73
|
+
49196,
|
|
74
|
+
49200,
|
|
75
|
+
52393,
|
|
76
|
+
52392,
|
|
77
|
+
49171,
|
|
78
|
+
49172,
|
|
79
|
+
156,
|
|
80
|
+
157,
|
|
81
|
+
47,
|
|
82
|
+
53
|
|
83
|
+
],
|
|
84
|
+
extensions: [
|
|
85
|
+
grease2,
|
|
86
|
+
65037,
|
|
87
|
+
27,
|
|
88
|
+
43,
|
|
89
|
+
16,
|
|
90
|
+
35,
|
|
91
|
+
45,
|
|
92
|
+
11,
|
|
93
|
+
65281,
|
|
94
|
+
5,
|
|
95
|
+
10,
|
|
96
|
+
23,
|
|
97
|
+
0,
|
|
98
|
+
17513,
|
|
99
|
+
51,
|
|
100
|
+
18,
|
|
101
|
+
13,
|
|
102
|
+
grease3
|
|
103
|
+
],
|
|
104
|
+
supportedGroups: [
|
|
105
|
+
grease4,
|
|
106
|
+
25497,
|
|
107
|
+
29,
|
|
108
|
+
23,
|
|
109
|
+
24
|
|
110
|
+
],
|
|
111
|
+
signatureAlgorithms: [
|
|
112
|
+
1027,
|
|
113
|
+
2052,
|
|
114
|
+
1025,
|
|
115
|
+
1283,
|
|
116
|
+
2053,
|
|
117
|
+
1281,
|
|
118
|
+
2054,
|
|
119
|
+
1537
|
|
120
|
+
],
|
|
121
|
+
supportedVersions: [
|
|
122
|
+
grease2,
|
|
123
|
+
772,
|
|
124
|
+
771
|
|
125
|
+
],
|
|
126
|
+
ecPointFormats: [0],
|
|
127
|
+
alpnProtocols: ['h2', 'http/1.1'],
|
|
128
|
+
pskKeyExchangeModes: [1],
|
|
129
|
+
compressionMethods: [0],
|
|
130
|
+
grease: true,
|
|
131
|
+
recordSizeLimit: 16385,
|
|
132
|
+
certificateCompression: [2],
|
|
133
|
+
ocspStapling: true,
|
|
134
|
+
signedCertificateTimestamp: true
|
|
135
|
+
},
|
|
136
|
+
http2: {
|
|
137
|
+
windowUpdate: 15663105,
|
|
138
|
+
headerTableSize: 65536,
|
|
139
|
+
enablePush: 0,
|
|
140
|
+
initialWindowSize: 6291456,
|
|
141
|
+
maxFrameSize: 16384,
|
|
142
|
+
maxHeaderListSize: 262144,
|
|
143
|
+
settingsOrder: [1, 2, 4, 6],
|
|
144
|
+
connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
|
|
145
|
+
priorityFrames: [
|
|
146
|
+
{
|
|
147
|
+
streamId: 0,
|
|
148
|
+
weight: 256,
|
|
149
|
+
dependency: 0,
|
|
150
|
+
exclusive: true
|
|
151
|
+
}
|
|
152
|
+
],
|
|
153
|
+
pseudoHeaderOrder: [':method', ':authority', ':scheme', ':path'],
|
|
154
|
+
headerOrder: [
|
|
155
|
+
'sec-ch-ua',
|
|
156
|
+
'sec-ch-ua-mobile',
|
|
157
|
+
'sec-ch-ua-platform',
|
|
158
|
+
'upgrade-insecure-requests',
|
|
159
|
+
'user-agent',
|
|
160
|
+
'accept',
|
|
161
|
+
'sec-fetch-site',
|
|
162
|
+
'sec-fetch-mode',
|
|
163
|
+
'sec-fetch-dest',
|
|
164
|
+
'accept-encoding',
|
|
165
|
+
'accept-language',
|
|
166
|
+
'priority'
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
userAgent: 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36',
|
|
170
|
+
secChUa: '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
|
|
171
|
+
secChUaPlatform: '"Android"',
|
|
172
|
+
secChUaMobile: '?1',
|
|
173
|
+
secFetchSite: 'none',
|
|
174
|
+
secFetchMode: 'navigate',
|
|
175
|
+
secFetchDest: 'document',
|
|
176
|
+
upgradeInsecureRequests: '1',
|
|
177
|
+
acceptLanguage: 'id,en-US;q=0.9,en;q=0.8,ms;q=0.7,ja;q=0.6,zh-CN;q=0.5,zh;q=0.4',
|
|
178
|
+
priority: 'u=0, i'
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
static chrome133PSK() {
|
|
182
|
+
const grease1 = this.getGrease();
|
|
183
|
+
const grease2 = this.getGrease();
|
|
184
|
+
const grease3 = this.getGrease();
|
|
185
|
+
const grease4 = this.getGrease();
|
|
186
|
+
return {
|
|
187
|
+
name: 'chrome_133_psk',
|
|
188
|
+
version: '133.0.0.0',
|
|
189
|
+
tls: {
|
|
190
|
+
cipherSuites: [
|
|
191
|
+
grease1,
|
|
192
|
+
4865,
|
|
193
|
+
4866,
|
|
194
|
+
4867,
|
|
195
|
+
49195,
|
|
196
|
+
49199,
|
|
197
|
+
49196,
|
|
198
|
+
49200,
|
|
199
|
+
52393,
|
|
200
|
+
52392,
|
|
201
|
+
49171,
|
|
202
|
+
49172,
|
|
203
|
+
156,
|
|
204
|
+
157,
|
|
205
|
+
47,
|
|
206
|
+
53
|
|
207
|
+
],
|
|
208
|
+
extensions: [
|
|
209
|
+
grease2,
|
|
210
|
+
0,
|
|
211
|
+
23,
|
|
212
|
+
65281,
|
|
213
|
+
10,
|
|
214
|
+
11,
|
|
215
|
+
35,
|
|
216
|
+
16,
|
|
217
|
+
5,
|
|
218
|
+
13,
|
|
219
|
+
18,
|
|
220
|
+
51,
|
|
221
|
+
45,
|
|
222
|
+
43,
|
|
223
|
+
27,
|
|
224
|
+
21,
|
|
225
|
+
grease3
|
|
226
|
+
],
|
|
227
|
+
supportedGroups: [
|
|
228
|
+
grease4,
|
|
229
|
+
25497,
|
|
230
|
+
29,
|
|
231
|
+
23,
|
|
232
|
+
24
|
|
233
|
+
],
|
|
234
|
+
signatureAlgorithms: [
|
|
235
|
+
1027,
|
|
236
|
+
2052,
|
|
237
|
+
1025,
|
|
238
|
+
1283,
|
|
239
|
+
2053,
|
|
240
|
+
1281,
|
|
241
|
+
2054,
|
|
242
|
+
1537
|
|
243
|
+
],
|
|
244
|
+
supportedVersions: [
|
|
245
|
+
grease2,
|
|
246
|
+
772,
|
|
247
|
+
771
|
|
248
|
+
],
|
|
249
|
+
ecPointFormats: [0],
|
|
250
|
+
alpnProtocols: ['h2', 'http/1.1'],
|
|
251
|
+
pskKeyExchangeModes: [1],
|
|
252
|
+
compressionMethods: [0],
|
|
253
|
+
grease: true,
|
|
254
|
+
recordSizeLimit: 16385
|
|
255
|
+
},
|
|
256
|
+
http2: {
|
|
257
|
+
windowUpdate: 15663105,
|
|
258
|
+
headerTableSize: 65536,
|
|
259
|
+
enablePush: 0,
|
|
260
|
+
initialWindowSize: 6291456,
|
|
261
|
+
maxFrameSize: 16384,
|
|
262
|
+
maxHeaderListSize: 262144,
|
|
263
|
+
settingsOrder: [1, 2, 4, 6],
|
|
264
|
+
connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
|
|
265
|
+
priorityFrames: [
|
|
266
|
+
{
|
|
267
|
+
streamId: 0,
|
|
268
|
+
weight: 256,
|
|
269
|
+
dependency: 0,
|
|
270
|
+
exclusive: true
|
|
271
|
+
}
|
|
272
|
+
],
|
|
273
|
+
pseudoHeaderOrder: [':method', ':authority', ':scheme', ':path'],
|
|
274
|
+
headerOrder: [
|
|
275
|
+
'cache-control',
|
|
276
|
+
'sec-ch-ua',
|
|
277
|
+
'sec-ch-ua-mobile',
|
|
278
|
+
'sec-ch-ua-platform',
|
|
279
|
+
'upgrade-insecure-requests',
|
|
280
|
+
'user-agent',
|
|
281
|
+
'accept',
|
|
282
|
+
'sec-fetch-site',
|
|
283
|
+
'sec-fetch-mode',
|
|
284
|
+
'sec-fetch-user',
|
|
285
|
+
'sec-fetch-dest',
|
|
286
|
+
'accept-encoding',
|
|
287
|
+
'accept-language'
|
|
288
|
+
]
|
|
289
|
+
},
|
|
290
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
|
|
291
|
+
secChUa: '"Google Chrome";v="133", "Chromium";v="133", "Not_A Brand";v="24"',
|
|
292
|
+
secChUaPlatform: '"Windows"',
|
|
293
|
+
secChUaMobile: '?0',
|
|
294
|
+
secFetchSite: 'none',
|
|
295
|
+
secFetchMode: 'navigate',
|
|
296
|
+
secFetchDest: 'document'
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
static chrome133() {
|
|
300
|
+
const grease1 = this.getGrease();
|
|
301
|
+
const grease2 = this.getGrease();
|
|
302
|
+
const grease3 = this.getGrease();
|
|
303
|
+
const grease4 = this.getGrease();
|
|
304
|
+
return {
|
|
305
|
+
name: 'chrome_133',
|
|
306
|
+
version: '133.0.0.0',
|
|
307
|
+
tls: {
|
|
308
|
+
cipherSuites: [
|
|
309
|
+
grease1,
|
|
310
|
+
4865,
|
|
311
|
+
4866,
|
|
312
|
+
4867,
|
|
313
|
+
49195,
|
|
314
|
+
49199,
|
|
315
|
+
49196,
|
|
316
|
+
49200,
|
|
317
|
+
52393,
|
|
318
|
+
52392,
|
|
319
|
+
49171,
|
|
320
|
+
49172,
|
|
321
|
+
156,
|
|
322
|
+
157,
|
|
323
|
+
47,
|
|
324
|
+
53
|
|
325
|
+
],
|
|
326
|
+
extensions: [
|
|
327
|
+
grease2,
|
|
328
|
+
0,
|
|
329
|
+
23,
|
|
330
|
+
65281,
|
|
331
|
+
10,
|
|
332
|
+
11,
|
|
333
|
+
35,
|
|
334
|
+
16,
|
|
335
|
+
5,
|
|
336
|
+
13,
|
|
337
|
+
18,
|
|
338
|
+
51,
|
|
339
|
+
45,
|
|
340
|
+
43,
|
|
341
|
+
27,
|
|
342
|
+
21,
|
|
343
|
+
grease3
|
|
344
|
+
],
|
|
345
|
+
supportedGroups: [
|
|
346
|
+
grease4,
|
|
347
|
+
25497,
|
|
348
|
+
29,
|
|
349
|
+
23,
|
|
350
|
+
24
|
|
351
|
+
],
|
|
352
|
+
signatureAlgorithms: [
|
|
353
|
+
1027,
|
|
354
|
+
2052,
|
|
355
|
+
1025,
|
|
356
|
+
1283,
|
|
357
|
+
2053,
|
|
358
|
+
1281,
|
|
359
|
+
2054,
|
|
360
|
+
1537
|
|
361
|
+
],
|
|
362
|
+
supportedVersions: [
|
|
363
|
+
grease2,
|
|
364
|
+
772,
|
|
365
|
+
771
|
|
366
|
+
],
|
|
367
|
+
ecPointFormats: [0],
|
|
368
|
+
alpnProtocols: ['h2', 'http/1.1'],
|
|
369
|
+
pskKeyExchangeModes: [1],
|
|
370
|
+
compressionMethods: [0],
|
|
371
|
+
grease: true,
|
|
372
|
+
recordSizeLimit: 16385
|
|
373
|
+
},
|
|
374
|
+
http2: {
|
|
375
|
+
windowUpdate: 15663105,
|
|
376
|
+
headerTableSize: 65536,
|
|
377
|
+
enablePush: 0,
|
|
378
|
+
initialWindowSize: 6291456,
|
|
379
|
+
maxFrameSize: 16384,
|
|
380
|
+
maxHeaderListSize: 262144,
|
|
381
|
+
settingsOrder: [1, 2, 4, 6],
|
|
382
|
+
connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
|
|
383
|
+
priorityFrames: [
|
|
384
|
+
{
|
|
385
|
+
streamId: 0,
|
|
386
|
+
weight: 256,
|
|
387
|
+
dependency: 0,
|
|
388
|
+
exclusive: true
|
|
389
|
+
}
|
|
390
|
+
],
|
|
391
|
+
pseudoHeaderOrder: [':method', ':authority', ':scheme', ':path'],
|
|
392
|
+
headerOrder: [
|
|
393
|
+
'cache-control',
|
|
394
|
+
'sec-ch-ua',
|
|
395
|
+
'sec-ch-ua-mobile',
|
|
396
|
+
'sec-ch-ua-platform',
|
|
397
|
+
'upgrade-insecure-requests',
|
|
398
|
+
'user-agent',
|
|
399
|
+
'accept',
|
|
400
|
+
'sec-fetch-site',
|
|
401
|
+
'sec-fetch-mode',
|
|
402
|
+
'sec-fetch-user',
|
|
403
|
+
'sec-fetch-dest',
|
|
404
|
+
'accept-encoding',
|
|
405
|
+
'accept-language'
|
|
406
|
+
]
|
|
407
|
+
},
|
|
408
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
|
|
409
|
+
secChUa: '"Google Chrome";v="133", "Chromium";v="133", "Not_A Brand";v="24"',
|
|
410
|
+
secChUaPlatform: '"Windows"',
|
|
411
|
+
secChUaMobile: '?0',
|
|
412
|
+
secFetchSite: 'none',
|
|
413
|
+
secFetchMode: 'navigate',
|
|
414
|
+
secFetchDest: 'document'
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
static firefox117() {
|
|
418
|
+
return {
|
|
419
|
+
name: 'firefox_117',
|
|
420
|
+
version: '117.0',
|
|
421
|
+
tls: {
|
|
422
|
+
cipherSuites: [
|
|
423
|
+
4865,
|
|
424
|
+
4867,
|
|
425
|
+
4866,
|
|
426
|
+
49195,
|
|
427
|
+
49199,
|
|
428
|
+
52393,
|
|
429
|
+
52392,
|
|
430
|
+
49196,
|
|
431
|
+
49200,
|
|
432
|
+
159,
|
|
433
|
+
158,
|
|
434
|
+
49161,
|
|
435
|
+
49162,
|
|
436
|
+
156,
|
|
437
|
+
157,
|
|
438
|
+
47,
|
|
439
|
+
53
|
|
440
|
+
],
|
|
441
|
+
extensions: [0, 23, 65281, 10, 11, 35, 16, 5, 34, 51, 43, 13, 45, 28, 21],
|
|
442
|
+
supportedGroups: [
|
|
443
|
+
29,
|
|
444
|
+
23,
|
|
445
|
+
24,
|
|
446
|
+
25,
|
|
447
|
+
256,
|
|
448
|
+
257
|
|
449
|
+
],
|
|
450
|
+
signatureAlgorithms: [
|
|
451
|
+
1027,
|
|
452
|
+
1283,
|
|
453
|
+
1539,
|
|
454
|
+
2052,
|
|
455
|
+
2053,
|
|
456
|
+
2054,
|
|
457
|
+
1025,
|
|
458
|
+
1281,
|
|
459
|
+
1537,
|
|
460
|
+
513,
|
|
461
|
+
515
|
|
462
|
+
],
|
|
463
|
+
supportedVersions: [
|
|
464
|
+
772,
|
|
465
|
+
771
|
|
466
|
+
],
|
|
467
|
+
ecPointFormats: [0],
|
|
468
|
+
alpnProtocols: ['h2', 'http/1.1'],
|
|
469
|
+
pskKeyExchangeModes: [1],
|
|
470
|
+
compressionMethods: [0],
|
|
471
|
+
grease: false
|
|
472
|
+
},
|
|
473
|
+
http2: {
|
|
474
|
+
windowUpdate: 12517377,
|
|
475
|
+
headerTableSize: 65536,
|
|
476
|
+
enablePush: 0,
|
|
477
|
+
initialWindowSize: 131072,
|
|
478
|
+
maxFrameSize: 16384,
|
|
479
|
+
settingsOrder: [1, 4, 5],
|
|
480
|
+
connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
|
|
481
|
+
priorityFrames: [
|
|
482
|
+
{ streamId: 3, weight: 200, dependency: 0, exclusive: false },
|
|
483
|
+
{ streamId: 5, weight: 100, dependency: 0, exclusive: false },
|
|
484
|
+
{ streamId: 7, weight: 0, dependency: 0, exclusive: false },
|
|
485
|
+
{ streamId: 9, weight: 0, dependency: 7, exclusive: false },
|
|
486
|
+
{ streamId: 11, weight: 0, dependency: 3, exclusive: false },
|
|
487
|
+
{ streamId: 13, weight: 240, dependency: 0, exclusive: false }
|
|
488
|
+
],
|
|
489
|
+
pseudoHeaderOrder: [':method', ':path', ':authority', ':scheme'],
|
|
490
|
+
headerPriority: { streamDep: 13, exclusive: false, weight: 41 },
|
|
491
|
+
headerOrder: []
|
|
492
|
+
},
|
|
493
|
+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:117.0) Gecko/20100101 Firefox/117.0',
|
|
494
|
+
secChUa: '',
|
|
495
|
+
secChUaPlatform: '',
|
|
496
|
+
secChUaMobile: '',
|
|
497
|
+
secFetchSite: 'none',
|
|
498
|
+
secFetchMode: 'navigate',
|
|
499
|
+
secFetchDest: 'document'
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
static safariIOS18() {
|
|
503
|
+
const grease1 = this.getGrease();
|
|
504
|
+
const grease2 = this.getGrease();
|
|
505
|
+
return {
|
|
506
|
+
name: 'safari_ios_18_0',
|
|
507
|
+
version: '18.0',
|
|
508
|
+
tls: {
|
|
509
|
+
cipherSuites: [
|
|
510
|
+
grease1,
|
|
511
|
+
4865,
|
|
512
|
+
4866,
|
|
513
|
+
4867,
|
|
514
|
+
49196,
|
|
515
|
+
49195,
|
|
516
|
+
52393,
|
|
517
|
+
49200,
|
|
518
|
+
49199,
|
|
519
|
+
52392,
|
|
520
|
+
159,
|
|
521
|
+
158,
|
|
522
|
+
49162,
|
|
523
|
+
49161,
|
|
524
|
+
157,
|
|
525
|
+
156,
|
|
526
|
+
53,
|
|
527
|
+
47,
|
|
528
|
+
49188,
|
|
529
|
+
49187,
|
|
530
|
+
60
|
|
531
|
+
],
|
|
532
|
+
extensions: [
|
|
533
|
+
grease2,
|
|
534
|
+
0,
|
|
535
|
+
23,
|
|
536
|
+
65281,
|
|
537
|
+
10,
|
|
538
|
+
11,
|
|
539
|
+
16,
|
|
540
|
+
5,
|
|
541
|
+
13,
|
|
542
|
+
18,
|
|
543
|
+
51,
|
|
544
|
+
45,
|
|
545
|
+
43,
|
|
546
|
+
21
|
|
547
|
+
],
|
|
548
|
+
supportedGroups: [
|
|
549
|
+
29,
|
|
550
|
+
23,
|
|
551
|
+
24,
|
|
552
|
+
25
|
|
553
|
+
],
|
|
554
|
+
signatureAlgorithms: [
|
|
555
|
+
1027,
|
|
556
|
+
2052,
|
|
557
|
+
1025,
|
|
558
|
+
1283,
|
|
559
|
+
513,
|
|
560
|
+
2053,
|
|
561
|
+
2053,
|
|
562
|
+
1281,
|
|
563
|
+
2054,
|
|
564
|
+
1537,
|
|
565
|
+
515
|
|
566
|
+
],
|
|
567
|
+
supportedVersions: [
|
|
568
|
+
772,
|
|
569
|
+
771,
|
|
570
|
+
770,
|
|
571
|
+
769
|
|
572
|
+
],
|
|
573
|
+
ecPointFormats: [0],
|
|
574
|
+
alpnProtocols: ['h2', 'http/1.1'],
|
|
575
|
+
pskKeyExchangeModes: [1],
|
|
576
|
+
compressionMethods: [0],
|
|
577
|
+
grease: true
|
|
578
|
+
},
|
|
579
|
+
http2: {
|
|
580
|
+
windowUpdate: 10420225,
|
|
581
|
+
headerTableSize: 4096,
|
|
582
|
+
enablePush: 0,
|
|
583
|
+
maxConcurrentStreams: 100,
|
|
584
|
+
initialWindowSize: 2097152,
|
|
585
|
+
maxFrameSize: 16384,
|
|
586
|
+
settingsOrder: [2, 3, 4, 8, 9],
|
|
587
|
+
connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
|
|
588
|
+
priorityFrames: [],
|
|
589
|
+
pseudoHeaderOrder: [':method', ':scheme', ':authority', ':path'],
|
|
590
|
+
headerOrder: []
|
|
591
|
+
},
|
|
592
|
+
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1',
|
|
593
|
+
secChUa: '',
|
|
594
|
+
secChUaPlatform: '',
|
|
595
|
+
secChUaMobile: '?1',
|
|
596
|
+
secFetchSite: 'none',
|
|
597
|
+
secFetchMode: 'navigate',
|
|
598
|
+
secFetchDest: 'document'
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
static get(name) {
|
|
602
|
+
switch (name.toLowerCase()) {
|
|
603
|
+
case 'chrome_mobile_143_android': return this.chromeMobile143Android();
|
|
604
|
+
case 'chrome_133_psk': return this.chrome133PSK();
|
|
605
|
+
case 'chrome_133': return this.chrome133();
|
|
606
|
+
case 'firefox_117': return this.firefox117();
|
|
607
|
+
case 'safari_ios_18_0': return this.safariIOS18();
|
|
608
|
+
default: return this.chrome133();
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
static get latest() {
|
|
612
|
+
return this.chrome133();
|
|
613
|
+
}
|
|
614
|
+
static get latestMobile() {
|
|
615
|
+
return this.chromeMobile143Android();
|
|
616
|
+
}
|
|
56
617
|
}
|
|
618
|
+
exports.ProfileRegistry = ProfileRegistry;
|
|
57
619
|
class CookieJar {
|
|
58
620
|
constructor() {
|
|
59
621
|
this.cookies = new Map();
|
|
@@ -82,97 +644,595 @@ class CookieJar {
|
|
|
82
644
|
return validCookies.join('; ');
|
|
83
645
|
}
|
|
84
646
|
parseSetCookie(domain, setCookieHeaders) {
|
|
85
|
-
|
|
647
|
+
if (!setCookieHeaders)
|
|
648
|
+
return;
|
|
649
|
+
const headers = Array.isArray(setCookieHeaders) ? setCookieHeaders : typeof setCookieHeaders === 'string' ? [setCookieHeaders] : [];
|
|
86
650
|
for (const header of headers) {
|
|
87
|
-
if (!header)
|
|
88
|
-
continue;
|
|
89
651
|
const parts = header.split(';').map(p => p.trim());
|
|
90
652
|
const [nameValue] = parts;
|
|
91
653
|
if (!nameValue)
|
|
92
654
|
continue;
|
|
93
|
-
const [name, value = ''] = nameValue.split('='
|
|
655
|
+
const [name, value = ''] = nameValue.split('=');
|
|
94
656
|
const options = { path: '/' };
|
|
95
657
|
for (let i = 1; i < parts.length; i++) {
|
|
96
|
-
const
|
|
658
|
+
const part = parts[i];
|
|
659
|
+
if (!part)
|
|
660
|
+
continue;
|
|
661
|
+
const [key, val] = part.split('=');
|
|
97
662
|
const lowerKey = key.toLowerCase();
|
|
98
|
-
if (lowerKey === 'expires')
|
|
663
|
+
if (lowerKey === 'expires' && val)
|
|
99
664
|
options.expires = new Date(val);
|
|
100
|
-
else if (lowerKey === 'path')
|
|
665
|
+
else if (lowerKey === 'path' && val)
|
|
101
666
|
options.path = val;
|
|
102
667
|
else if (lowerKey === 'secure')
|
|
103
668
|
options.secure = true;
|
|
104
669
|
else if (lowerKey === 'httponly')
|
|
105
670
|
options.httpOnly = true;
|
|
106
|
-
else if (lowerKey === 'samesite')
|
|
671
|
+
else if (lowerKey === 'samesite' && val)
|
|
107
672
|
options.sameSite = val;
|
|
108
673
|
}
|
|
109
674
|
this.setCookie(domain, name, value, options);
|
|
110
675
|
}
|
|
111
676
|
}
|
|
112
|
-
clear() {
|
|
113
|
-
this.cookies.clear();
|
|
114
|
-
}
|
|
115
677
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
678
|
+
var Protocol;
|
|
679
|
+
(function (Protocol) {
|
|
680
|
+
Protocol["HTTP1"] = "http1";
|
|
681
|
+
Protocol["HTTP2"] = "http2";
|
|
682
|
+
})(Protocol || (Protocol = {}));
|
|
683
|
+
class UnifiedClientManager extends events_1.EventEmitter {
|
|
684
|
+
constructor(tlsSocket, profile, hostname, cookieJar) {
|
|
685
|
+
super();
|
|
686
|
+
this.http2Client = null;
|
|
687
|
+
this.negotiatedProtocol = Protocol.HTTP1;
|
|
688
|
+
this.tlsSocket = tlsSocket;
|
|
689
|
+
this.profile = profile;
|
|
690
|
+
this.hostname = hostname;
|
|
691
|
+
this.cookieJar = cookieJar;
|
|
119
692
|
}
|
|
120
|
-
async
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
693
|
+
async initialize() {
|
|
694
|
+
const alpn = this.tlsSocket.alpnProtocol;
|
|
695
|
+
if (alpn === 'h2') {
|
|
696
|
+
this.negotiatedProtocol = Protocol.HTTP2;
|
|
697
|
+
await this.createHttp2Session();
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
this.negotiatedProtocol = Protocol.HTTP1;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
async createHttp2Session() {
|
|
704
|
+
return new Promise((resolve, reject) => {
|
|
705
|
+
const settings = {};
|
|
706
|
+
this.profile.http2.settingsOrder.forEach(id => {
|
|
707
|
+
switch (id) {
|
|
708
|
+
case 1:
|
|
709
|
+
settings.headerTableSize = this.profile.http2.headerTableSize;
|
|
710
|
+
break;
|
|
711
|
+
case 2:
|
|
712
|
+
settings.enablePush = this.profile.http2.enablePush === 1;
|
|
713
|
+
break;
|
|
714
|
+
case 3:
|
|
715
|
+
if (this.profile.http2.maxConcurrentStreams)
|
|
716
|
+
settings.maxConcurrentStreams = this.profile.http2.maxConcurrentStreams;
|
|
717
|
+
break;
|
|
718
|
+
case 4:
|
|
719
|
+
settings.initialWindowSize = this.profile.http2.initialWindowSize;
|
|
720
|
+
break;
|
|
721
|
+
case 5:
|
|
722
|
+
if (this.profile.http2.maxFrameSize)
|
|
723
|
+
settings.maxFrameSize = this.profile.http2.maxFrameSize;
|
|
724
|
+
break;
|
|
725
|
+
case 6:
|
|
726
|
+
if (this.profile.http2.maxHeaderListSize)
|
|
727
|
+
settings.maxHeaderListSize = this.profile.http2.maxHeaderListSize;
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
this.http2Client = http2.connect(`https://${this.hostname}`, {
|
|
732
|
+
createConnection: () => this.tlsSocket,
|
|
733
|
+
settings
|
|
734
|
+
});
|
|
735
|
+
this.http2Client.once('connect', () => resolve());
|
|
736
|
+
this.http2Client.once('error', reject);
|
|
737
|
+
this.http2Client.once('timeout', () => reject(new Error('HTTP/2 session timeout')));
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
async request(path, options, timingStart, headers, post_data, out, callback) {
|
|
741
|
+
const cleanHeaders = { ...headers };
|
|
742
|
+
delete cleanHeaders['connection'];
|
|
743
|
+
delete cleanHeaders['Connection'];
|
|
744
|
+
delete cleanHeaders['keep-alive'];
|
|
745
|
+
delete cleanHeaders['Keep-Alive'];
|
|
746
|
+
delete cleanHeaders['proxy-connection'];
|
|
747
|
+
delete cleanHeaders['Proxy-Connection'];
|
|
748
|
+
delete cleanHeaders['upgrade'];
|
|
749
|
+
delete cleanHeaders['Upgrade'];
|
|
750
|
+
delete cleanHeaders['transfer-encoding'];
|
|
751
|
+
delete cleanHeaders['Transfer-Encoding'];
|
|
752
|
+
if (this.negotiatedProtocol === Protocol.HTTP2 && this.http2Client && !this.http2Client.destroyed) {
|
|
753
|
+
await this.requestHttp2(path, options, timingStart, cleanHeaders, post_data, out, callback);
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
await this.requestHttp1(path, options, timingStart, cleanHeaders, post_data, out, callback);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
async requestHttp2(path, options, timingStart, headers, post_data, out, callback) {
|
|
760
|
+
if (!this.http2Client || this.http2Client.destroyed)
|
|
761
|
+
throw new Error('HTTP/2 session not available');
|
|
762
|
+
const reqHeaders = {
|
|
763
|
+
':method': options.method?.toUpperCase() || 'GET',
|
|
764
|
+
':authority': this.hostname,
|
|
765
|
+
':scheme': 'https',
|
|
766
|
+
':path': path,
|
|
767
|
+
...headers
|
|
768
|
+
};
|
|
769
|
+
const stream = this.http2Client.request(reqHeaders);
|
|
770
|
+
const response = new events_1.EventEmitter();
|
|
771
|
+
response.timing = { socket: timingStart, lookup: 0, connect: 0, secureConnect: 0, response: 0, end: 0, total: 0 };
|
|
772
|
+
response.fingerprints = {
|
|
773
|
+
ja3: this.generateJA3(),
|
|
774
|
+
ja3Hash: crypto.createHash('md5').update(this.generateJA3()).digest('hex'),
|
|
775
|
+
akamai: `1:${this.profile.http2.headerTableSize};2:${this.profile.http2.enablePush};4:${this.profile.http2.initialWindowSize};6:${this.profile.http2.maxHeaderListSize ?? ''}|00|0|m,a,s,p`
|
|
131
776
|
};
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
headers
|
|
777
|
+
stream.once('response', (hdrs) => {
|
|
778
|
+
response.statusCode = hdrs[':status'];
|
|
779
|
+
response.headers = hdrs;
|
|
780
|
+
if (hdrs['set-cookie'])
|
|
781
|
+
this.cookieJar.parseSetCookie(this.hostname, hdrs['set-cookie']);
|
|
782
|
+
response.timing.response = Date.now() - timingStart;
|
|
783
|
+
out.emit('header', response.statusCode, hdrs);
|
|
784
|
+
out.emit('headers', hdrs);
|
|
785
|
+
});
|
|
786
|
+
const chunks = [];
|
|
787
|
+
const rawChunks = [];
|
|
788
|
+
stream.on('data', (chunk) => {
|
|
789
|
+
// Convert string to Buffer if needed
|
|
790
|
+
const bufferChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
791
|
+
rawChunks.push(bufferChunk);
|
|
792
|
+
chunks.push(bufferChunk);
|
|
793
|
+
out.write(bufferChunk);
|
|
794
|
+
});
|
|
795
|
+
stream.once('end', async () => {
|
|
796
|
+
let body = Buffer.concat(chunks);
|
|
797
|
+
response.bytes = body.length;
|
|
798
|
+
response.raw = Buffer.concat(rawChunks);
|
|
799
|
+
if (options.decompress !== false && response.headers) {
|
|
800
|
+
const encodingHeader = response.headers['content-encoding'];
|
|
801
|
+
const encoding = Array.isArray(encodingHeader) ? encodingHeader[0]?.toLowerCase() : typeof encodingHeader === 'string' ? encodingHeader.toLowerCase() : undefined;
|
|
802
|
+
if (encoding) {
|
|
803
|
+
try {
|
|
804
|
+
if (encoding.includes('gzip'))
|
|
805
|
+
body = await gunzip(body);
|
|
806
|
+
else if (encoding.includes('br'))
|
|
807
|
+
body = await brotliDecompress(body);
|
|
808
|
+
else if (encoding.includes('deflate'))
|
|
809
|
+
body = await inflate(body);
|
|
810
|
+
}
|
|
811
|
+
catch (e) {
|
|
812
|
+
console.error('Decompression error:', e);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
response.body = body;
|
|
817
|
+
try {
|
|
818
|
+
response.text = body.toString('utf-8');
|
|
819
|
+
const contentTypeHeader = response.headers['content-type'];
|
|
820
|
+
const ct = Array.isArray(contentTypeHeader) ? contentTypeHeader[0] : typeof contentTypeHeader === 'string' ? contentTypeHeader : undefined;
|
|
821
|
+
if (ct && ct.includes('application/json')) {
|
|
822
|
+
response.json = JSON.parse(response.text);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
catch { }
|
|
826
|
+
response.timing.end = Date.now() - timingStart;
|
|
827
|
+
response.timing.total = response.timing.end;
|
|
828
|
+
out.end();
|
|
829
|
+
if (callback)
|
|
830
|
+
callback(null, response, response.body);
|
|
831
|
+
});
|
|
832
|
+
stream.once('error', (err) => {
|
|
833
|
+
out.emit('err', err);
|
|
834
|
+
if (callback)
|
|
835
|
+
callback(err);
|
|
836
|
+
});
|
|
837
|
+
if (post_data) {
|
|
838
|
+
if (Buffer.isBuffer(post_data) || typeof post_data === 'string')
|
|
839
|
+
stream.end(post_data);
|
|
840
|
+
else if (post_data instanceof stream_1.Readable)
|
|
841
|
+
post_data.pipe(stream);
|
|
135
842
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
headers['cookie'] = headers['cookie'] ? `${headers['cookie']}; ${manualCookies}` : manualCookies;
|
|
843
|
+
else {
|
|
844
|
+
stream.end();
|
|
139
845
|
}
|
|
846
|
+
}
|
|
847
|
+
async requestHttp1(path, options, timingStart, headers, post_data, out, callback) {
|
|
140
848
|
const reqOptions = {
|
|
141
849
|
method: options.method?.toUpperCase() || 'GET',
|
|
142
|
-
|
|
143
|
-
|
|
850
|
+
path,
|
|
851
|
+
headers: {
|
|
852
|
+
host: this.hostname,
|
|
853
|
+
...headers
|
|
854
|
+
},
|
|
855
|
+
createConnection: () => this.tlsSocket
|
|
144
856
|
};
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
857
|
+
const req = https.request(reqOptions);
|
|
858
|
+
const response = new events_1.EventEmitter();
|
|
859
|
+
response.timing = { socket: timingStart, lookup: 0, connect: 0, secureConnect: 0, response: 0, end: 0, total: 0 };
|
|
860
|
+
response.fingerprints = {
|
|
861
|
+
ja3: this.generateJA3(),
|
|
862
|
+
ja3Hash: crypto.createHash('md5').update(this.generateJA3()).digest('hex'),
|
|
863
|
+
akamai: ''
|
|
864
|
+
};
|
|
865
|
+
req.once('response', (res) => {
|
|
866
|
+
response.statusCode = res.statusCode;
|
|
867
|
+
response.headers = res.headers;
|
|
868
|
+
if (res.headers['set-cookie'])
|
|
869
|
+
this.cookieJar.parseSetCookie(this.hostname, res.headers['set-cookie']);
|
|
870
|
+
response.timing.response = Date.now() - timingStart;
|
|
871
|
+
out.emit('header', response.statusCode, res.headers);
|
|
872
|
+
out.emit('headers', res.headers);
|
|
873
|
+
const chunks = [];
|
|
874
|
+
const rawChunks = [];
|
|
875
|
+
res.on('data', (chunk) => {
|
|
876
|
+
// Convert string to Buffer if needed
|
|
877
|
+
const bufferChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
878
|
+
rawChunks.push(bufferChunk);
|
|
879
|
+
chunks.push(bufferChunk);
|
|
880
|
+
out.write(bufferChunk);
|
|
881
|
+
});
|
|
882
|
+
res.once('end', async () => {
|
|
883
|
+
let body = Buffer.concat(chunks);
|
|
884
|
+
response.bytes = body.length;
|
|
885
|
+
response.raw = Buffer.concat(rawChunks);
|
|
886
|
+
if (options.decompress !== false) {
|
|
887
|
+
const encodingHeader = res.headers['content-encoding'];
|
|
888
|
+
const encoding = Array.isArray(encodingHeader) ? encodingHeader[0]?.toLowerCase() : typeof encodingHeader === 'string' ? encodingHeader.toLowerCase() : undefined;
|
|
889
|
+
if (encoding) {
|
|
890
|
+
try {
|
|
891
|
+
if (encoding.includes('gzip'))
|
|
892
|
+
body = await gunzip(body);
|
|
893
|
+
else if (encoding.includes('br'))
|
|
894
|
+
body = await brotliDecompress(body);
|
|
895
|
+
else if (encoding.includes('deflate'))
|
|
896
|
+
body = await inflate(body);
|
|
897
|
+
}
|
|
898
|
+
catch (e) {
|
|
899
|
+
console.error('Decompression error:', e);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
response.body = body;
|
|
904
|
+
try {
|
|
905
|
+
response.text = body.toString('utf-8');
|
|
906
|
+
const contentTypeHeader = res.headers['content-type'];
|
|
907
|
+
const ct = Array.isArray(contentTypeHeader) ? contentTypeHeader[0] : typeof contentTypeHeader === 'string' ? contentTypeHeader : undefined;
|
|
908
|
+
if (ct && ct.includes('application/json')) {
|
|
909
|
+
response.json = JSON.parse(response.text);
|
|
910
|
+
}
|
|
162
911
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
912
|
+
catch { }
|
|
913
|
+
response.timing.end = Date.now() - timingStart;
|
|
914
|
+
response.timing.total = response.timing.end;
|
|
915
|
+
out.end();
|
|
916
|
+
if (callback)
|
|
917
|
+
callback(null, response, response.body);
|
|
918
|
+
});
|
|
919
|
+
res.once('error', (err) => {
|
|
920
|
+
out.emit('err', err);
|
|
921
|
+
if (callback)
|
|
922
|
+
callback(err);
|
|
923
|
+
});
|
|
924
|
+
});
|
|
925
|
+
req.once('error', (err) => {
|
|
926
|
+
out.emit('err', err);
|
|
927
|
+
if (callback)
|
|
928
|
+
callback(err);
|
|
929
|
+
});
|
|
930
|
+
if (post_data) {
|
|
931
|
+
if (Buffer.isBuffer(post_data) || typeof post_data === 'string')
|
|
932
|
+
req.end(post_data);
|
|
933
|
+
else if (post_data instanceof stream_1.Readable)
|
|
934
|
+
post_data.pipe(req);
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
req.end();
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
generateJA3() {
|
|
941
|
+
const version = '771';
|
|
942
|
+
const ciphers = this.profile.tls.cipherSuites.filter(c => c < 0xff00).join('-');
|
|
943
|
+
const extensions = this.profile.tls.extensions.filter(e => typeof e === 'number' && e < 0xff00).join('-');
|
|
944
|
+
const curves = this.profile.tls.supportedGroups.filter(g => g < 0xff00).join('-');
|
|
945
|
+
const ecPoints = this.profile.tls.ecPointFormats.join('-');
|
|
946
|
+
return `${version},${ciphers},${extensions},${curves},${ecPoints}`;
|
|
947
|
+
}
|
|
948
|
+
destroy() {
|
|
949
|
+
if (this.http2Client && !this.http2Client.destroyed)
|
|
950
|
+
this.http2Client.destroy();
|
|
951
|
+
this.http2Client = null;
|
|
952
|
+
}
|
|
953
|
+
getProtocol() {
|
|
954
|
+
return this.negotiatedProtocol;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
class TLSSocketManager extends events_1.EventEmitter {
|
|
958
|
+
constructor(profile, proxy) {
|
|
959
|
+
super();
|
|
960
|
+
this.socket = null;
|
|
961
|
+
this.tlsSocket = null;
|
|
962
|
+
this.startTime = 0;
|
|
963
|
+
this.hostname = '';
|
|
964
|
+
this.port = 443;
|
|
965
|
+
this.profile = profile;
|
|
966
|
+
this.proxy = proxy;
|
|
967
|
+
this.timing = { socket: 0, lookup: 0, connect: 0, secureConnect: 0, response: 0, end: 0, total: 0 };
|
|
968
|
+
}
|
|
969
|
+
async connect(hostname, port = 443) {
|
|
970
|
+
this.hostname = hostname;
|
|
971
|
+
this.port = port;
|
|
972
|
+
this.startTime = Date.now();
|
|
973
|
+
return new Promise((resolve, reject) => {
|
|
974
|
+
const timeout = setTimeout(() => {
|
|
975
|
+
this.destroy();
|
|
976
|
+
reject(new Error('Connection timeout'));
|
|
977
|
+
}, 30000);
|
|
978
|
+
const handleError = (err) => {
|
|
979
|
+
clearTimeout(timeout);
|
|
980
|
+
this.destroy();
|
|
981
|
+
reject(err);
|
|
982
|
+
};
|
|
983
|
+
if (this.proxy) {
|
|
984
|
+
const proxyUrl = new url.URL(this.proxy);
|
|
985
|
+
this.socket = net.connect(+proxyUrl.port || 80, proxyUrl.hostname, () => {
|
|
986
|
+
let request = `CONNECT ${hostname}:${port} HTTP/1.1\r\nHost: ${hostname}:${port}\r\n`;
|
|
987
|
+
if (proxyUrl.username && proxyUrl.password) {
|
|
988
|
+
const auth = Buffer.from(`${decodeURIComponent(proxyUrl.username)}:${decodeURIComponent(proxyUrl.password)}`).toString('base64');
|
|
989
|
+
request += `Proxy-Authorization: Basic ${auth}\r\n`;
|
|
990
|
+
}
|
|
991
|
+
request += '\r\n';
|
|
992
|
+
this.socket.write(request);
|
|
993
|
+
});
|
|
994
|
+
let response = '';
|
|
995
|
+
this.socket.on('data', (chunk) => {
|
|
996
|
+
response += chunk.toString();
|
|
997
|
+
if (response.includes('\r\n\r\n')) {
|
|
998
|
+
if (!response.startsWith('HTTP/1.1 200'))
|
|
999
|
+
return reject(new Error('Proxy failed: ' + response.split('\r\n')[0]));
|
|
1000
|
+
this.proceedWithTLS(resolve, reject, timeout);
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
this.socket = new net.Socket();
|
|
1006
|
+
this.socket.setNoDelay(true);
|
|
1007
|
+
this.socket.setKeepAlive(true, 60000);
|
|
1008
|
+
this.socket.once('lookup', () => {
|
|
1009
|
+
this.timing.lookup = Date.now() - this.startTime;
|
|
1010
|
+
});
|
|
1011
|
+
this.socket.connect(port, hostname, () => {
|
|
1012
|
+
this.timing.connect = Date.now() - this.startTime;
|
|
1013
|
+
this.proceedWithTLS(resolve, reject, timeout);
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
this.socket.on('error', handleError);
|
|
1017
|
+
this.socket.on('timeout', () => handleError(new Error('Socket timeout')));
|
|
1018
|
+
});
|
|
1019
|
+
}
|
|
1020
|
+
proceedWithTLS(resolve, reject, timeout) {
|
|
1021
|
+
const validCiphers = [
|
|
1022
|
+
'TLS_AES_256_GCM_SHA384',
|
|
1023
|
+
'TLS_CHACHA20_POLY1305_SHA256',
|
|
1024
|
+
'TLS_AES_128_GCM_SHA256',
|
|
1025
|
+
'ECDHE-ECDSA-AES256-GCM-SHA384',
|
|
1026
|
+
'ECDHE-RSA-AES256-GCM-SHA384',
|
|
1027
|
+
'ECDHE-ECDSA-CHACHA20-POLY1305',
|
|
1028
|
+
'ECDHE-RSA-CHACHA20-POLY1305',
|
|
1029
|
+
'ECDHE-ECDSA-AES128-GCM-SHA256',
|
|
1030
|
+
'ECDHE-RSA-AES128-GCM-SHA256',
|
|
1031
|
+
'ECDHE-ECDSA-AES256-SHA',
|
|
1032
|
+
'ECDHE-RSA-AES256-SHA',
|
|
1033
|
+
'ECDHE-ECDSA-AES128-SHA',
|
|
1034
|
+
'ECDHE-RSA-AES128-SHA',
|
|
1035
|
+
'AES256-GCM-SHA384',
|
|
1036
|
+
'AES128-GCM-SHA256',
|
|
1037
|
+
'AES256-SHA',
|
|
1038
|
+
'AES128-SHA'
|
|
1039
|
+
].join(':');
|
|
1040
|
+
const tlsOptions = {
|
|
1041
|
+
socket: this.socket,
|
|
1042
|
+
servername: this.hostname,
|
|
1043
|
+
ALPNProtocols: this.profile.tls.alpnProtocols,
|
|
1044
|
+
ciphers: validCiphers,
|
|
1045
|
+
minVersion: 'TLSv1.2',
|
|
1046
|
+
maxVersion: 'TLSv1.3',
|
|
1047
|
+
rejectUnauthorized: false,
|
|
1048
|
+
requestCert: false,
|
|
1049
|
+
honorCipherOrder: true,
|
|
1050
|
+
sessionTimeout: 300
|
|
1051
|
+
};
|
|
1052
|
+
this.tlsSocket = tls.connect(tlsOptions);
|
|
1053
|
+
this.tlsSocket.once('secureConnect', () => {
|
|
1054
|
+
clearTimeout(timeout);
|
|
1055
|
+
this.timing.secureConnect = Date.now() - this.startTime;
|
|
1056
|
+
this.timing.socket = this.startTime;
|
|
1057
|
+
resolve(this.tlsSocket);
|
|
1058
|
+
});
|
|
1059
|
+
this.tlsSocket.on('error', (err) => {
|
|
1060
|
+
clearTimeout(timeout);
|
|
1061
|
+
this.destroy();
|
|
1062
|
+
reject(err);
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
getTiming() {
|
|
1066
|
+
return { ...this.timing, total: Date.now() - this.startTime };
|
|
1067
|
+
}
|
|
1068
|
+
destroy() {
|
|
1069
|
+
this.tlsSocket?.destroy();
|
|
1070
|
+
this.socket?.destroy();
|
|
1071
|
+
this.tlsSocket = null;
|
|
1072
|
+
this.socket = null;
|
|
1073
|
+
}
|
|
1074
|
+
getSocket() {
|
|
1075
|
+
return this.tlsSocket;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
class AdvancedTLSClient {
|
|
1079
|
+
constructor(profileName) {
|
|
1080
|
+
this.sessionCache = new Map();
|
|
1081
|
+
this.cookieJar = new CookieJar();
|
|
1082
|
+
this.maxCachedSessions = 10;
|
|
1083
|
+
this.sessionTimeout = 300000;
|
|
1084
|
+
this.defaults = {
|
|
1085
|
+
boundary: '--------------------SIPUTZXCOMPANY',
|
|
1086
|
+
encoding: 'utf8',
|
|
1087
|
+
parse_response: 'all',
|
|
1088
|
+
proxy: null,
|
|
1089
|
+
agent: null,
|
|
1090
|
+
headers: {},
|
|
1091
|
+
accept: '*/*',
|
|
1092
|
+
user_agent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
|
|
1093
|
+
open_timeout: 10000,
|
|
1094
|
+
response_timeout: 0,
|
|
1095
|
+
read_timeout: 0,
|
|
1096
|
+
follow_max: 0,
|
|
1097
|
+
stream_length: -1,
|
|
1098
|
+
signal: null,
|
|
1099
|
+
compressed: false,
|
|
1100
|
+
decode_response: true,
|
|
1101
|
+
parse_cookies: true,
|
|
1102
|
+
follow_set_cookies: false,
|
|
1103
|
+
follow_set_referer: false,
|
|
1104
|
+
follow_keep_method: false,
|
|
1105
|
+
follow_if_same_host: false,
|
|
1106
|
+
follow_if_same_protocol: false,
|
|
1107
|
+
follow_if_same_location: false,
|
|
1108
|
+
use_proxy_from_env_var: true
|
|
1109
|
+
};
|
|
1110
|
+
this.profile = ProfileRegistry.get(profileName || 'chrome_133');
|
|
1111
|
+
this.cleanupInterval = setInterval(() => {
|
|
1112
|
+
const now = Date.now();
|
|
1113
|
+
for (const [key, session] of this.sessionCache) {
|
|
1114
|
+
if (now - session.lastUsed > this.sessionTimeout) {
|
|
1115
|
+
session.clientManager.destroy();
|
|
1116
|
+
session.tlsManager.destroy();
|
|
1117
|
+
this.sessionCache.delete(key);
|
|
169
1118
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
1119
|
+
}
|
|
1120
|
+
}, 60000);
|
|
1121
|
+
}
|
|
1122
|
+
setup(uri, options) {
|
|
1123
|
+
const config = {
|
|
1124
|
+
headers: { ...options.headers },
|
|
1125
|
+
proxy: options.proxy || this.defaults.proxy,
|
|
1126
|
+
decompress: options.decompress !== undefined ? options.decompress : true
|
|
1127
|
+
};
|
|
1128
|
+
config.headers['user-agent'] = this.profile.userAgent;
|
|
1129
|
+
config.headers['accept-language'] = this.profile.acceptLanguage || 'en-US,en;q=0.9';
|
|
1130
|
+
config.headers['accept-encoding'] = 'gzip, deflate, br';
|
|
1131
|
+
config.headers['accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8';
|
|
1132
|
+
config.headers['sec-ch-ua'] = this.profile.secChUa;
|
|
1133
|
+
config.headers['sec-ch-ua-mobile'] = this.profile.secChUaMobile;
|
|
1134
|
+
config.headers['sec-ch-ua-platform'] = this.profile.secChUaPlatform;
|
|
1135
|
+
config.headers['sec-fetch-site'] = this.profile.secFetchSite;
|
|
1136
|
+
config.headers['sec-fetch-mode'] = this.profile.secFetchMode;
|
|
1137
|
+
config.headers['sec-fetch-dest'] = this.profile.secFetchDest;
|
|
1138
|
+
if (this.profile.upgradeInsecureRequests)
|
|
1139
|
+
config.headers['upgrade-insecure-requests'] = this.profile.upgradeInsecureRequests;
|
|
1140
|
+
if (this.profile.priority)
|
|
1141
|
+
config.headers['priority'] = this.profile.priority;
|
|
1142
|
+
return config;
|
|
1143
|
+
}
|
|
1144
|
+
async request(uri, data, options = {}, callback) {
|
|
1145
|
+
if (typeof options === 'function') {
|
|
1146
|
+
callback = options;
|
|
1147
|
+
options = {};
|
|
1148
|
+
}
|
|
1149
|
+
const parsed = new url.URL(uri);
|
|
1150
|
+
const hostname = parsed.hostname;
|
|
1151
|
+
const port = parsed.port ? +parsed.port : 443;
|
|
1152
|
+
const path = parsed.pathname + parsed.search;
|
|
1153
|
+
const cacheKey = `${hostname}:${port}`;
|
|
1154
|
+
const startTime = Date.now();
|
|
1155
|
+
const out = new stream_1.PassThrough();
|
|
1156
|
+
let session = this.sessionCache.get(cacheKey);
|
|
1157
|
+
if (!session || session.tlsManager.getSocket()?.destroyed) {
|
|
1158
|
+
const tlsManager = new TLSSocketManager(this.profile, options.proxy);
|
|
1159
|
+
const tlsSocket = await tlsManager.connect(hostname, port);
|
|
1160
|
+
const clientManager = new UnifiedClientManager(tlsSocket, this.profile, hostname, this.cookieJar);
|
|
1161
|
+
await clientManager.initialize();
|
|
1162
|
+
session = { tlsManager, clientManager, lastUsed: Date.now() };
|
|
1163
|
+
this.sessionCache.set(cacheKey, session);
|
|
1164
|
+
if (this.sessionCache.size > this.maxCachedSessions) {
|
|
1165
|
+
const oldest = [...this.sessionCache.entries()].sort((a, b) => a[1].lastUsed - b[1].lastUsed)[0];
|
|
1166
|
+
oldest[1].clientManager.destroy();
|
|
1167
|
+
oldest[1].tlsManager.destroy();
|
|
1168
|
+
this.sessionCache.delete(oldest[0]);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
session.lastUsed = Date.now();
|
|
1172
|
+
const config = this.setup(uri, options);
|
|
1173
|
+
let post_data = null;
|
|
1174
|
+
let json = options.json || (options.json !== false && config.headers['content-type']?.includes('application/json'));
|
|
1175
|
+
if (data) {
|
|
1176
|
+
if (options.multipart) {
|
|
1177
|
+
const boundary = options.boundary || this.defaults.boundary;
|
|
1178
|
+
post_data = await this.buildMultipart(data, boundary);
|
|
1179
|
+
config.headers['content-type'] = 'multipart/form-data; boundary=' + boundary;
|
|
1180
|
+
}
|
|
1181
|
+
else if (data instanceof stream_1.Readable) {
|
|
1182
|
+
post_data = data;
|
|
1183
|
+
}
|
|
1184
|
+
else if (Buffer.isBuffer(data)) {
|
|
1185
|
+
post_data = data;
|
|
1186
|
+
}
|
|
1187
|
+
else if (typeof data === 'string') {
|
|
1188
|
+
post_data = data;
|
|
1189
|
+
}
|
|
1190
|
+
else if (json) {
|
|
1191
|
+
post_data = JSON.stringify(data);
|
|
1192
|
+
config.headers['content-type'] = 'application/json; charset=utf-8';
|
|
1193
|
+
}
|
|
1194
|
+
else {
|
|
1195
|
+
post_data = new URLSearchParams(data).toString();
|
|
1196
|
+
config.headers['content-type'] = 'application/x-www-form-urlencoded';
|
|
1197
|
+
}
|
|
1198
|
+
if (post_data && (typeof post_data === 'string' || Buffer.isBuffer(post_data))) {
|
|
1199
|
+
config.headers['content-length'] = Buffer.byteLength(post_data).toString();
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
const cookies = this.cookieJar.getCookies(hostname, parsed.pathname, parsed.protocol === 'https:');
|
|
1203
|
+
if (cookies)
|
|
1204
|
+
config.headers['cookie'] = cookies;
|
|
1205
|
+
// MODE LEGACY: Jika ada callback, kembalikan stream
|
|
1206
|
+
if (callback) {
|
|
1207
|
+
await session.clientManager.request(path, options, startTime, config.headers, post_data, out, callback);
|
|
1208
|
+
return out;
|
|
1209
|
+
}
|
|
1210
|
+
// MODE BARU: Kembalikan object response lengkap
|
|
1211
|
+
return new Promise((resolve, reject) => {
|
|
1212
|
+
let statusCode = 0;
|
|
1213
|
+
let headers = {};
|
|
1214
|
+
const chunks = [];
|
|
1215
|
+
out.on('header', (status, hdrs) => {
|
|
1216
|
+
statusCode = status;
|
|
1217
|
+
headers = hdrs;
|
|
1218
|
+
});
|
|
1219
|
+
out.on('headers', (hdrs) => {
|
|
1220
|
+
headers = hdrs;
|
|
1221
|
+
});
|
|
1222
|
+
out.on('data', (chunk) => {
|
|
1223
|
+
chunks.push(chunk);
|
|
1224
|
+
});
|
|
1225
|
+
out.once('end', async () => {
|
|
1226
|
+
let body = Buffer.concat(chunks);
|
|
1227
|
+
// Auto-decompress jika diminta
|
|
1228
|
+
if (options.decompress !== false && headers) {
|
|
1229
|
+
const encodingHeader = headers['content-encoding'];
|
|
1230
|
+
const encoding = Array.isArray(encodingHeader)
|
|
1231
|
+
? encodingHeader[0]?.toLowerCase()
|
|
1232
|
+
: typeof encodingHeader === 'string'
|
|
1233
|
+
? encodingHeader.toLowerCase()
|
|
1234
|
+
: undefined;
|
|
1235
|
+
if (encoding) {
|
|
176
1236
|
try {
|
|
177
1237
|
if (encoding.includes('gzip'))
|
|
178
1238
|
body = await gunzip(body);
|
|
@@ -181,55 +1241,142 @@ class FastProxiedClient {
|
|
|
181
1241
|
else if (encoding.includes('deflate'))
|
|
182
1242
|
body = await inflate(body);
|
|
183
1243
|
}
|
|
184
|
-
catch (e) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
try {
|
|
188
|
-
response.text = body.toString('utf-8');
|
|
1244
|
+
catch (e) {
|
|
1245
|
+
console.error('Decompression failed:', e);
|
|
1246
|
+
}
|
|
189
1247
|
}
|
|
190
|
-
|
|
1248
|
+
}
|
|
1249
|
+
const text = body.toString('utf-8');
|
|
1250
|
+
let parsedJson = undefined;
|
|
1251
|
+
const contentType = headers['content-type'];
|
|
1252
|
+
const ct = Array.isArray(contentType)
|
|
1253
|
+
? contentType[0]
|
|
1254
|
+
: typeof contentType === 'string'
|
|
1255
|
+
? contentType
|
|
1256
|
+
: undefined;
|
|
1257
|
+
if (ct && ct.includes('application/json')) {
|
|
191
1258
|
try {
|
|
192
|
-
|
|
193
|
-
response.json = JSON.parse(response.text);
|
|
194
|
-
}
|
|
1259
|
+
parsedJson = JSON.parse(text);
|
|
195
1260
|
}
|
|
196
1261
|
catch { }
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
1262
|
+
}
|
|
1263
|
+
// Generate fingerprints
|
|
1264
|
+
const ja3 = this.generateJA3();
|
|
1265
|
+
const ja3Hash = crypto.createHash('md5').update(ja3).digest('hex');
|
|
1266
|
+
const akamai = `1:${this.profile.http2.headerTableSize};2:${this.profile.http2.enablePush};4:${this.profile.http2.initialWindowSize};6:${this.profile.http2.maxHeaderListSize ?? ''}|00|0|m,a,s,p`;
|
|
1267
|
+
const fullResponse = {
|
|
1268
|
+
statusCode,
|
|
1269
|
+
headers,
|
|
1270
|
+
body,
|
|
1271
|
+
text,
|
|
1272
|
+
json: parsedJson,
|
|
1273
|
+
fingerprints: {
|
|
1274
|
+
ja3,
|
|
1275
|
+
ja3Hash,
|
|
1276
|
+
akamai
|
|
1277
|
+
},
|
|
1278
|
+
timing: {
|
|
1279
|
+
socket: startTime,
|
|
1280
|
+
lookup: 0,
|
|
1281
|
+
connect: 0,
|
|
1282
|
+
secureConnect: 0,
|
|
1283
|
+
response: Date.now() - startTime,
|
|
1284
|
+
end: Date.now() - startTime,
|
|
1285
|
+
total: Date.now() - startTime
|
|
208
1286
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
res.on('error', reject);
|
|
212
|
-
});
|
|
213
|
-
req.on('error', reject);
|
|
214
|
-
req.on('timeout', () => {
|
|
215
|
-
req.destroy();
|
|
216
|
-
reject(new Error('Request timeout'));
|
|
1287
|
+
};
|
|
1288
|
+
resolve(fullResponse);
|
|
217
1289
|
});
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
1290
|
+
out.once('error', (err) => reject(err));
|
|
1291
|
+
// Jalankan request
|
|
1292
|
+
session.clientManager.request(path, options, startTime, config.headers, post_data, out).catch(reject);
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
generateJA3() {
|
|
1296
|
+
const version = '771';
|
|
1297
|
+
const ciphers = this.profile.tls.cipherSuites.filter(c => c < 0xff00).join('-');
|
|
1298
|
+
const extensions = this.profile.tls.extensions.filter(e => typeof e === 'number' && e < 0xff00).join('-');
|
|
1299
|
+
const curves = this.profile.tls.supportedGroups.filter(g => g < 0xff00).join('-');
|
|
1300
|
+
const ecPoints = this.profile.tls.ecPointFormats.join('-');
|
|
1301
|
+
return `${version},${ciphers},${extensions},${curves},${ecPoints}`;
|
|
1302
|
+
}
|
|
1303
|
+
async buildMultipart(data, boundary) {
|
|
1304
|
+
return new Promise((resolve, reject) => {
|
|
1305
|
+
let body = '';
|
|
1306
|
+
const object = this.flatten(data);
|
|
1307
|
+
const count = Object.keys(object).length;
|
|
1308
|
+
if (count === 0)
|
|
1309
|
+
return reject(new Error('Empty multipart body'));
|
|
1310
|
+
let doneCount = count;
|
|
1311
|
+
const done = () => {
|
|
1312
|
+
if (--doneCount === 0)
|
|
1313
|
+
resolve(Buffer.from(body + '--' + boundary + '--\r\n'));
|
|
1314
|
+
};
|
|
1315
|
+
for (const key in object) {
|
|
1316
|
+
const value = object[key];
|
|
1317
|
+
if (value === null || typeof value === 'undefined') {
|
|
1318
|
+
done();
|
|
1319
|
+
continue;
|
|
221
1320
|
}
|
|
222
|
-
|
|
223
|
-
|
|
1321
|
+
if (Buffer.isBuffer(value)) {
|
|
1322
|
+
const part = { buffer: value, content_type: 'application/octet-stream' };
|
|
1323
|
+
this.generateMultipart(key, part, boundary).then(section => {
|
|
1324
|
+
body += section;
|
|
1325
|
+
done();
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
else {
|
|
1329
|
+
const part = (value.buffer || value.file || value.content_type) ? value : { value: value };
|
|
1330
|
+
this.generateMultipart(key, part, boundary).then(section => {
|
|
1331
|
+
body += section;
|
|
1332
|
+
done();
|
|
1333
|
+
});
|
|
224
1334
|
}
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
req.end();
|
|
228
1335
|
}
|
|
229
1336
|
});
|
|
230
1337
|
}
|
|
1338
|
+
async generateMultipart(name, part, boundary) {
|
|
1339
|
+
let return_part = '--' + boundary + '\r\n';
|
|
1340
|
+
return_part += 'Content-Disposition: form-data; name="' + name + '"';
|
|
1341
|
+
const append = (data, filename) => {
|
|
1342
|
+
if (data) {
|
|
1343
|
+
return_part += '; filename="' + encodeURIComponent(filename) + '"\r\n';
|
|
1344
|
+
return_part += 'Content-Type: ' + (part.content_type || 'application/octet-stream') + '\r\n\r\n';
|
|
1345
|
+
return_part += data.toString('binary');
|
|
1346
|
+
}
|
|
1347
|
+
return return_part + '\r\n';
|
|
1348
|
+
};
|
|
1349
|
+
if ((part.file || part.buffer) && part.content_type) {
|
|
1350
|
+
const filename = part.filename || (part.file ? path.basename(part.file) : name);
|
|
1351
|
+
if (part.buffer)
|
|
1352
|
+
return append(part.buffer, filename);
|
|
1353
|
+
const data = await fs.promises.readFile(part.file);
|
|
1354
|
+
return append(data, filename);
|
|
1355
|
+
}
|
|
1356
|
+
else {
|
|
1357
|
+
return_part += '\r\n\r\n';
|
|
1358
|
+
return_part += String(part.value || '');
|
|
1359
|
+
return return_part + '\r\n';
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
flatten(object, into = {}, prefix) {
|
|
1363
|
+
for (const key in object) {
|
|
1364
|
+
const prefix_key = prefix ? prefix + '[' + key + ']' : key;
|
|
1365
|
+
const prop = object[key];
|
|
1366
|
+
if (prop && typeof prop === 'object' && !(prop.buffer || prop.file || prop.content_type))
|
|
1367
|
+
this.flatten(prop, into, prefix_key);
|
|
1368
|
+
else
|
|
1369
|
+
into[prefix_key] = prop;
|
|
1370
|
+
}
|
|
1371
|
+
return into;
|
|
1372
|
+
}
|
|
231
1373
|
destroy() {
|
|
232
|
-
this.
|
|
1374
|
+
clearInterval(this.cleanupInterval);
|
|
1375
|
+
this.sessionCache.forEach(s => {
|
|
1376
|
+
s.clientManager.destroy();
|
|
1377
|
+
s.tlsManager.destroy();
|
|
1378
|
+
});
|
|
1379
|
+
this.sessionCache.clear();
|
|
233
1380
|
}
|
|
234
1381
|
}
|
|
235
|
-
exports.
|
|
1382
|
+
exports.AdvancedTLSClient = AdvancedTLSClient;
|