advanced-tls-client 1.0.0 → 1.0.2

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.js ADDED
@@ -0,0 +1,1037 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ProfileRegistry = exports.AdvancedTLSClient = void 0;
37
+ const tls = __importStar(require("tls"));
38
+ const net = __importStar(require("net"));
39
+ const http2 = __importStar(require("http2"));
40
+ const crypto = __importStar(require("crypto"));
41
+ const events_1 = require("events");
42
+ const zlib = __importStar(require("zlib"));
43
+ const util_1 = require("util");
44
+ const stream_1 = require("stream");
45
+ const gunzip = (0, util_1.promisify)(zlib.gunzip);
46
+ const brotliDecompress = (0, util_1.promisify)(zlib.brotliDecompress);
47
+ const inflate = (0, util_1.promisify)(zlib.inflate);
48
+ const GREASE_VALUES = [0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a, 0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa];
49
+ class ProfileRegistry {
50
+ static getGrease() {
51
+ return GREASE_VALUES[Math.floor(Math.random() * GREASE_VALUES.length)];
52
+ }
53
+ static chromeMobile143Android() {
54
+ const grease1 = this.getGrease();
55
+ const grease2 = this.getGrease();
56
+ const grease3 = this.getGrease();
57
+ const grease4 = this.getGrease();
58
+ return {
59
+ name: 'chrome_mobile_143_android',
60
+ version: '143.0.0.0',
61
+ tls: {
62
+ cipherSuites: [
63
+ grease1,
64
+ 4865,
65
+ 4866,
66
+ 4867,
67
+ 49195,
68
+ 49199,
69
+ 49196,
70
+ 49200,
71
+ 52393,
72
+ 52392,
73
+ 49171,
74
+ 49172,
75
+ 156,
76
+ 157,
77
+ 47,
78
+ 53
79
+ ],
80
+ extensions: [
81
+ grease2,
82
+ 65037,
83
+ 27,
84
+ 43,
85
+ 16,
86
+ 35,
87
+ 45,
88
+ 11,
89
+ 65281,
90
+ 5,
91
+ 10,
92
+ 23,
93
+ 0,
94
+ 17513,
95
+ 51,
96
+ 18,
97
+ 13,
98
+ grease3
99
+ ],
100
+ supportedGroups: [
101
+ grease4,
102
+ 25497,
103
+ 29,
104
+ 23,
105
+ 24
106
+ ],
107
+ signatureAlgorithms: [
108
+ 1027,
109
+ 2052,
110
+ 1025,
111
+ 1283,
112
+ 2053,
113
+ 1281,
114
+ 2054,
115
+ 1537
116
+ ],
117
+ supportedVersions: [
118
+ grease2,
119
+ 772,
120
+ 771
121
+ ],
122
+ ecPointFormats: [0],
123
+ alpnProtocols: ['h2', 'http/1.1'],
124
+ pskKeyExchangeModes: [1],
125
+ compressionMethods: [0],
126
+ grease: true,
127
+ recordSizeLimit: 16385,
128
+ certificateCompression: [2],
129
+ ocspStapling: true,
130
+ signedCertificateTimestamp: true
131
+ },
132
+ http2: {
133
+ windowUpdate: 15663105,
134
+ headerTableSize: 65536,
135
+ enablePush: 0,
136
+ initialWindowSize: 6291456,
137
+ maxHeaderListSize: 262144,
138
+ maxFrameSize: 16384,
139
+ settingsOrder: [1, 2, 4, 6],
140
+ connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
141
+ priorityFrames: [
142
+ {
143
+ streamId: 0,
144
+ weight: 256,
145
+ dependency: 0,
146
+ exclusive: true
147
+ }
148
+ ],
149
+ pseudoHeaderOrder: [':method', ':authority', ':scheme', ':path'],
150
+ headerOrder: [
151
+ 'sec-ch-ua',
152
+ 'sec-ch-ua-mobile',
153
+ 'sec-ch-ua-platform',
154
+ 'upgrade-insecure-requests',
155
+ 'user-agent',
156
+ 'accept',
157
+ 'sec-fetch-site',
158
+ 'sec-fetch-mode',
159
+ 'sec-fetch-dest',
160
+ 'accept-encoding',
161
+ 'accept-language',
162
+ 'priority'
163
+ ]
164
+ },
165
+ userAgent: 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36',
166
+ secChUa: '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
167
+ secChUaPlatform: '"Android"',
168
+ secChUaMobile: '?1',
169
+ secFetchSite: 'none',
170
+ secFetchMode: 'navigate',
171
+ secFetchDest: 'document',
172
+ upgradeInsecureRequests: '1',
173
+ 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',
174
+ priority: 'u=0, i'
175
+ };
176
+ }
177
+ static chrome133PSK() {
178
+ const grease1 = this.getGrease();
179
+ const grease2 = this.getGrease();
180
+ const grease3 = this.getGrease();
181
+ const grease4 = this.getGrease();
182
+ return {
183
+ name: 'chrome_133_psk',
184
+ version: '133.0.0.0',
185
+ tls: {
186
+ cipherSuites: [
187
+ grease1,
188
+ 4865,
189
+ 4866,
190
+ 4867,
191
+ 49195,
192
+ 49199,
193
+ 49196,
194
+ 49200,
195
+ 52393,
196
+ 52392,
197
+ 49171,
198
+ 49172,
199
+ 156,
200
+ 157,
201
+ 47,
202
+ 53
203
+ ],
204
+ extensions: [
205
+ grease2,
206
+ 0,
207
+ 23,
208
+ 65281,
209
+ 10,
210
+ 11,
211
+ 35,
212
+ 16,
213
+ 5,
214
+ 13,
215
+ 18,
216
+ 51,
217
+ 45,
218
+ 43,
219
+ 27,
220
+ 21,
221
+ grease3
222
+ ],
223
+ supportedGroups: [
224
+ grease4,
225
+ 25497,
226
+ 29,
227
+ 23,
228
+ 24
229
+ ],
230
+ signatureAlgorithms: [
231
+ 1027,
232
+ 2052,
233
+ 1025,
234
+ 1283,
235
+ 2053,
236
+ 1281,
237
+ 2054,
238
+ 1537
239
+ ],
240
+ supportedVersions: [
241
+ grease2,
242
+ 772,
243
+ 771
244
+ ],
245
+ ecPointFormats: [0],
246
+ alpnProtocols: ['h2', 'http/1.1'],
247
+ pskKeyExchangeModes: [1],
248
+ compressionMethods: [0],
249
+ grease: true,
250
+ recordSizeLimit: 16385
251
+ },
252
+ http2: {
253
+ windowUpdate: 15663105,
254
+ headerTableSize: 65536,
255
+ enablePush: 0,
256
+ initialWindowSize: 6291456,
257
+ maxFrameSize: 16384,
258
+ maxHeaderListSize: 262144,
259
+ settingsOrder: [1, 2, 4, 6],
260
+ connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
261
+ priorityFrames: [
262
+ {
263
+ streamId: 0,
264
+ weight: 256,
265
+ dependency: 0,
266
+ exclusive: true
267
+ }
268
+ ],
269
+ pseudoHeaderOrder: [':method', ':authority', ':scheme', ':path'],
270
+ headerOrder: [
271
+ 'cache-control',
272
+ 'sec-ch-ua',
273
+ 'sec-ch-ua-mobile',
274
+ 'sec-ch-ua-platform',
275
+ 'upgrade-insecure-requests',
276
+ 'user-agent',
277
+ 'accept',
278
+ 'sec-fetch-site',
279
+ 'sec-fetch-mode',
280
+ 'sec-fetch-user',
281
+ 'sec-fetch-dest',
282
+ 'accept-encoding',
283
+ 'accept-language'
284
+ ]
285
+ },
286
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
287
+ secChUa: '"Google Chrome";v="133", "Chromium";v="133", "Not_A Brand";v="24"',
288
+ secChUaPlatform: '"Windows"',
289
+ secChUaMobile: '?0',
290
+ secFetchSite: 'none',
291
+ secFetchMode: 'navigate',
292
+ secFetchDest: 'document'
293
+ };
294
+ }
295
+ static chrome133() {
296
+ const grease1 = this.getGrease();
297
+ const grease2 = this.getGrease();
298
+ const grease3 = this.getGrease();
299
+ const grease4 = this.getGrease();
300
+ return {
301
+ name: 'chrome_133',
302
+ version: '133.0.0.0',
303
+ tls: {
304
+ cipherSuites: [
305
+ grease1,
306
+ 4865,
307
+ 4866,
308
+ 4867,
309
+ 49195,
310
+ 49199,
311
+ 49196,
312
+ 49200,
313
+ 52393,
314
+ 52392,
315
+ 49171,
316
+ 49172,
317
+ 156,
318
+ 157,
319
+ 47,
320
+ 53
321
+ ],
322
+ extensions: [
323
+ grease2,
324
+ 0,
325
+ 23,
326
+ 65281,
327
+ 10,
328
+ 11,
329
+ 35,
330
+ 16,
331
+ 5,
332
+ 13,
333
+ 18,
334
+ 51,
335
+ 45,
336
+ 43,
337
+ 27,
338
+ 21,
339
+ grease3
340
+ ],
341
+ supportedGroups: [
342
+ grease4,
343
+ 25497,
344
+ 29,
345
+ 23,
346
+ 24
347
+ ],
348
+ signatureAlgorithms: [
349
+ 1027,
350
+ 2052,
351
+ 1025,
352
+ 1283,
353
+ 2053,
354
+ 1281,
355
+ 2054,
356
+ 1537
357
+ ],
358
+ supportedVersions: [
359
+ grease2,
360
+ 772,
361
+ 771
362
+ ],
363
+ ecPointFormats: [0],
364
+ alpnProtocols: ['h2', 'http/1.1'],
365
+ pskKeyExchangeModes: [1],
366
+ compressionMethods: [0],
367
+ grease: true,
368
+ recordSizeLimit: 16385
369
+ },
370
+ http2: {
371
+ windowUpdate: 15663105,
372
+ headerTableSize: 65536,
373
+ enablePush: 0,
374
+ initialWindowSize: 6291456,
375
+ maxFrameSize: 16384,
376
+ maxHeaderListSize: 262144,
377
+ settingsOrder: [1, 2, 4, 6],
378
+ connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
379
+ priorityFrames: [
380
+ {
381
+ streamId: 0,
382
+ weight: 256,
383
+ dependency: 0,
384
+ exclusive: true
385
+ }
386
+ ],
387
+ pseudoHeaderOrder: [':method', ':authority', ':scheme', ':path'],
388
+ headerOrder: [
389
+ 'cache-control',
390
+ 'sec-ch-ua',
391
+ 'sec-ch-ua-mobile',
392
+ 'sec-ch-ua-platform',
393
+ 'upgrade-insecure-requests',
394
+ 'user-agent',
395
+ 'accept',
396
+ 'sec-fetch-site',
397
+ 'sec-fetch-mode',
398
+ 'sec-fetch-user',
399
+ 'sec-fetch-dest',
400
+ 'accept-encoding',
401
+ 'accept-language'
402
+ ]
403
+ },
404
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
405
+ secChUa: '"Google Chrome";v="133", "Chromium";v="133", "Not_A Brand";v="24"',
406
+ secChUaPlatform: '"Windows"',
407
+ secChUaMobile: '?0',
408
+ secFetchSite: 'none',
409
+ secFetchMode: 'navigate',
410
+ secFetchDest: 'document'
411
+ };
412
+ }
413
+ static firefox117() {
414
+ return {
415
+ name: 'firefox_117',
416
+ version: '117.0',
417
+ tls: {
418
+ cipherSuites: [
419
+ 4865,
420
+ 4867,
421
+ 4866,
422
+ 49195,
423
+ 49199,
424
+ 52393,
425
+ 52392,
426
+ 49196,
427
+ 49200,
428
+ 159,
429
+ 158,
430
+ 49161,
431
+ 49162,
432
+ 156,
433
+ 157,
434
+ 47,
435
+ 53
436
+ ],
437
+ extensions: [0, 23, 65281, 10, 11, 35, 16, 5, 34, 51, 43, 13, 45, 28, 21],
438
+ supportedGroups: [
439
+ 29,
440
+ 23,
441
+ 24,
442
+ 25,
443
+ 256,
444
+ 257
445
+ ],
446
+ signatureAlgorithms: [
447
+ 1027,
448
+ 1283,
449
+ 1539,
450
+ 2052,
451
+ 2053,
452
+ 2054,
453
+ 1025,
454
+ 1281,
455
+ 1537,
456
+ 513,
457
+ 515
458
+ ],
459
+ supportedVersions: [
460
+ 772,
461
+ 771
462
+ ],
463
+ ecPointFormats: [0],
464
+ alpnProtocols: ['h2', 'http/1.1'],
465
+ pskKeyExchangeModes: [1],
466
+ compressionMethods: [0],
467
+ grease: false
468
+ },
469
+ http2: {
470
+ windowUpdate: 12517377,
471
+ headerTableSize: 65536,
472
+ enablePush: 0,
473
+ initialWindowSize: 131072,
474
+ maxFrameSize: 16384,
475
+ settingsOrder: [1, 4, 5],
476
+ connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
477
+ priorityFrames: [
478
+ { streamId: 3, weight: 200, dependency: 0, exclusive: false },
479
+ { streamId: 5, weight: 100, dependency: 0, exclusive: false },
480
+ { streamId: 7, weight: 0, dependency: 0, exclusive: false },
481
+ { streamId: 9, weight: 0, dependency: 7, exclusive: false },
482
+ { streamId: 11, weight: 0, dependency: 3, exclusive: false },
483
+ { streamId: 13, weight: 240, dependency: 0, exclusive: false }
484
+ ],
485
+ pseudoHeaderOrder: [':method', ':path', ':authority', ':scheme'],
486
+ headerPriority: { streamDep: 13, exclusive: false, weight: 41 },
487
+ headerOrder: []
488
+ },
489
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:117.0) Gecko/20100101 Firefox/117.0',
490
+ secChUa: '',
491
+ secChUaPlatform: '',
492
+ secChUaMobile: '',
493
+ secFetchSite: 'none',
494
+ secFetchMode: 'navigate',
495
+ secFetchDest: 'document'
496
+ };
497
+ }
498
+ static safariIOS18() {
499
+ const grease1 = this.getGrease();
500
+ const grease2 = this.getGrease();
501
+ return {
502
+ name: 'safari_ios_18_0',
503
+ version: '18.0',
504
+ tls: {
505
+ cipherSuites: [
506
+ grease1,
507
+ 4865,
508
+ 4866,
509
+ 4867,
510
+ 49196,
511
+ 49195,
512
+ 52393,
513
+ 49200,
514
+ 49199,
515
+ 52392,
516
+ 159,
517
+ 158,
518
+ 49162,
519
+ 49161,
520
+ 157,
521
+ 156,
522
+ 53,
523
+ 47,
524
+ 49188,
525
+ 49187,
526
+ 60
527
+ ],
528
+ extensions: [
529
+ grease2,
530
+ 0,
531
+ 23,
532
+ 65281,
533
+ 10,
534
+ 11,
535
+ 16,
536
+ 5,
537
+ 13,
538
+ 18,
539
+ 51,
540
+ 45,
541
+ 43,
542
+ 21
543
+ ],
544
+ supportedGroups: [
545
+ 29,
546
+ 23,
547
+ 24,
548
+ 25
549
+ ],
550
+ signatureAlgorithms: [
551
+ 1027,
552
+ 2052,
553
+ 1025,
554
+ 1283,
555
+ 513,
556
+ 2053,
557
+ 2053,
558
+ 1281,
559
+ 2054,
560
+ 1537,
561
+ 515
562
+ ],
563
+ supportedVersions: [
564
+ 772,
565
+ 771,
566
+ 770,
567
+ 769
568
+ ],
569
+ ecPointFormats: [0],
570
+ alpnProtocols: ['h2', 'http/1.1'],
571
+ pskKeyExchangeModes: [1],
572
+ compressionMethods: [0],
573
+ grease: true
574
+ },
575
+ http2: {
576
+ windowUpdate: 10420225,
577
+ headerTableSize: 4096,
578
+ enablePush: 0,
579
+ maxConcurrentStreams: 100,
580
+ initialWindowSize: 2097152,
581
+ maxFrameSize: 16384,
582
+ settingsOrder: [2, 3, 4, 8, 9],
583
+ connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
584
+ priorityFrames: [],
585
+ pseudoHeaderOrder: [':method', ':scheme', ':authority', ':path'],
586
+ headerOrder: []
587
+ },
588
+ 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',
589
+ secChUa: '',
590
+ secChUaPlatform: '',
591
+ secChUaMobile: '?1',
592
+ secFetchSite: 'none',
593
+ secFetchMode: 'navigate',
594
+ secFetchDest: 'document'
595
+ };
596
+ }
597
+ static get(name) {
598
+ switch (name.toLowerCase()) {
599
+ case 'chrome_mobile_143_android': return this.chromeMobile143Android();
600
+ case 'chrome_133_psk': return this.chrome133PSK();
601
+ case 'chrome_133': return this.chrome133();
602
+ case 'firefox_117': return this.firefox117();
603
+ case 'safari_ios_18_0': return this.safariIOS18();
604
+ default: return this.chrome133();
605
+ }
606
+ }
607
+ static get latest() {
608
+ return this.chrome133();
609
+ }
610
+ static get latestMobile() {
611
+ return this.chromeMobile143Android();
612
+ }
613
+ }
614
+ exports.ProfileRegistry = ProfileRegistry;
615
+ class CookieJar {
616
+ constructor() {
617
+ this.cookies = new Map();
618
+ }
619
+ setCookie(domain, name, value, options = {}) {
620
+ if (!this.cookies.has(domain))
621
+ this.cookies.set(domain, new Map());
622
+ this.cookies.get(domain).set(name, { value, ...options, path: options.path || '/', domain });
623
+ }
624
+ getCookies(domain, path = '/', secure = true) {
625
+ const domainCookies = this.cookies.get(domain);
626
+ if (!domainCookies)
627
+ return '';
628
+ const validCookies = [];
629
+ const now = new Date();
630
+ for (const [name, cookie] of domainCookies) {
631
+ if (cookie.expires && cookie.expires < now) {
632
+ domainCookies.delete(name);
633
+ continue;
634
+ }
635
+ if (cookie.secure && !secure)
636
+ continue;
637
+ if (path.startsWith(cookie.path))
638
+ validCookies.push(`${name}=${cookie.value}`);
639
+ }
640
+ return validCookies.join('; ');
641
+ }
642
+ parseSetCookie(domain, setCookieHeaders) {
643
+ for (const header of setCookieHeaders) {
644
+ const parts = header.split(';').map(p => p.trim());
645
+ const [nameValue] = parts;
646
+ const [name, value] = nameValue.split('=');
647
+ const options = { path: '/' };
648
+ for (let i = 1; i < parts.length; i++) {
649
+ const [key, val] = parts[i].split('=');
650
+ const lowerKey = key.toLowerCase();
651
+ if (lowerKey === 'expires')
652
+ options.expires = new Date(val);
653
+ else if (lowerKey === 'path')
654
+ options.path = val;
655
+ else if (lowerKey === 'secure')
656
+ options.secure = true;
657
+ else if (lowerKey === 'httponly')
658
+ options.httpOnly = true;
659
+ else if (lowerKey === 'samesite')
660
+ options.sameSite = val;
661
+ }
662
+ this.setCookie(domain, name, value, options);
663
+ }
664
+ }
665
+ }
666
+ class TLSSocketManager extends events_1.EventEmitter {
667
+ constructor(profile) {
668
+ super();
669
+ this.socket = null;
670
+ this.tlsSocket = null;
671
+ this.startTime = 0;
672
+ this.hostname = '';
673
+ this.port = 443;
674
+ this.profile = profile;
675
+ this.timing = { socket: 0, lookup: 0, connect: 0, secureConnect: 0, response: 0, end: 0, total: 0 };
676
+ }
677
+ async connect(hostname, port = 443) {
678
+ this.hostname = hostname;
679
+ this.port = port;
680
+ this.startTime = Date.now();
681
+ return new Promise((resolve, reject) => {
682
+ const timeout = setTimeout(() => {
683
+ this.destroy();
684
+ reject(new Error('Connection timeout'));
685
+ }, 30000);
686
+ this.socket = new net.Socket();
687
+ this.socket.setNoDelay(true);
688
+ this.socket.setKeepAlive(true, 60000);
689
+ this.socket.on('lookup', () => {
690
+ this.timing.lookup = Date.now() - this.startTime;
691
+ });
692
+ this.socket.connect(port, hostname, () => {
693
+ this.timing.connect = Date.now() - this.startTime;
694
+ const validCiphers = this.profile.tls.cipherSuites
695
+ .filter(c => c < 0xff00)
696
+ .map(c => {
697
+ const hex = c.toString(16).toUpperCase();
698
+ return hex.length === 3 ? `0${hex}` : hex;
699
+ });
700
+ const cipherList = [
701
+ 'TLS_AES_128_GCM_SHA256',
702
+ 'TLS_AES_256_GCM_SHA384',
703
+ 'TLS_CHACHA20_POLY1305_SHA256',
704
+ 'ECDHE-ECDSA-AES128-GCM-SHA256',
705
+ 'ECDHE-RSA-AES128-GCM-SHA256',
706
+ 'ECDHE-ECDSA-AES256-GCM-SHA384',
707
+ 'ECDHE-RSA-AES256-GCM-SHA384',
708
+ 'ECDHE-ECDSA-CHACHA20-POLY1305',
709
+ 'ECDHE-RSA-CHACHA20-POLY1305',
710
+ 'ECDHE-RSA-AES128-SHA',
711
+ 'ECDHE-RSA-AES256-SHA',
712
+ 'AES128-GCM-SHA256',
713
+ 'AES256-GCM-SHA384',
714
+ 'AES128-SHA',
715
+ 'AES256-SHA'
716
+ ].join(':');
717
+ const tlsOptions = {
718
+ socket: this.socket,
719
+ servername: hostname,
720
+ ALPNProtocols: this.profile.tls.alpnProtocols,
721
+ ciphers: cipherList,
722
+ minVersion: 'TLSv1.2',
723
+ maxVersion: 'TLSv1.3',
724
+ rejectUnauthorized: false,
725
+ requestCert: false,
726
+ honorCipherOrder: true,
727
+ sessionTimeout: 300
728
+ };
729
+ this.tlsSocket = tls.connect(tlsOptions);
730
+ this.tlsSocket.once('secureConnect', () => {
731
+ clearTimeout(timeout);
732
+ this.timing.secureConnect = Date.now() - this.startTime;
733
+ this.timing.socket = this.startTime;
734
+ resolve(this.tlsSocket);
735
+ });
736
+ this.tlsSocket.on('error', (err) => {
737
+ clearTimeout(timeout);
738
+ this.destroy();
739
+ reject(err);
740
+ });
741
+ });
742
+ this.socket.on('error', (err) => {
743
+ clearTimeout(timeout);
744
+ this.destroy();
745
+ reject(err);
746
+ });
747
+ this.socket.on('timeout', () => {
748
+ clearTimeout(timeout);
749
+ this.destroy();
750
+ reject(new Error('Socket timeout'));
751
+ });
752
+ });
753
+ }
754
+ getTiming() {
755
+ return { ...this.timing, total: Date.now() - this.startTime };
756
+ }
757
+ destroy() {
758
+ if (this.tlsSocket && !this.tlsSocket.destroyed) {
759
+ this.tlsSocket.destroy();
760
+ }
761
+ if (this.socket && !this.socket.destroyed) {
762
+ this.socket.destroy();
763
+ }
764
+ this.tlsSocket = null;
765
+ this.socket = null;
766
+ }
767
+ getSocket() {
768
+ return this.tlsSocket;
769
+ }
770
+ }
771
+ class HTTP2ClientManager extends events_1.EventEmitter {
772
+ constructor(tlsSocket, profile, hostname, cookieJar) {
773
+ super();
774
+ this.client = null;
775
+ this.tlsSocket = tlsSocket;
776
+ this.profile = profile;
777
+ this.hostname = hostname;
778
+ this.cookieJar = cookieJar;
779
+ }
780
+ async createSession() {
781
+ return new Promise((resolve, reject) => {
782
+ const settings = {};
783
+ this.profile.http2.settingsOrder.forEach(id => {
784
+ switch (id) {
785
+ case 1:
786
+ settings.headerTableSize = this.profile.http2.headerTableSize;
787
+ break;
788
+ case 2:
789
+ settings.enablePush = this.profile.http2.enablePush === 1;
790
+ break;
791
+ case 3:
792
+ if (this.profile.http2.maxConcurrentStreams)
793
+ settings.maxConcurrentStreams = this.profile.http2.maxConcurrentStreams;
794
+ break;
795
+ case 4:
796
+ settings.initialWindowSize = this.profile.http2.initialWindowSize;
797
+ break;
798
+ case 5:
799
+ if (this.profile.http2.maxFrameSize)
800
+ settings.maxFrameSize = this.profile.http2.maxFrameSize;
801
+ break;
802
+ case 6:
803
+ if (this.profile.http2.maxHeaderListSize)
804
+ settings.maxHeaderListSize = this.profile.http2.maxHeaderListSize;
805
+ break;
806
+ }
807
+ });
808
+ this.client = http2.connect(`https://${this.hostname}`, {
809
+ createConnection: () => this.tlsSocket,
810
+ settings
811
+ });
812
+ this.client.once('connect', () => resolve(this.client));
813
+ this.client.once('error', reject);
814
+ this.client.once('timeout', () => reject(new Error('HTTP/2 session timeout')));
815
+ });
816
+ }
817
+ async request(path, options = {}, timingStart) {
818
+ if (!this.client || this.client.destroyed) {
819
+ throw new Error('HTTP/2 session not established or destroyed');
820
+ }
821
+ const headers = {
822
+ ':method': options.method?.toUpperCase() || 'GET',
823
+ ':authority': this.hostname,
824
+ ':scheme': 'https',
825
+ ':path': path
826
+ };
827
+ const defaultHeaders = {
828
+ 'user-agent': this.profile.userAgent,
829
+ 'sec-ch-ua': this.profile.secChUa,
830
+ 'sec-ch-ua-mobile': this.profile.secChUaMobile,
831
+ 'sec-ch-ua-platform': this.profile.secChUaPlatform,
832
+ 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
833
+ 'accept-language': this.profile.acceptLanguage || 'en-US,en;q=0.9',
834
+ 'accept-encoding': 'gzip, deflate, br, zstd',
835
+ 'sec-fetch-site': this.profile.secFetchSite,
836
+ 'sec-fetch-mode': this.profile.secFetchMode,
837
+ 'sec-fetch-dest': this.profile.secFetchDest,
838
+ };
839
+ if (this.profile.upgradeInsecureRequests)
840
+ defaultHeaders['upgrade-insecure-requests'] = '1';
841
+ if (this.profile.priority)
842
+ defaultHeaders['priority'] = this.profile.priority;
843
+ if (this.profile.http2.headerOrder.length > 0) {
844
+ this.profile.http2.headerOrder.forEach(name => {
845
+ if (defaultHeaders[name])
846
+ headers[name] = defaultHeaders[name];
847
+ });
848
+ }
849
+ Object.assign(headers, defaultHeaders);
850
+ const cookies = this.cookieJar.getCookies(this.hostname, path);
851
+ if (cookies)
852
+ headers['cookie'] = cookies;
853
+ if (options.headers)
854
+ Object.assign(headers, options.headers);
855
+ if (options.body && (options.method || 'GET').toUpperCase() !== 'GET') {
856
+ if (!headers['content-type'])
857
+ headers['content-type'] = 'application/x-www-form-urlencoded';
858
+ if (typeof options.body === 'string') {
859
+ headers['content-length'] = Buffer.byteLength(options.body).toString();
860
+ }
861
+ else if (Buffer.isBuffer(options.body)) {
862
+ headers['content-length'] = options.body.length.toString();
863
+ }
864
+ }
865
+ const stream = this.client.request(headers);
866
+ const response = new events_1.EventEmitter();
867
+ response.timing = {
868
+ socket: timingStart,
869
+ lookup: 0,
870
+ connect: 0,
871
+ secureConnect: 0,
872
+ response: 0,
873
+ end: 0,
874
+ total: 0
875
+ };
876
+ response.fingerprints = {
877
+ ja3: this.generateJA3(),
878
+ ja3Hash: crypto.createHash('md5').update(this.generateJA3()).digest('hex'),
879
+ 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`
880
+ };
881
+ const chunks = [];
882
+ stream.once('response', (headers, flags) => {
883
+ response.statusCode = headers[':status'];
884
+ response.headers = headers;
885
+ const setCookies = headers['set-cookie'];
886
+ if (setCookies) {
887
+ const cookies = Array.isArray(setCookies) ? setCookies : [setCookies];
888
+ this.cookieJar.parseSetCookie(this.hostname, cookies);
889
+ }
890
+ response.timing.response = Date.now() - timingStart;
891
+ });
892
+ stream.on('data', (chunk) => chunks.push(chunk));
893
+ stream.once('end', async () => {
894
+ let body = Buffer.concat(chunks);
895
+ if (options.decompress !== false) {
896
+ const encoding = response.headers['content-encoding']?.toLowerCase();
897
+ try {
898
+ if (encoding === 'gzip')
899
+ body = await gunzip(body);
900
+ else if (encoding === 'br')
901
+ body = await brotliDecompress(body);
902
+ else if (encoding === 'deflate')
903
+ body = await inflate(body);
904
+ }
905
+ catch (e) { }
906
+ }
907
+ response.body = body;
908
+ try {
909
+ response.text = body.toString('utf-8');
910
+ if ((response.headers['content-type'] || '').includes('application/json')) {
911
+ response.json = JSON.parse(response.text);
912
+ }
913
+ }
914
+ catch { }
915
+ response.timing.end = Date.now() - timingStart;
916
+ response.timing.total = response.timing.end;
917
+ response.emit('complete', response);
918
+ });
919
+ stream.once('error', (err) => response.emit('error', err));
920
+ stream.once('close', () => response.emit('close'));
921
+ if (options.body) {
922
+ if (Buffer.isBuffer(options.body) || typeof options.body === 'string') {
923
+ stream.end(options.body);
924
+ }
925
+ else if (options.body instanceof stream_1.Readable) {
926
+ options.body.pipe(stream);
927
+ }
928
+ }
929
+ else {
930
+ stream.end();
931
+ }
932
+ return new Promise((resolve, reject) => {
933
+ response.once('complete', () => resolve(response));
934
+ response.once('error', reject);
935
+ });
936
+ }
937
+ generateJA3() {
938
+ const version = '771';
939
+ const ciphers = this.profile.tls.cipherSuites
940
+ .filter(c => c < 0xff00)
941
+ .join('-');
942
+ const extensions = this.profile.tls.extensions
943
+ .filter(e => typeof e === 'number' && e < 0xff00)
944
+ .join('-');
945
+ const curves = this.profile.tls.supportedGroups
946
+ .filter(g => g < 0xff00)
947
+ .join('-');
948
+ const ecPoints = this.profile.tls.ecPointFormats.join('-');
949
+ return `${version},${ciphers},${extensions},${curves},${ecPoints}`;
950
+ }
951
+ destroy() {
952
+ if (this.client && !this.client.destroyed) {
953
+ this.client.destroy();
954
+ }
955
+ this.client = null;
956
+ }
957
+ }
958
+ class AdvancedTLSClient {
959
+ constructor(profileName) {
960
+ this.sessionCache = new Map();
961
+ this.cookieJar = new CookieJar();
962
+ this.maxCachedSessions = 10;
963
+ this.sessionTimeout = 300000;
964
+ this.profile = ProfileRegistry.get(profileName || 'chrome_133');
965
+ this.cleanupInterval = setInterval(() => {
966
+ const now = Date.now();
967
+ for (const [key, session] of this.sessionCache) {
968
+ if (now - session.lastUsed > this.sessionTimeout) {
969
+ session.http2Manager.destroy();
970
+ session.tlsManager.destroy();
971
+ this.sessionCache.delete(key);
972
+ }
973
+ }
974
+ }, 60000);
975
+ }
976
+ async request(url, options = {}) {
977
+ const parsed = new URL(url);
978
+ const hostname = parsed.hostname;
979
+ const port = parsed.port ? +parsed.port : 443;
980
+ const path = parsed.pathname + parsed.search;
981
+ const cacheKey = `${hostname}:${port}`;
982
+ let session = this.sessionCache.get(cacheKey);
983
+ const startTime = Date.now();
984
+ if (!session || session.tlsManager.getSocket()?.destroyed) {
985
+ const tlsManager = new TLSSocketManager(this.profile);
986
+ const tlsSocket = await tlsManager.connect(hostname, port);
987
+ const http2Manager = new HTTP2ClientManager(tlsSocket, this.profile, hostname, this.cookieJar);
988
+ await http2Manager.createSession();
989
+ session = { tlsManager, http2Manager, lastUsed: Date.now() };
990
+ this.sessionCache.set(cacheKey, session);
991
+ if (this.sessionCache.size > this.maxCachedSessions) {
992
+ const oldest = [...this.sessionCache.entries()].sort((a, b) => a[1].lastUsed - b[1].lastUsed)[0];
993
+ oldest[1].http2Manager.destroy();
994
+ oldest[1].tlsManager.destroy();
995
+ this.sessionCache.delete(oldest[0]);
996
+ }
997
+ }
998
+ session.lastUsed = Date.now();
999
+ options.headers = options.headers || {};
1000
+ options.headers['user-agent'] = this.profile.userAgent;
1001
+ options.headers['accept-language'] = this.profile.acceptLanguage || 'en-US,en;q=0.9';
1002
+ options.headers['accept-encoding'] = 'gzip, deflate, br';
1003
+ options.headers['accept'] = 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8';
1004
+ options.headers['sec-ch-ua'] = this.profile.secChUa;
1005
+ options.headers['sec-ch-ua-mobile'] = this.profile.secChUaMobile;
1006
+ options.headers['sec-ch-ua-platform'] = this.profile.secChUaPlatform;
1007
+ options.headers['sec-fetch-site'] = this.profile.secFetchSite;
1008
+ options.headers['sec-fetch-mode'] = this.profile.secFetchMode;
1009
+ options.headers['sec-fetch-dest'] = this.profile.secFetchDest;
1010
+ if (this.profile.upgradeInsecureRequests)
1011
+ options.headers['upgrade-insecure-requests'] = this.profile.upgradeInsecureRequests;
1012
+ if (this.profile.priority)
1013
+ options.headers['priority'] = this.profile.priority;
1014
+ const cookies = this.cookieJar.getCookies(hostname, parsed.pathname, parsed.protocol === 'https:');
1015
+ if (cookies)
1016
+ options.headers['cookie'] = cookies;
1017
+ let response = await session.http2Manager.request(path, options, startTime);
1018
+ if (options.followRedirects !== false && [301, 302, 303, 307, 308].includes(response.statusCode) && (options.maxRedirects ?? 5) > 0) {
1019
+ options.maxRedirects = (options.maxRedirects ?? 5) - 1;
1020
+ const location = response.headers['location'];
1021
+ if (location) {
1022
+ const redirectUrl = new URL(location, url).toString();
1023
+ return this.request(redirectUrl, options);
1024
+ }
1025
+ }
1026
+ return response;
1027
+ }
1028
+ destroy() {
1029
+ clearInterval(this.cleanupInterval);
1030
+ this.sessionCache.forEach(s => {
1031
+ s.http2Manager.destroy();
1032
+ s.tlsManager.destroy();
1033
+ });
1034
+ this.sessionCache.clear();
1035
+ }
1036
+ }
1037
+ exports.AdvancedTLSClient = AdvancedTLSClient;