advanced-tls-client 1.0.1 → 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 CHANGED
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.AdvancedTLSClient = void 0;
36
+ exports.ProfileRegistry = exports.AdvancedTLSClient = void 0;
37
37
  const tls = __importStar(require("tls"));
38
38
  const net = __importStar(require("net"));
39
39
  const http2 = __importStar(require("http2"));
@@ -41,8 +41,10 @@ const crypto = __importStar(require("crypto"));
41
41
  const events_1 = require("events");
42
42
  const zlib = __importStar(require("zlib"));
43
43
  const util_1 = require("util");
44
+ const stream_1 = require("stream");
44
45
  const gunzip = (0, util_1.promisify)(zlib.gunzip);
45
46
  const brotliDecompress = (0, util_1.promisify)(zlib.brotliDecompress);
47
+ const inflate = (0, util_1.promisify)(zlib.inflate);
46
48
  const GREASE_VALUES = [0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a, 0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa];
47
49
  class ProfileRegistry {
48
50
  static getGrease() {
@@ -58,72 +60,72 @@ class ProfileRegistry {
58
60
  version: '143.0.0.0',
59
61
  tls: {
60
62
  cipherSuites: [
61
- `GREASE_${grease1.toString(16)}`,
62
- 'TLS_AES_128_GCM_SHA256',
63
- 'TLS_AES_256_GCM_SHA384',
64
- 'TLS_CHACHA20_POLY1305_SHA256',
65
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
66
- 'ECDHE-RSA-AES128-GCM-SHA256',
67
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
68
- 'ECDHE-RSA-AES256-GCM-SHA384',
69
- 'ECDHE-ECDSA-CHACHA20-POLY1305',
70
- 'ECDHE-RSA-CHACHA20-POLY1305',
71
- 'ECDHE-RSA-AES128-SHA',
72
- 'ECDHE-RSA-AES256-SHA',
73
- 'AES128-GCM-SHA256',
74
- 'AES256-GCM-SHA384',
75
- 'AES128-SHA',
76
- 'AES256-SHA'
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
77
79
  ],
78
80
  extensions: [
79
81
  grease2,
80
- 0xfe0d,
81
- 0x001b,
82
- 0x002b,
83
- 0x0010,
84
- 0x0023,
85
- 0x002d,
86
- 0x000b,
87
- 0xff01,
88
- 0x0005,
89
- 0x000a,
90
- 0x0017,
91
- 0x0000,
92
- 0x44cd,
93
- 0x0033,
94
- 0x0012,
95
- 0x000d,
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,
96
98
  grease3
97
99
  ],
98
100
  supportedGroups: [
99
- `GREASE_${grease4.toString(16)}`,
100
- 'X25519Kyber768',
101
- 'X25519',
102
- 'prime256v1',
103
- 'secp384r1'
101
+ grease4,
102
+ 25497,
103
+ 29,
104
+ 23,
105
+ 24
104
106
  ],
105
107
  signatureAlgorithms: [
106
- 'ecdsa_secp256r1_sha256',
107
- 'rsa_pss_rsae_sha256',
108
- 'rsa_pkcs1_sha256',
109
- 'ecdsa_secp384r1_sha384',
110
- 'rsa_pss_rsae_sha384',
111
- 'rsa_pkcs1_sha384',
112
- 'rsa_pss_rsae_sha512',
113
- 'rsa_pkcs1_sha512'
108
+ 1027,
109
+ 2052,
110
+ 1025,
111
+ 1283,
112
+ 2053,
113
+ 1281,
114
+ 2054,
115
+ 1537
114
116
  ],
115
117
  supportedVersions: [
116
- `GREASE_${grease2.toString(16)}`,
117
- 'TLSv1.3',
118
- 'TLSv1.2'
118
+ grease2,
119
+ 772,
120
+ 771
119
121
  ],
120
- ecPointFormats: [0x00],
122
+ ecPointFormats: [0],
121
123
  alpnProtocols: ['h2', 'http/1.1'],
122
- pskKeyExchangeModes: [0x01],
123
- compressionMethods: [0x00],
124
+ pskKeyExchangeModes: [1],
125
+ compressionMethods: [0],
124
126
  grease: true,
125
127
  recordSizeLimit: 16385,
126
- certificateCompression: [0x02],
128
+ certificateCompression: [2],
127
129
  ocspStapling: true,
128
130
  signedCertificateTimestamp: true
129
131
  },
@@ -182,68 +184,68 @@ class ProfileRegistry {
182
184
  version: '133.0.0.0',
183
185
  tls: {
184
186
  cipherSuites: [
185
- `GREASE_${grease1.toString(16)}`,
186
- 'TLS_AES_128_GCM_SHA256',
187
- 'TLS_AES_256_GCM_SHA384',
188
- 'TLS_CHACHA20_POLY1305_SHA256',
189
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
190
- 'ECDHE-RSA-AES128-GCM-SHA256',
191
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
192
- 'ECDHE-RSA-AES256-GCM-SHA384',
193
- 'ECDHE-ECDSA-CHACHA20-POLY1305',
194
- 'ECDHE-RSA-CHACHA20-POLY1305',
195
- 'ECDHE-RSA-AES128-SHA',
196
- 'ECDHE-RSA-AES256-SHA',
197
- 'AES128-GCM-SHA256',
198
- 'AES256-GCM-SHA384',
199
- 'AES128-SHA',
200
- 'AES256-SHA'
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
201
203
  ],
202
204
  extensions: [
203
205
  grease2,
204
- 0x0000,
205
- 0x0017,
206
- 0xff01,
207
- 0x000a,
208
- 0x000b,
209
- 0x0023,
210
- 0x0010,
211
- 0x0005,
212
- 0x000d,
213
- 0x0012,
214
- 0x0033,
215
- 0x002d,
216
- 0x002b,
217
- 0x001b,
218
- 0x0015,
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,
219
221
  grease3
220
222
  ],
221
223
  supportedGroups: [
222
- `GREASE_${grease4.toString(16)}`,
223
- 'X25519Kyber768',
224
- 'X25519',
225
- 'prime256v1',
226
- 'secp384r1'
224
+ grease4,
225
+ 25497,
226
+ 29,
227
+ 23,
228
+ 24
227
229
  ],
228
230
  signatureAlgorithms: [
229
- 'ecdsa_secp256r1_sha256',
230
- 'rsa_pss_rsae_sha256',
231
- 'rsa_pkcs1_sha256',
232
- 'ecdsa_secp384r1_sha384',
233
- 'rsa_pss_rsae_sha384',
234
- 'rsa_pkcs1_sha384',
235
- 'rsa_pss_rsae_sha512',
236
- 'rsa_pkcs1_sha512'
231
+ 1027,
232
+ 2052,
233
+ 1025,
234
+ 1283,
235
+ 2053,
236
+ 1281,
237
+ 2054,
238
+ 1537
237
239
  ],
238
240
  supportedVersions: [
239
- `GREASE_${grease2.toString(16)}`,
240
- 'TLSv1.3',
241
- 'TLSv1.2'
241
+ grease2,
242
+ 772,
243
+ 771
242
244
  ],
243
- ecPointFormats: [0x00],
245
+ ecPointFormats: [0],
244
246
  alpnProtocols: ['h2', 'http/1.1'],
245
- pskKeyExchangeModes: [0x01],
246
- compressionMethods: [0x00],
247
+ pskKeyExchangeModes: [1],
248
+ compressionMethods: [0],
247
249
  grease: true,
248
250
  recordSizeLimit: 16385
249
251
  },
@@ -300,68 +302,68 @@ class ProfileRegistry {
300
302
  version: '133.0.0.0',
301
303
  tls: {
302
304
  cipherSuites: [
303
- `GREASE_${grease1.toString(16)}`,
304
- 'TLS_AES_128_GCM_SHA256',
305
- 'TLS_AES_256_GCM_SHA384',
306
- 'TLS_CHACHA20_POLY1305_SHA256',
307
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
308
- 'ECDHE-RSA-AES128-GCM-SHA256',
309
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
310
- 'ECDHE-RSA-AES256-GCM-SHA384',
311
- 'ECDHE-ECDSA-CHACHA20-POLY1305',
312
- 'ECDHE-RSA-CHACHA20-POLY1305',
313
- 'ECDHE-RSA-AES128-SHA',
314
- 'ECDHE-RSA-AES256-SHA',
315
- 'AES128-GCM-SHA256',
316
- 'AES256-GCM-SHA384',
317
- 'AES128-SHA',
318
- 'AES256-SHA'
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
319
321
  ],
320
322
  extensions: [
321
323
  grease2,
322
- 0x0000,
323
- 0x0017,
324
- 0xff01,
325
- 0x000a,
326
- 0x000b,
327
- 0x0023,
328
- 0x0010,
329
- 0x0005,
330
- 0x000d,
331
- 0x0012,
332
- 0x0033,
333
- 0x002d,
334
- 0x002b,
335
- 0x001b,
336
- 0x0015,
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,
337
339
  grease3
338
340
  ],
339
341
  supportedGroups: [
340
- `GREASE_${grease4.toString(16)}`,
341
- 'X25519Kyber768',
342
- 'X25519',
343
- 'prime256v1',
344
- 'secp384r1'
342
+ grease4,
343
+ 25497,
344
+ 29,
345
+ 23,
346
+ 24
345
347
  ],
346
348
  signatureAlgorithms: [
347
- 'ecdsa_secp256r1_sha256',
348
- 'rsa_pss_rsae_sha256',
349
- 'rsa_pkcs1_sha256',
350
- 'ecdsa_secp384r1_sha384',
351
- 'rsa_pss_rsae_sha384',
352
- 'rsa_pkcs1_sha384',
353
- 'rsa_pss_rsae_sha512',
354
- 'rsa_pkcs1_sha512'
349
+ 1027,
350
+ 2052,
351
+ 1025,
352
+ 1283,
353
+ 2053,
354
+ 1281,
355
+ 2054,
356
+ 1537
355
357
  ],
356
358
  supportedVersions: [
357
- `GREASE_${grease2.toString(16)}`,
358
- 'TLSv1.3',
359
- 'TLSv1.2'
359
+ grease2,
360
+ 772,
361
+ 771
360
362
  ],
361
- ecPointFormats: [0x00],
363
+ ecPointFormats: [0],
362
364
  alpnProtocols: ['h2', 'http/1.1'],
363
- pskKeyExchangeModes: [0x01],
364
- compressionMethods: [0x00],
365
+ pskKeyExchangeModes: [1],
366
+ compressionMethods: [0],
365
367
  grease: true,
366
368
  recordSizeLimit: 16385
367
369
  },
@@ -414,44 +416,54 @@ class ProfileRegistry {
414
416
  version: '117.0',
415
417
  tls: {
416
418
  cipherSuites: [
417
- 'TLS_AES_128_GCM_SHA256',
418
- 'TLS_CHACHA20_POLY1305_SHA256',
419
- 'TLS_AES_256_GCM_SHA384',
420
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
421
- 'ECDHE-RSA-AES128-GCM-SHA256',
422
- 'ECDHE-ECDSA-CHACHA20-POLY1305',
423
- 'ECDHE-RSA-CHACHA20-POLY1305',
424
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
425
- 'ECDHE-RSA-AES256-GCM-SHA384',
426
- 'ECDHE-ECDSA-AES256-SHA',
427
- 'ECDHE-ECDSA-AES128-SHA',
428
- 'ECDHE-RSA-AES128-SHA',
429
- 'ECDHE-RSA-AES256-SHA',
430
- 'AES128-GCM-SHA256',
431
- 'AES256-GCM-SHA384',
432
- 'AES128-SHA',
433
- 'AES256-SHA'
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
434
445
  ],
435
- extensions: [0x0000, 0x0017, 0xff01, 0x000a, 0x000b, 0x0023, 0x0010, 0x0005, 0x0022, 0x0033, 0x002b, 0x000d, 0x002d, 0x0029, 0x0015],
436
- supportedGroups: ['X25519', 'prime256v1', 'secp384r1', 'secp521r1', 'ffdhe2048', 'ffdhe3072'],
437
446
  signatureAlgorithms: [
438
- 'ecdsa_secp256r1_sha256',
439
- 'ecdsa_secp384r1_sha384',
440
- 'ecdsa_secp521r1_sha512',
441
- 'rsa_pss_rsae_sha256',
442
- 'rsa_pss_rsae_sha384',
443
- 'rsa_pss_rsae_sha512',
444
- 'rsa_pkcs1_sha256',
445
- 'rsa_pkcs1_sha384',
446
- 'rsa_pkcs1_sha512',
447
- 'ecdsa_sha1',
448
- 'rsa_pkcs1_sha1'
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
449
462
  ],
450
- supportedVersions: ['TLSv1.3', 'TLSv1.2'],
451
- ecPointFormats: [0x00],
463
+ ecPointFormats: [0],
452
464
  alpnProtocols: ['h2', 'http/1.1'],
453
- pskKeyExchangeModes: [0x01],
454
- compressionMethods: [0x00],
465
+ pskKeyExchangeModes: [1],
466
+ compressionMethods: [0],
455
467
  grease: false
456
468
  },
457
469
  http2: {
@@ -471,7 +483,8 @@ class ProfileRegistry {
471
483
  { streamId: 13, weight: 240, dependency: 0, exclusive: false }
472
484
  ],
473
485
  pseudoHeaderOrder: [':method', ':path', ':authority', ':scheme'],
474
- headerPriority: { streamDep: 13, exclusive: false, weight: 41 }
486
+ headerPriority: { streamDep: 13, exclusive: false, weight: 41 },
487
+ headerOrder: []
475
488
  },
476
489
  userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:117.0) Gecko/20100101 Firefox/117.0',
477
490
  secChUa: '',
@@ -490,63 +503,73 @@ class ProfileRegistry {
490
503
  version: '18.0',
491
504
  tls: {
492
505
  cipherSuites: [
493
- `GREASE_${grease1.toString(16)}`,
494
- 'TLS_AES_128_GCM_SHA256',
495
- 'TLS_AES_256_GCM_SHA384',
496
- 'TLS_CHACHA20_POLY1305_SHA256',
497
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
498
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
499
- 'ECDHE-ECDSA-CHACHA20-POLY1305',
500
- 'ECDHE-RSA-AES256-GCM-SHA384',
501
- 'ECDHE-RSA-AES128-GCM-SHA256',
502
- 'ECDHE-RSA-CHACHA20-POLY1305',
503
- 'ECDHE-ECDSA-AES256-SHA',
504
- 'ECDHE-ECDSA-AES128-SHA',
505
- 'ECDHE-RSA-AES256-SHA',
506
- 'ECDHE-RSA-AES128-SHA',
507
- 'AES256-GCM-SHA384',
508
- 'AES128-GCM-SHA256',
509
- 'AES256-SHA',
510
- 'AES128-SHA',
511
- 'ECDHE-ECDSA-DES-CBC3-SHA',
512
- 'ECDHE-RSA-DES-CBC3-SHA',
513
- 'DES-CBC3-SHA'
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
514
527
  ],
515
528
  extensions: [
516
529
  grease2,
517
- 0x0000,
518
- 0x0017,
519
- 0xff01,
520
- 0x000a,
521
- 0x000b,
522
- 0x0010,
523
- 0x0005,
524
- 0x000d,
525
- 0x0012,
526
- 0x0033,
527
- 0x002d,
528
- 0x002b,
529
- 0x0015
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
530
549
  ],
531
- supportedGroups: ['X25519', 'prime256v1', 'secp384r1', 'secp521r1'],
532
550
  signatureAlgorithms: [
533
- 'ecdsa_secp256r1_sha256',
534
- 'rsa_pss_rsae_sha256',
535
- 'rsa_pkcs1_sha256',
536
- 'ecdsa_secp384r1_sha384',
537
- 'ecdsa_sha1',
538
- 'rsa_pss_rsae_sha384',
539
- 'rsa_pss_rsae_sha384',
540
- 'rsa_pkcs1_sha384',
541
- 'rsa_pss_rsae_sha512',
542
- 'rsa_pkcs1_sha512',
543
- 'rsa_pkcs1_sha1'
551
+ 1027,
552
+ 2052,
553
+ 1025,
554
+ 1283,
555
+ 513,
556
+ 2053,
557
+ 2053,
558
+ 1281,
559
+ 2054,
560
+ 1537,
561
+ 515
544
562
  ],
545
- supportedVersions: ['TLSv1.3', 'TLSv1.2', 'TLSv1.1', 'TLSv1.0'],
546
- ecPointFormats: [0x00],
563
+ supportedVersions: [
564
+ 772,
565
+ 771,
566
+ 770,
567
+ 769
568
+ ],
569
+ ecPointFormats: [0],
547
570
  alpnProtocols: ['h2', 'http/1.1'],
548
- pskKeyExchangeModes: [0x01],
549
- compressionMethods: [0x00],
571
+ pskKeyExchangeModes: [1],
572
+ compressionMethods: [0],
550
573
  grease: true
551
574
  },
552
575
  http2: {
@@ -559,7 +582,8 @@ class ProfileRegistry {
559
582
  settingsOrder: [2, 3, 4, 8, 9],
560
583
  connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
561
584
  priorityFrames: [],
562
- pseudoHeaderOrder: [':method', ':scheme', ':authority', ':path']
585
+ pseudoHeaderOrder: [':method', ':scheme', ':authority', ':path'],
586
+ headerOrder: []
563
587
  },
564
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',
565
589
  secChUa: '',
@@ -587,6 +611,7 @@ class ProfileRegistry {
587
611
  return this.chromeMobile143Android();
588
612
  }
589
613
  }
614
+ exports.ProfileRegistry = ProfileRegistry;
590
615
  class CookieJar {
591
616
  constructor() {
592
617
  this.cookies = new Map();
@@ -594,9 +619,9 @@ class CookieJar {
594
619
  setCookie(domain, name, value, options = {}) {
595
620
  if (!this.cookies.has(domain))
596
621
  this.cookies.set(domain, new Map());
597
- this.cookies.get(domain).set(name, { value, expires: options.expires, path: options.path || '/', domain });
622
+ this.cookies.get(domain).set(name, { value, ...options, path: options.path || '/', domain });
598
623
  }
599
- getCookies(domain, path = '/') {
624
+ getCookies(domain, path = '/', secure = true) {
600
625
  const domainCookies = this.cookies.get(domain);
601
626
  if (!domainCookies)
602
627
  return '';
@@ -607,6 +632,8 @@ class CookieJar {
607
632
  domainCookies.delete(name);
608
633
  continue;
609
634
  }
635
+ if (cookie.secure && !secure)
636
+ continue;
610
637
  if (path.startsWith(cookie.path))
611
638
  validCookies.push(`${name}=${cookie.value}`);
612
639
  }
@@ -620,10 +647,17 @@ class CookieJar {
620
647
  const options = { path: '/' };
621
648
  for (let i = 1; i < parts.length; i++) {
622
649
  const [key, val] = parts[i].split('=');
623
- if (key.toLowerCase() === 'expires')
650
+ const lowerKey = key.toLowerCase();
651
+ if (lowerKey === 'expires')
624
652
  options.expires = new Date(val);
625
- else if (key.toLowerCase() === 'path')
653
+ else if (lowerKey === 'path')
626
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;
627
661
  }
628
662
  this.setCookie(domain, name, value, options);
629
663
  }
@@ -636,44 +670,13 @@ class TLSSocketManager extends events_1.EventEmitter {
636
670
  this.tlsSocket = null;
637
671
  this.startTime = 0;
638
672
  this.hostname = '';
673
+ this.port = 443;
639
674
  this.profile = profile;
640
675
  this.timing = { socket: 0, lookup: 0, connect: 0, secureConnect: 0, response: 0, end: 0, total: 0 };
641
676
  }
642
- convertCipherSuite(suite) {
643
- const cipherMap = {
644
- 'TLS_AES_128_GCM_SHA256': 'TLS_AES_128_GCM_SHA256',
645
- 'TLS_AES_256_GCM_SHA384': 'TLS_AES_256_GCM_SHA384',
646
- 'TLS_CHACHA20_POLY1305_SHA256': 'TLS_CHACHA20_POLY1305_SHA256',
647
- 'ECDHE-ECDSA-AES128-GCM-SHA256': 'ECDHE-ECDSA-AES128-GCM-SHA256',
648
- 'ECDHE-RSA-AES128-GCM-SHA256': 'ECDHE-RSA-AES128-GCM-SHA256',
649
- 'ECDHE-ECDSA-AES256-GCM-SHA384': 'ECDHE-ECDSA-AES256-GCM-SHA384',
650
- 'ECDHE-RSA-AES256-GCM-SHA384': 'ECDHE-RSA-AES256-GCM-SHA384',
651
- 'ECDHE-ECDSA-CHACHA20-POLY1305': 'ECDHE-ECDSA-CHACHA20-POLY1305',
652
- 'ECDHE-RSA-CHACHA20-POLY1305': 'ECDHE-RSA-CHACHA20-POLY1305',
653
- 'ECDHE-RSA-AES128-SHA': 'ECDHE-RSA-AES128-SHA',
654
- 'ECDHE-RSA-AES256-SHA': 'ECDHE-RSA-AES256-SHA',
655
- 'AES128-GCM-SHA256': 'AES128-GCM-SHA256',
656
- 'AES256-GCM-SHA384': 'AES256-GCM-SHA384',
657
- 'AES128-SHA': 'AES128-SHA',
658
- 'AES256-SHA': 'AES256-SHA'
659
- };
660
- if (suite.startsWith('GREASE_'))
661
- return '';
662
- return cipherMap[suite] || suite;
663
- }
664
- convertSupportedGroup(group) {
665
- const groupMap = {
666
- 'X25519Kyber768': 'X25519',
667
- 'X25519': 'X25519',
668
- 'prime256v1': 'prime256v1',
669
- 'secp384r1': 'secp384r1'
670
- };
671
- if (group.startsWith('GREASE_'))
672
- return '';
673
- return groupMap[group] || group;
674
- }
675
- async connect(hostname, port) {
677
+ async connect(hostname, port = 443) {
676
678
  this.hostname = hostname;
679
+ this.port = port;
677
680
  this.startTime = Date.now();
678
681
  return new Promise((resolve, reject) => {
679
682
  const timeout = setTimeout(() => {
@@ -681,40 +684,53 @@ class TLSSocketManager extends events_1.EventEmitter {
681
684
  reject(new Error('Connection timeout'));
682
685
  }, 30000);
683
686
  this.socket = new net.Socket();
684
- this.socket.setKeepAlive(true, 60000);
685
687
  this.socket.setNoDelay(true);
686
- this.timing.socket = Date.now() - this.startTime;
688
+ this.socket.setKeepAlive(true, 60000);
687
689
  this.socket.on('lookup', () => {
688
690
  this.timing.lookup = Date.now() - this.startTime;
689
691
  });
690
692
  this.socket.connect(port, hostname, () => {
691
693
  this.timing.connect = Date.now() - this.startTime;
692
- const ciphers = this.profile.tls.cipherSuites
693
- .map(suite => this.convertCipherSuite(suite))
694
- .filter(suite => suite !== '')
695
- .join(':');
696
- const curves = this.profile.tls.supportedGroups
697
- .map(group => this.convertSupportedGroup(group))
698
- .filter(group => group !== '')
699
- .join(':');
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(':');
700
717
  const tlsOptions = {
701
718
  socket: this.socket,
702
719
  servername: hostname,
703
720
  ALPNProtocols: this.profile.tls.alpnProtocols,
704
- ciphers: ciphers,
721
+ ciphers: cipherList,
705
722
  minVersion: 'TLSv1.2',
706
723
  maxVersion: 'TLSv1.3',
707
- ecdhCurve: curves,
708
- sigalgs: this.profile.tls.signatureAlgorithms.join(':'),
709
724
  rejectUnauthorized: false,
710
725
  requestCert: false,
711
726
  honorCipherOrder: true,
712
727
  sessionTimeout: 300
713
728
  };
714
729
  this.tlsSocket = tls.connect(tlsOptions);
715
- this.tlsSocket.on('secureConnect', () => {
730
+ this.tlsSocket.once('secureConnect', () => {
716
731
  clearTimeout(timeout);
717
732
  this.timing.secureConnect = Date.now() - this.startTime;
733
+ this.timing.socket = this.startTime;
718
734
  resolve(this.tlsSocket);
719
735
  });
720
736
  this.tlsSocket.on('error', (err) => {
@@ -740,133 +756,203 @@ class TLSSocketManager extends events_1.EventEmitter {
740
756
  }
741
757
  destroy() {
742
758
  if (this.tlsSocket && !this.tlsSocket.destroyed) {
743
- this.tlsSocket.removeAllListeners();
744
759
  this.tlsSocket.destroy();
745
- this.tlsSocket = null;
746
760
  }
747
761
  if (this.socket && !this.socket.destroyed) {
748
- this.socket.removeAllListeners();
749
762
  this.socket.destroy();
750
- this.socket = null;
751
763
  }
752
- }
753
- isConnected() {
754
- return this.tlsSocket !== null && !this.tlsSocket.destroyed;
764
+ this.tlsSocket = null;
765
+ this.socket = null;
755
766
  }
756
767
  getSocket() {
757
768
  return this.tlsSocket;
758
769
  }
759
- getHostname() {
760
- return this.hostname;
761
- }
762
770
  }
763
- class HTTP2ClientManager {
764
- constructor(tlsSocket, profile, hostname) {
771
+ class HTTP2ClientManager extends events_1.EventEmitter {
772
+ constructor(tlsSocket, profile, hostname, cookieJar) {
773
+ super();
765
774
  this.client = null;
766
775
  this.tlsSocket = tlsSocket;
767
776
  this.profile = profile;
768
777
  this.hostname = hostname;
778
+ this.cookieJar = cookieJar;
769
779
  }
770
780
  async createSession() {
771
781
  return new Promise((resolve, reject) => {
772
- const settingsMap = {};
773
- this.profile.http2.settingsOrder.forEach(settingId => {
774
- switch (settingId) {
782
+ const settings = {};
783
+ this.profile.http2.settingsOrder.forEach(id => {
784
+ switch (id) {
775
785
  case 1:
776
- settingsMap.headerTableSize = this.profile.http2.headerTableSize;
786
+ settings.headerTableSize = this.profile.http2.headerTableSize;
777
787
  break;
778
788
  case 2:
779
- settingsMap.enablePush = this.profile.http2.enablePush === 1;
789
+ settings.enablePush = this.profile.http2.enablePush === 1;
780
790
  break;
781
791
  case 3:
782
- if (this.profile.http2.maxConcurrentStreams !== undefined) {
783
- settingsMap.maxConcurrentStreams = this.profile.http2.maxConcurrentStreams;
784
- }
792
+ if (this.profile.http2.maxConcurrentStreams)
793
+ settings.maxConcurrentStreams = this.profile.http2.maxConcurrentStreams;
785
794
  break;
786
795
  case 4:
787
- settingsMap.initialWindowSize = this.profile.http2.initialWindowSize;
796
+ settings.initialWindowSize = this.profile.http2.initialWindowSize;
788
797
  break;
789
798
  case 5:
790
- if (this.profile.http2.maxFrameSize !== undefined) {
791
- settingsMap.maxFrameSize = this.profile.http2.maxFrameSize;
792
- }
799
+ if (this.profile.http2.maxFrameSize)
800
+ settings.maxFrameSize = this.profile.http2.maxFrameSize;
793
801
  break;
794
802
  case 6:
795
- if (this.profile.http2.maxHeaderListSize !== undefined) {
796
- settingsMap.maxHeaderListSize = this.profile.http2.maxHeaderListSize;
797
- }
803
+ if (this.profile.http2.maxHeaderListSize)
804
+ settings.maxHeaderListSize = this.profile.http2.maxHeaderListSize;
798
805
  break;
799
806
  }
800
807
  });
801
- const settings = {
808
+ this.client = http2.connect(`https://${this.hostname}`, {
802
809
  createConnection: () => this.tlsSocket,
803
- settings: settingsMap
804
- };
805
- this.client = http2.connect(`https://${this.hostname}`, settings);
806
- this.client.on('connect', () => {
807
- resolve(this.client);
810
+ settings
808
811
  });
809
- this.client.on('error', reject);
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')));
810
815
  });
811
816
  }
812
- request(path, headers) {
813
- if (!this.client)
814
- throw new Error('HTTP2 session not established');
815
- const orderedHeaders = {};
816
- this.profile.http2.pseudoHeaderOrder.forEach(pseudo => {
817
- if (headers[pseudo]) {
818
- orderedHeaders[pseudo] = headers[pseudo];
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();
819
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;
820
891
  });
821
- if (this.profile.http2.headerOrder) {
822
- this.profile.http2.headerOrder.forEach(headerName => {
823
- if (headers[headerName] && !headerName.startsWith(':')) {
824
- orderedHeaders[headerName] = headers[headerName];
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);
825
904
  }
826
- });
827
- Object.keys(headers).forEach(key => {
828
- if (!key.startsWith(':') && !orderedHeaders[key]) {
829
- orderedHeaders[key] = headers[key];
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);
830
912
  }
831
- });
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
+ }
832
928
  }
833
929
  else {
834
- Object.assign(orderedHeaders, headers);
835
- }
836
- const requestOptions = { ...orderedHeaders };
837
- if (this.profile.http2.priorityFrames && this.profile.http2.priorityFrames.length > 0) {
838
- const priority = this.profile.http2.priorityFrames[0];
839
- if (priority.weight)
840
- requestOptions.weight = priority.weight;
841
- if (priority.exclusive !== undefined)
842
- requestOptions.exclusive = priority.exclusive;
843
- if (priority.dependency !== undefined && priority.dependency !== 0)
844
- requestOptions.parent = priority.dependency;
845
- }
846
- const stream = this.client.request(requestOptions);
847
- if (this.profile.http2.headerPriority) {
848
- try {
849
- stream.priority({
850
- parent: this.profile.http2.headerPriority.streamDep,
851
- weight: this.profile.http2.headerPriority.weight,
852
- exclusive: this.profile.http2.headerPriority.exclusive
853
- });
854
- }
855
- catch (e) { }
930
+ stream.end();
856
931
  }
857
- return stream;
858
- }
859
- getClient() {
860
- return this.client;
932
+ return new Promise((resolve, reject) => {
933
+ response.once('complete', () => resolve(response));
934
+ response.once('error', reject);
935
+ });
861
936
  }
862
- isConnected() {
863
- return this.client !== null && !this.client.destroyed;
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}`;
864
950
  }
865
951
  destroy() {
866
952
  if (this.client && !this.client.destroyed) {
867
- this.client.close();
868
- this.client = null;
953
+ this.client.destroy();
869
954
  }
955
+ this.client = null;
870
956
  }
871
957
  }
872
958
  class AdvancedTLSClient {
@@ -875,11 +961,7 @@ class AdvancedTLSClient {
875
961
  this.cookieJar = new CookieJar();
876
962
  this.maxCachedSessions = 10;
877
963
  this.sessionTimeout = 300000;
878
- this.cleanupInterval = null;
879
- this.profile = profileName ? ProfileRegistry.get(profileName) : ProfileRegistry.latest;
880
- this.startSessionCleanup();
881
- }
882
- startSessionCleanup() {
964
+ this.profile = ProfileRegistry.get(profileName || 'chrome_133');
883
965
  this.cleanupInterval = setInterval(() => {
884
966
  const now = Date.now();
885
967
  for (const [key, session] of this.sessionCache) {
@@ -890,252 +972,64 @@ class AdvancedTLSClient {
890
972
  }
891
973
  }
892
974
  }, 60000);
893
- if (this.cleanupInterval.unref) {
894
- this.cleanupInterval.unref();
895
- }
896
975
  }
897
976
  async request(url, options = {}) {
898
- const maxRedirects = options.maxRedirects ?? 5;
899
- const followRedirects = options.followRedirects !== false;
900
- let redirectCount = 0;
901
- let currentUrl = url;
902
- let response;
903
- while (true) {
904
- response = await this.performRequest(currentUrl, options);
905
- if (followRedirects && [301, 302, 303, 307, 308].includes(response.statusCode)) {
906
- if (redirectCount >= maxRedirects) {
907
- throw new Error(`Too many redirects (max: ${maxRedirects})`);
908
- }
909
- const locationHeader = response.headers['location'];
910
- if (!locationHeader)
911
- break;
912
- const location = Array.isArray(locationHeader) ? locationHeader[0] : locationHeader;
913
- currentUrl = new URL(location, currentUrl).toString();
914
- redirectCount++;
915
- if ([301, 302, 303].includes(response.statusCode)) {
916
- options.method = 'GET';
917
- delete options.body;
918
- }
919
- }
920
- else {
921
- break;
922
- }
923
- }
924
- return response;
925
- }
926
- async performRequest(url, options = {}) {
927
- const parsedUrl = new URL(url);
928
- const hostname = parsedUrl.hostname;
929
- const port = parsedUrl.port ? parseInt(parsedUrl.port) : 443;
930
- const path = parsedUrl.pathname + parsedUrl.search;
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;
931
981
  const cacheKey = `${hostname}:${port}`;
932
982
  let session = this.sessionCache.get(cacheKey);
933
- if (!session || !this.isSessionAlive(session)) {
934
- if (session) {
935
- session.http2Manager.destroy();
936
- session.tlsManager.destroy();
937
- this.sessionCache.delete(cacheKey);
938
- }
983
+ const startTime = Date.now();
984
+ if (!session || session.tlsManager.getSocket()?.destroyed) {
939
985
  const tlsManager = new TLSSocketManager(this.profile);
940
986
  const tlsSocket = await tlsManager.connect(hostname, port);
941
- const http2Manager = new HTTP2ClientManager(tlsSocket, this.profile, hostname);
987
+ const http2Manager = new HTTP2ClientManager(tlsSocket, this.profile, hostname, this.cookieJar);
942
988
  await http2Manager.createSession();
943
989
  session = { tlsManager, http2Manager, lastUsed: Date.now() };
944
- if (this.sessionCache.size >= this.maxCachedSessions) {
945
- const oldestKey = Array.from(this.sessionCache.entries())
946
- .sort((a, b) => a[1].lastUsed - b[1].lastUsed)[0][0];
947
- const oldest = this.sessionCache.get(oldestKey);
948
- oldest.http2Manager.destroy();
949
- oldest.tlsManager.destroy();
950
- this.sessionCache.delete(oldestKey);
951
- }
952
990
  this.sessionCache.set(cacheKey, session);
953
- }
954
- session.lastUsed = Date.now();
955
- const headers = this.buildHeaders(hostname, path, options);
956
- const stream = session.http2Manager.request(path, headers);
957
- return new Promise((resolve, reject) => {
958
- const chunks = [];
959
- let responseHeaders = {};
960
- let statusCode = 0;
961
- stream.on('response', (headers) => {
962
- responseHeaders = headers;
963
- statusCode = Number(headers[':status']);
964
- const setCookieHeaders = headers['set-cookie'];
965
- if (setCookieHeaders) {
966
- const cookieArray = Array.isArray(setCookieHeaders) ? setCookieHeaders : [setCookieHeaders];
967
- this.cookieJar.parseSetCookie(hostname, cookieArray);
968
- }
969
- });
970
- stream.on('data', (chunk) => chunks.push(chunk));
971
- stream.on('end', async () => {
972
- stream.removeAllListeners();
973
- let body = Buffer.concat(chunks);
974
- const timing = session.tlsManager.getTiming();
975
- timing.end = Date.now();
976
- timing.total = timing.end - (Date.now() - timing.end);
977
- if (options.decompress !== false) {
978
- const encoding = responseHeaders['content-encoding'];
979
- if (encoding === 'gzip')
980
- body = await gunzip(body);
981
- else if (encoding === 'br')
982
- body = await brotliDecompress(body);
983
- }
984
- let text;
985
- let json;
986
- try {
987
- text = body.toString('utf-8');
988
- if ((responseHeaders['content-type'] || '').includes('application/json')) {
989
- json = JSON.parse(text);
990
- }
991
- }
992
- catch (e) { }
993
- const ja3 = this.generateJA3();
994
- resolve({
995
- statusCode,
996
- headers: responseHeaders,
997
- body,
998
- text,
999
- json,
1000
- timing,
1001
- fingerprints: {
1002
- ja3,
1003
- ja3Hash: crypto.createHash('md5').update(ja3).digest('hex'),
1004
- 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`
1005
- }
1006
- });
1007
- });
1008
- stream.on('error', (err) => {
1009
- stream.removeAllListeners();
1010
- reject(err);
1011
- });
1012
- if (options.body) {
1013
- const bodyBuffer = Buffer.isBuffer(options.body) ? options.body : Buffer.from(options.body);
1014
- stream.write(bodyBuffer);
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]);
1015
996
  }
1016
- stream.end();
1017
- });
1018
- }
1019
- generateJA3() {
1020
- const version = '771';
1021
- const ciphers = this.profile.tls.cipherSuites
1022
- .filter(c => !c.startsWith('GREASE_'))
1023
- .map(c => {
1024
- const map = {
1025
- 'TLS_AES_128_GCM_SHA256': '4865',
1026
- 'TLS_AES_256_GCM_SHA384': '4866',
1027
- 'TLS_CHACHA20_POLY1305_SHA256': '4867',
1028
- 'ECDHE-ECDSA-AES128-GCM-SHA256': '49195',
1029
- 'ECDHE-RSA-AES128-GCM-SHA256': '49199',
1030
- 'ECDHE-ECDSA-AES256-GCM-SHA384': '49196',
1031
- 'ECDHE-RSA-AES256-GCM-SHA384': '49200',
1032
- 'ECDHE-ECDSA-CHACHA20-POLY1305': '52393',
1033
- 'ECDHE-RSA-CHACHA20-POLY1305': '52392',
1034
- 'ECDHE-RSA-AES128-SHA': '49171',
1035
- 'ECDHE-RSA-AES256-SHA': '49172',
1036
- 'AES128-GCM-SHA256': '156',
1037
- 'AES256-GCM-SHA384': '157',
1038
- 'AES128-SHA': '47',
1039
- 'AES256-SHA': '53'
1040
- };
1041
- return map[c] || '0';
1042
- }).join('-');
1043
- const extensions = this.profile.tls.extensions
1044
- .filter(e => typeof e === 'number')
1045
- .join('-');
1046
- const groups = this.profile.tls.supportedGroups
1047
- .filter(g => !g.startsWith('GREASE_'))
1048
- .map(g => {
1049
- const map = {
1050
- 'X25519': '29',
1051
- 'prime256v1': '23',
1052
- 'secp384r1': '24',
1053
- 'secp521r1': '25',
1054
- 'X25519Kyber768': '25497'
1055
- };
1056
- return map[g] || '0';
1057
- }).join('-');
1058
- const ecFormats = this.profile.tls.ecPointFormats.join('-');
1059
- return `${version},${ciphers},${extensions},${groups},${ecFormats}`;
1060
- }
1061
- buildHeaders(hostname, path, options) {
1062
- const method = (options.method || 'GET').toUpperCase();
1063
- const headers = {
1064
- ':method': method,
1065
- ':authority': hostname,
1066
- ':scheme': 'https',
1067
- ':path': path
1068
- };
1069
- const defaultHeaders = {
1070
- 'sec-ch-ua': this.profile.secChUa,
1071
- 'sec-ch-ua-mobile': this.profile.secChUaMobile,
1072
- 'sec-ch-ua-platform': this.profile.secChUaPlatform,
1073
- 'upgrade-insecure-requests': this.profile.upgradeInsecureRequests || '1',
1074
- 'user-agent': this.profile.userAgent,
1075
- 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
1076
- 'sec-fetch-site': this.profile.secFetchSite,
1077
- 'sec-fetch-mode': this.profile.secFetchMode,
1078
- 'sec-fetch-dest': this.profile.secFetchDest,
1079
- 'accept-encoding': 'gzip, deflate, br, zstd',
1080
- 'accept-language': this.profile.acceptLanguage || 'en-US,en;q=0.9'
1081
- };
1082
- if (this.profile.priority) {
1083
- defaultHeaders['priority'] = this.profile.priority;
1084
- }
1085
- if (this.profile.http2.headerOrder) {
1086
- this.profile.http2.headerOrder.forEach(headerName => {
1087
- if (defaultHeaders[headerName]) {
1088
- headers[headerName] = defaultHeaders[headerName];
1089
- }
1090
- });
1091
997
  }
1092
- Object.keys(defaultHeaders).forEach(key => {
1093
- if (!headers[key]) {
1094
- headers[key] = defaultHeaders[key];
1095
- }
1096
- });
1097
- const cookies = options.cookies
1098
- ? Object.entries(options.cookies).map(([k, v]) => `${k}=${v}`).join('; ')
1099
- : this.cookieJar.getCookies(hostname, path);
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:');
1100
1015
  if (cookies)
1101
- headers['cookie'] = cookies;
1102
- if (options.headers)
1103
- Object.assign(headers, options.headers);
1104
- if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
1105
- if (!headers['content-type'])
1106
- headers['content-type'] = 'application/x-www-form-urlencoded';
1107
- if (options.body) {
1108
- const bodyLength = Buffer.isBuffer(options.body) ? options.body.length : Buffer.byteLength(options.body);
1109
- headers['content-length'] = bodyLength.toString();
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);
1110
1024
  }
1111
1025
  }
1112
- return headers;
1113
- }
1114
- isSessionAlive(session) {
1115
- try {
1116
- return session.http2Manager.isConnected();
1117
- }
1118
- catch {
1119
- return false;
1120
- }
1121
- }
1122
- getCookies(domain) {
1123
- return this.cookieJar.getCookies(domain);
1124
- }
1125
- setCookie(domain, name, value, options) {
1126
- this.cookieJar.setCookie(domain, name, value, options);
1026
+ return response;
1127
1027
  }
1128
1028
  destroy() {
1129
- if (this.cleanupInterval) {
1130
- clearInterval(this.cleanupInterval);
1131
- this.cleanupInterval = null;
1132
- }
1133
- this.sessionCache.forEach(session => {
1134
- try {
1135
- session.http2Manager.destroy();
1136
- session.tlsManager.destroy();
1137
- }
1138
- catch (e) { }
1029
+ clearInterval(this.cleanupInterval);
1030
+ this.sessionCache.forEach(s => {
1031
+ s.http2Manager.destroy();
1032
+ s.tlsManager.destroy();
1139
1033
  });
1140
1034
  this.sessionCache.clear();
1141
1035
  }