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/index.ts DELETED
@@ -1,1319 +0,0 @@
1
- import * as tls from 'tls';
2
- import * as net from 'net';
3
- import * as http2 from 'http2';
4
- import * as crypto from 'crypto';
5
- import { EventEmitter } from 'events';
6
- import * as zlib from 'zlib';
7
- import { promisify } from 'util';
8
-
9
- const gunzip = promisify(zlib.gunzip);
10
- const brotliDecompress = promisify(zlib.brotliDecompress);
11
-
12
- const GREASE_VALUES = [0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a, 0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa];
13
-
14
- interface TLSExtension {
15
- type: number;
16
- data?: Buffer | number[] | string[];
17
- }
18
-
19
- interface TLSFingerprint {
20
- cipherSuites: string[];
21
- extensions: number[];
22
- supportedGroups: string[];
23
- signatureAlgorithms: string[];
24
- supportedVersions: string[];
25
- ecPointFormats: number[];
26
- alpnProtocols: string[];
27
- pskKeyExchangeModes: number[];
28
- compressionMethods: number[];
29
- grease: boolean;
30
- recordSizeLimit?: number;
31
- }
32
-
33
- interface HTTP2Fingerprint {
34
- windowUpdate: number;
35
- headerTableSize: number;
36
- maxConcurrentStreams?: number;
37
- initialWindowSize: number;
38
- maxFrameSize: number;
39
- maxHeaderListSize?: number;
40
- enablePush: number;
41
- settingsOrder: number[];
42
- connectionPreface: Buffer;
43
- priorityFrames: Array<{
44
- streamId: number;
45
- weight: number;
46
- dependency: number;
47
- exclusive: boolean;
48
- }>;
49
- pseudoHeaderOrder: string[];
50
- headerPriority?: {
51
- streamDep: number;
52
- exclusive: boolean;
53
- weight: number;
54
- };
55
- }
56
-
57
- interface ChromeProfile {
58
- name: string;
59
- version: string;
60
- tls: TLSFingerprint;
61
- http2: HTTP2Fingerprint;
62
- userAgent: string;
63
- secChUa: string;
64
- secChUaPlatform: string;
65
- secChUaMobile: string;
66
- secFetchSite: string;
67
- secFetchMode: string;
68
- secFetchDest: string;
69
- }
70
-
71
- interface RequestOptions {
72
- method?: string;
73
- headers?: Record<string, string>;
74
- body?: string | Buffer;
75
- cookies?: Record<string, string>;
76
- decompress?: boolean;
77
- followRedirects?: boolean;
78
- maxRedirects?: number;
79
- }
80
-
81
- interface Response {
82
- statusCode: number;
83
- headers: Record<string, string | string[]>;
84
- body: Buffer;
85
- text?: string;
86
- json?: any;
87
- timing: {
88
- socket: number;
89
- lookup: number;
90
- connect: number;
91
- secureConnect: number;
92
- response: number;
93
- end: number;
94
- total: number;
95
- };
96
- fingerprints: {
97
- ja3: string;
98
- ja3Hash: string;
99
- akamai: string;
100
- };
101
- }
102
-
103
- class ProfileRegistry {
104
- private static profiles: Map<string, ChromeProfile> = new Map();
105
-
106
- private static getGrease(): number {
107
- const GREASE_VALUES = [0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a, 0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa];
108
- return GREASE_VALUES[Math.floor(Math.random() * GREASE_VALUES.length)];
109
- }
110
-
111
- static chromeMobile143Android(): ChromeProfile {
112
- const grease1 = this.getGrease();
113
- const grease2 = this.getGrease();
114
- const grease3 = this.getGrease();
115
- const grease4 = this.getGrease();
116
-
117
- return {
118
- name: 'chrome_mobile_143_android',
119
- version: '143.0.0.0',
120
- tls: {
121
- cipherSuites: [
122
- `GREASE_${grease1.toString(16)}`,
123
- 'TLS_AES_128_GCM_SHA256',
124
- 'TLS_AES_256_GCM_SHA384',
125
- 'TLS_CHACHA20_POLY1305_SHA256',
126
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
127
- 'ECDHE-RSA-AES128-GCM-SHA256',
128
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
129
- 'ECDHE-RSA-AES256-GCM-SHA384',
130
- 'ECDHE-ECDSA-CHACHA20-POLY1305',
131
- 'ECDHE-RSA-CHACHA20-POLY1305',
132
- 'ECDHE-RSA-AES128-SHA',
133
- 'ECDHE-RSA-AES256-SHA',
134
- 'AES128-GCM-SHA256',
135
- 'AES256-GCM-SHA384',
136
- 'AES128-SHA',
137
- 'AES256-SHA'
138
- ],
139
- extensions: [
140
- grease2,
141
- 0xfe0d,
142
- 0x001b,
143
- 0x002b,
144
- 0x0010,
145
- 0x0023,
146
- 0x002d,
147
- 0x000b,
148
- 0xff01,
149
- 0x0005,
150
- 0x000a,
151
- 0x0017,
152
- 0x0000,
153
- 0x44cd,
154
- 0x0033,
155
- 0x0012,
156
- 0x000d,
157
- grease3
158
- ],
159
- supportedGroups: [
160
- `GREASE_${grease4.toString(16)}`,
161
- 'X25519Kyber768',
162
- 'X25519',
163
- 'prime256v1',
164
- 'secp384r1'
165
- ],
166
- signatureAlgorithms: [
167
- 'ecdsa_secp256r1_sha256',
168
- 'rsa_pss_rsae_sha256',
169
- 'rsa_pkcs1_sha256',
170
- 'ecdsa_secp384r1_sha384',
171
- 'rsa_pss_rsae_sha384',
172
- 'rsa_pkcs1_sha384',
173
- 'rsa_pss_rsae_sha512',
174
- 'rsa_pkcs1_sha512'
175
- ],
176
- supportedVersions: [
177
- `GREASE_${grease2.toString(16)}`,
178
- 'TLSv1.3',
179
- 'TLSv1.2'
180
- ],
181
- ecPointFormats: [0x00],
182
- alpnProtocols: ['h2', 'http/1.1'],
183
- pskKeyExchangeModes: [0x01],
184
- compressionMethods: [0x00],
185
- grease: true,
186
- recordSizeLimit: 16385,
187
- encryptedClientHello: true,
188
- certificateCompression: [0x02],
189
- ocspStapling: true,
190
- signedCertificateTimestamp: true
191
- },
192
- http2: {
193
- windowUpdate: 15663105,
194
- headerTableSize: 65536,
195
- enablePush: 0,
196
- initialWindowSize: 6291456,
197
- maxHeaderListSize: 262144,
198
- settingsOrder: [1, 2, 4, 6],
199
- connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
200
- priorityFrames: [
201
- {
202
- streamId: 0,
203
- weight: 256,
204
- dependency: 0,
205
- exclusive: 1
206
- }
207
- ],
208
- pseudoHeaderOrder: [':method', ':authority', ':scheme', ':path'],
209
- headerOrder: [
210
- 'sec-ch-ua',
211
- 'sec-ch-ua-mobile',
212
- 'sec-ch-ua-platform',
213
- 'upgrade-insecure-requests',
214
- 'user-agent',
215
- 'accept',
216
- 'sec-fetch-site',
217
- 'sec-fetch-mode',
218
- 'sec-fetch-dest',
219
- 'accept-encoding',
220
- 'accept-language',
221
- 'priority'
222
- ]
223
- },
224
- userAgent: 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Mobile Safari/537.36',
225
- secChUa: '"Google Chrome";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
226
- secChUaPlatform: '"Android"',
227
- secChUaMobile: '?1',
228
- secFetchSite: 'none',
229
- secFetchMode: 'navigate',
230
- secFetchDest: 'document',
231
- upgradeInsecureRequests: '1',
232
- 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',
233
- priority: 'u=0, i'
234
- };
235
- }
236
-
237
- static chrome133PSK(): ChromeProfile {
238
- const grease1 = this.getGrease();
239
- const grease2 = this.getGrease();
240
- const grease3 = this.getGrease();
241
- const grease4 = this.getGrease();
242
-
243
- return {
244
- name: 'chrome_133_psk',
245
- version: '133.0.0.0',
246
- tls: {
247
- cipherSuites: [
248
- `GREASE_${grease1.toString(16)}`,
249
- 'TLS_AES_128_GCM_SHA256',
250
- 'TLS_AES_256_GCM_SHA384',
251
- 'TLS_CHACHA20_POLY1305_SHA256',
252
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
253
- 'ECDHE-RSA-AES128-GCM-SHA256',
254
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
255
- 'ECDHE-RSA-AES256-GCM-SHA384',
256
- 'ECDHE-ECDSA-CHACHA20-POLY1305',
257
- 'ECDHE-RSA-CHACHA20-POLY1305',
258
- 'ECDHE-RSA-AES128-SHA',
259
- 'ECDHE-RSA-AES256-SHA',
260
- 'AES128-GCM-SHA256',
261
- 'AES256-GCM-SHA384',
262
- 'AES128-SHA',
263
- 'AES256-SHA'
264
- ],
265
- extensions: [
266
- grease2,
267
- 0x0000,
268
- 0x0017,
269
- 0xff01,
270
- 0x000a,
271
- 0x000b,
272
- 0x0023,
273
- 0x0010,
274
- 0x0005,
275
- 0x000d,
276
- 0x0012,
277
- 0x0033,
278
- 0x002d,
279
- 0x002b,
280
- 0x001b,
281
- 0x0015,
282
- grease3
283
- ],
284
- supportedGroups: [
285
- `GREASE_${grease4.toString(16)}`,
286
- 'X25519Kyber768',
287
- 'X25519',
288
- 'prime256v1',
289
- 'secp384r1'
290
- ],
291
- signatureAlgorithms: [
292
- 'ecdsa_secp256r1_sha256',
293
- 'rsa_pss_rsae_sha256',
294
- 'rsa_pkcs1_sha256',
295
- 'ecdsa_secp384r1_sha384',
296
- 'rsa_pss_rsae_sha384',
297
- 'rsa_pkcs1_sha384',
298
- 'rsa_pss_rsae_sha512',
299
- 'rsa_pkcs1_sha512'
300
- ],
301
- supportedVersions: [
302
- `GREASE_${grease2.toString(16)}`,
303
- 'TLSv1.3',
304
- 'TLSv1.2'
305
- ],
306
- ecPointFormats: [0x00],
307
- alpnProtocols: ['h2', 'http/1.1'],
308
- pskKeyExchangeModes: [0x01],
309
- compressionMethods: [0x00],
310
- grease: true,
311
- recordSizeLimit: 16385
312
- },
313
- http2: {
314
- windowUpdate: 15663105,
315
- headerTableSize: 65536,
316
- enablePush: 0,
317
- initialWindowSize: 6291456,
318
- maxFrameSize: 16384,
319
- maxHeaderListSize: 262144,
320
- settingsOrder: [1, 2, 4, 6],
321
- connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
322
- priorityFrames: [
323
- {
324
- streamId: 0,
325
- weight: 256,
326
- dependency: 0,
327
- exclusive: 1
328
- }
329
- ],
330
- pseudoHeaderOrder: [':method', ':authority', ':scheme', ':path'],
331
- headerOrder: [
332
- 'cache-control',
333
- 'sec-ch-ua',
334
- 'sec-ch-ua-mobile',
335
- 'sec-ch-ua-platform',
336
- 'upgrade-insecure-requests',
337
- 'user-agent',
338
- 'accept',
339
- 'sec-fetch-site',
340
- 'sec-fetch-mode',
341
- 'sec-fetch-user',
342
- 'sec-fetch-dest',
343
- 'accept-encoding',
344
- 'accept-language'
345
- ]
346
- },
347
- userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
348
- secChUa: '"Google Chrome";v="133", "Chromium";v="133", "Not_A Brand";v="24"',
349
- secChUaPlatform: '"Windows"',
350
- secChUaMobile: '?0',
351
- secFetchSite: 'none',
352
- secFetchMode: 'navigate',
353
- secFetchDest: 'document'
354
- };
355
- }
356
-
357
- static chrome133(): ChromeProfile {
358
- const grease1 = this.getGrease();
359
- const grease2 = this.getGrease();
360
- const grease3 = this.getGrease();
361
- const grease4 = this.getGrease();
362
-
363
- return {
364
- name: 'chrome_133',
365
- version: '133.0.0.0',
366
- tls: {
367
- cipherSuites: [
368
- `GREASE_${grease1.toString(16)}`,
369
- 'TLS_AES_128_GCM_SHA256',
370
- 'TLS_AES_256_GCM_SHA384',
371
- 'TLS_CHACHA20_POLY1305_SHA256',
372
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
373
- 'ECDHE-RSA-AES128-GCM-SHA256',
374
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
375
- 'ECDHE-RSA-AES256-GCM-SHA384',
376
- 'ECDHE-ECDSA-CHACHA20-POLY1305',
377
- 'ECDHE-RSA-CHACHA20-POLY1305',
378
- 'ECDHE-RSA-AES128-SHA',
379
- 'ECDHE-RSA-AES256-SHA',
380
- 'AES128-GCM-SHA256',
381
- 'AES256-GCM-SHA384',
382
- 'AES128-SHA',
383
- 'AES256-SHA'
384
- ],
385
- extensions: [
386
- grease2,
387
- 0x0000,
388
- 0x0017,
389
- 0xff01,
390
- 0x000a,
391
- 0x000b,
392
- 0x0023,
393
- 0x0010,
394
- 0x0005,
395
- 0x000d,
396
- 0x0012,
397
- 0x0033,
398
- 0x002d,
399
- 0x002b,
400
- 0x001b,
401
- 0x0015,
402
- grease3
403
- ],
404
- supportedGroups: [
405
- `GREASE_${grease4.toString(16)}`,
406
- 'X25519Kyber768',
407
- 'X25519',
408
- 'prime256v1',
409
- 'secp384r1'
410
- ],
411
- signatureAlgorithms: [
412
- 'ecdsa_secp256r1_sha256',
413
- 'rsa_pss_rsae_sha256',
414
- 'rsa_pkcs1_sha256',
415
- 'ecdsa_secp384r1_sha384',
416
- 'rsa_pss_rsae_sha384',
417
- 'rsa_pkcs1_sha384',
418
- 'rsa_pss_rsae_sha512',
419
- 'rsa_pkcs1_sha512'
420
- ],
421
- supportedVersions: [
422
- `GREASE_${grease2.toString(16)}`,
423
- 'TLSv1.3',
424
- 'TLSv1.2'
425
- ],
426
- ecPointFormats: [0x00],
427
- alpnProtocols: ['h2', 'http/1.1'],
428
- pskKeyExchangeModes: [0x01],
429
- compressionMethods: [0x00],
430
- grease: true,
431
- recordSizeLimit: 16385
432
- },
433
- http2: {
434
- windowUpdate: 15663105,
435
- headerTableSize: 65536,
436
- enablePush: 0,
437
- initialWindowSize: 6291456,
438
- maxFrameSize: 16384,
439
- maxHeaderListSize: 262144,
440
- settingsOrder: [1, 2, 4, 6],
441
- connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
442
- priorityFrames: [
443
- {
444
- streamId: 0,
445
- weight: 256,
446
- dependency: 0,
447
- exclusive: 1
448
- }
449
- ],
450
- pseudoHeaderOrder: [':method', ':authority', ':scheme', ':path'],
451
- headerOrder: [
452
- 'cache-control',
453
- 'sec-ch-ua',
454
- 'sec-ch-ua-mobile',
455
- 'sec-ch-ua-platform',
456
- 'upgrade-insecure-requests',
457
- 'user-agent',
458
- 'accept',
459
- 'sec-fetch-site',
460
- 'sec-fetch-mode',
461
- 'sec-fetch-user',
462
- 'sec-fetch-dest',
463
- 'accept-encoding',
464
- 'accept-language'
465
- ]
466
- },
467
- userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36',
468
- secChUa: '"Google Chrome";v="133", "Chromium";v="133", "Not_A Brand";v="24"',
469
- secChUaPlatform: '"Windows"',
470
- secChUaMobile: '?0',
471
- secFetchSite: 'none',
472
- secFetchMode: 'navigate',
473
- secFetchDest: 'document'
474
- };
475
- }
476
-
477
- static firefox117(): ChromeProfile {
478
- return {
479
- name: 'firefox_117',
480
- version: '117.0',
481
- tls: {
482
- cipherSuites: [
483
- 'TLS_AES_128_GCM_SHA256',
484
- 'TLS_CHACHA20_POLY1305_SHA256',
485
- 'TLS_AES_256_GCM_SHA384',
486
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
487
- 'ECDHE-RSA-AES128-GCM-SHA256',
488
- 'ECDHE-ECDSA-CHACHA20-POLY1305',
489
- 'ECDHE-RSA-CHACHA20-POLY1305',
490
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
491
- 'ECDHE-RSA-AES256-GCM-SHA384',
492
- 'ECDHE-ECDSA-AES256-SHA',
493
- 'ECDHE-ECDSA-AES128-SHA',
494
- 'ECDHE-RSA-AES128-SHA',
495
- 'ECDHE-RSA-AES256-SHA',
496
- 'AES128-GCM-SHA256',
497
- 'AES256-GCM-SHA384',
498
- 'AES128-SHA',
499
- 'AES256-SHA'
500
- ],
501
- extensions: [0x0000, 0x0017, 0xff01, 0x000a, 0x000b, 0x0023, 0x0010, 0x0005, 0x0022, 0x0033, 0x002b, 0x000d, 0x002d, 0x0029, 0x0015],
502
- supportedGroups: ['X25519', 'prime256v1', 'secp384r1', 'secp521r1', 'ffdhe2048', 'ffdhe3072'],
503
- signatureAlgorithms: [
504
- 'ecdsa_secp256r1_sha256',
505
- 'ecdsa_secp384r1_sha384',
506
- 'ecdsa_secp521r1_sha512',
507
- 'rsa_pss_rsae_sha256',
508
- 'rsa_pss_rsae_sha384',
509
- 'rsa_pss_rsae_sha512',
510
- 'rsa_pkcs1_sha256',
511
- 'rsa_pkcs1_sha384',
512
- 'rsa_pkcs1_sha512',
513
- 'ecdsa_sha1',
514
- 'rsa_pkcs1_sha1'
515
- ],
516
- supportedVersions: ['TLSv1.3', 'TLSv1.2'],
517
- ecPointFormats: [0x00],
518
- alpnProtocols: ['h2', 'http/1.1'],
519
- pskKeyExchangeModes: [0x01],
520
- compressionMethods: [0x00],
521
- grease: false
522
- },
523
- http2: {
524
- windowUpdate: 12517377,
525
- headerTableSize: 65536,
526
- enablePush: 0,
527
- initialWindowSize: 131072,
528
- maxFrameSize: 16384,
529
- settingsOrder: [1, 4, 5],
530
- connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
531
- priorityFrames: [
532
- { streamId: 3, weight: 200, dependency: 0, exclusive: false },
533
- { streamId: 5, weight: 100, dependency: 0, exclusive: false },
534
- { streamId: 7, weight: 0, dependency: 0, exclusive: false },
535
- { streamId: 9, weight: 0, dependency: 7, exclusive: false },
536
- { streamId: 11, weight: 0, dependency: 3, exclusive: false },
537
- { streamId: 13, weight: 240, dependency: 0, exclusive: false }
538
- ],
539
- pseudoHeaderOrder: [':method', ':path', ':authority', ':scheme'],
540
- headerPriority: { streamDep: 13, exclusive: false, weight: 41 }
541
- },
542
- userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:117.0) Gecko/20100101 Firefox/117.0',
543
- secChUa: '',
544
- secChUaPlatform: '',
545
- secChUaMobile: '',
546
- secFetchSite: 'none',
547
- secFetchMode: 'navigate',
548
- secFetchDest: 'document'
549
- };
550
- }
551
-
552
- static safariIOS18(): ChromeProfile {
553
- const grease1 = this.getGrease();
554
- const grease2 = this.getGrease();
555
-
556
- return {
557
- name: 'safari_ios_18_0',
558
- version: '18.0',
559
- tls: {
560
- cipherSuites: [
561
- `GREASE_${grease1.toString(16)}`,
562
- 'TLS_AES_128_GCM_SHA256',
563
- 'TLS_AES_256_GCM_SHA384',
564
- 'TLS_CHACHA20_POLY1305_SHA256',
565
- 'ECDHE-ECDSA-AES256-GCM-SHA384',
566
- 'ECDHE-ECDSA-AES128-GCM-SHA256',
567
- 'ECDHE-ECDSA-CHACHA20-POLY1305',
568
- 'ECDHE-RSA-AES256-GCM-SHA384',
569
- 'ECDHE-RSA-AES128-GCM-SHA256',
570
- 'ECDHE-RSA-CHACHA20-POLY1305',
571
- 'ECDHE-ECDSA-AES256-SHA',
572
- 'ECDHE-ECDSA-AES128-SHA',
573
- 'ECDHE-RSA-AES256-SHA',
574
- 'ECDHE-RSA-AES128-SHA',
575
- 'AES256-GCM-SHA384',
576
- 'AES128-GCM-SHA256',
577
- 'AES256-SHA',
578
- 'AES128-SHA',
579
- 'ECDHE-ECDSA-DES-CBC3-SHA',
580
- 'ECDHE-RSA-DES-CBC3-SHA',
581
- 'DES-CBC3-SHA'
582
- ],
583
- extensions: [
584
- grease2,
585
- 0x0000,
586
- 0x0017,
587
- 0xff01,
588
- 0x000a,
589
- 0x000b,
590
- 0x0010,
591
- 0x0005,
592
- 0x000d,
593
- 0x0012,
594
- 0x0033,
595
- 0x002d,
596
- 0x002b,
597
- 0x0015
598
- ],
599
- supportedGroups: ['X25519', 'prime256v1', 'secp384r1', 'secp521r1'],
600
- signatureAlgorithms: [
601
- 'ecdsa_secp256r1_sha256',
602
- 'rsa_pss_rsae_sha256',
603
- 'rsa_pkcs1_sha256',
604
- 'ecdsa_secp384r1_sha384',
605
- 'ecdsa_sha1',
606
- 'rsa_pss_rsae_sha384',
607
- 'rsa_pss_rsae_sha384',
608
- 'rsa_pkcs1_sha384',
609
- 'rsa_pss_rsae_sha512',
610
- 'rsa_pkcs1_sha512',
611
- 'rsa_pkcs1_sha1'
612
- ],
613
- supportedVersions: ['TLSv1.3', 'TLSv1.2', 'TLSv1.1', 'TLSv1.0'],
614
- ecPointFormats: [0x00],
615
- alpnProtocols: ['h2', 'http/1.1'],
616
- pskKeyExchangeModes: [0x01],
617
- compressionMethods: [0x00],
618
- grease: true
619
- },
620
- http2: {
621
- windowUpdate: 10420225,
622
- headerTableSize: 4096,
623
- enablePush: 0,
624
- maxConcurrentStreams: 100,
625
- initialWindowSize: 2097152,
626
- settingsOrder: [2, 3, 4, 8, 9],
627
- connectionPreface: Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n'),
628
- priorityFrames: [],
629
- pseudoHeaderOrder: [':method', ':scheme', ':authority', ':path']
630
- },
631
- 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',
632
- secChUa: '',
633
- secChUaPlatform: '',
634
- secChUaMobile: '?1',
635
- secFetchSite: 'none',
636
- secFetchMode: 'navigate',
637
- secFetchDest: 'document'
638
- };
639
- }
640
-
641
- static get(name: string): ChromeProfile {
642
- switch(name.toLowerCase()) {
643
- case 'chrome_mobile_143_android': return this.chromeMobile143Android();
644
- case 'chrome_133_psk': return this.chrome133PSK();
645
- case 'chrome_133': return this.chrome133();
646
- case 'firefox_117': return this.firefox117();
647
- case 'safari_ios_18_0': return this.safariIOS18();
648
- default: return this.chrome133();
649
- }
650
- }
651
-
652
- static get latest(): ChromeProfile {
653
- return this.chrome133();
654
- }
655
-
656
- static get latestMobile(): ChromeProfile {
657
- return this.chromeMobile143Android();
658
- }
659
- }
660
-
661
-
662
- class CookieJar {
663
- private cookies: Map<string, Map<string, { value: string; expires?: Date; path: string; domain: string }>> = new Map();
664
-
665
- setCookie(domain: string, name: string, value: string, options: { expires?: Date; path?: string } = {}): void {
666
- if (!this.cookies.has(domain)) this.cookies.set(domain, new Map());
667
- this.cookies.get(domain)!.set(name, { value, expires: options.expires, path: options.path || '/', domain });
668
- }
669
-
670
- getCookies(domain: string, path: string = '/'): string {
671
- const domainCookies = this.cookies.get(domain);
672
- if (!domainCookies) return '';
673
- const validCookies: string[] = [];
674
- const now = new Date();
675
- for (const [name, cookie] of domainCookies) {
676
- if (cookie.expires && cookie.expires < now) {
677
- domainCookies.delete(name);
678
- continue;
679
- }
680
- if (path.startsWith(cookie.path)) validCookies.push(`${name}=${cookie.value}`);
681
- }
682
- return validCookies.join('; ');
683
- }
684
-
685
- parseSetCookie(domain: string, setCookieHeaders: string[]): void {
686
- for (const header of setCookieHeaders) {
687
- const parts = header.split(';').map(p => p.trim());
688
- const [nameValue] = parts;
689
- const [name, value] = nameValue.split('=');
690
- const options: { expires?: Date; path?: string } = { path: '/' };
691
- for (let i = 1; i < parts.length; i++) {
692
- const [key, val] = parts[i].split('=');
693
- if (key.toLowerCase() === 'expires') options.expires = new Date(val);
694
- else if (key.toLowerCase() === 'path') options.path = val;
695
- }
696
- this.setCookie(domain, name, value, options);
697
- }
698
- }
699
- }
700
-
701
- class TLSSocketManager extends EventEmitter {
702
- private socket: net.Socket | null = null;
703
- private tlsSocket: tls.TLSSocket | null = null;
704
- private profile: ChromeProfile;
705
- private timing: Response['timing'];
706
- private startTime: number = 0;
707
-
708
- constructor(profile: ChromeProfile) {
709
- super();
710
- this.profile = profile;
711
- this.timing = { socket: 0, lookup: 0, connect: 0, secureConnect: 0, response: 0, end: 0, total: 0 };
712
- }
713
-
714
- private convertCipherSuite(suite: string): string {
715
- const cipherMap: { [key: string]: string } = {
716
- 'TLS_AES_128_GCM_SHA256': 'TLS_AES_128_GCM_SHA256',
717
- 'TLS_AES_256_GCM_SHA384': 'TLS_AES_256_GCM_SHA384',
718
- 'TLS_CHACHA20_POLY1305_SHA256': 'TLS_CHACHA20_POLY1305_SHA256',
719
- 'ECDHE-ECDSA-AES128-GCM-SHA256': 'ECDHE-ECDSA-AES128-GCM-SHA256',
720
- 'ECDHE-RSA-AES128-GCM-SHA256': 'ECDHE-RSA-AES128-GCM-SHA256',
721
- 'ECDHE-ECDSA-AES256-GCM-SHA384': 'ECDHE-ECDSA-AES256-GCM-SHA384',
722
- 'ECDHE-RSA-AES256-GCM-SHA384': 'ECDHE-RSA-AES256-GCM-SHA384',
723
- 'ECDHE-ECDSA-CHACHA20-POLY1305': 'ECDHE-ECDSA-CHACHA20-POLY1305',
724
- 'ECDHE-RSA-CHACHA20-POLY1305': 'ECDHE-RSA-CHACHA20-POLY1305',
725
- 'ECDHE-RSA-AES128-SHA': 'ECDHE-RSA-AES128-SHA',
726
- 'ECDHE-RSA-AES256-SHA': 'ECDHE-RSA-AES256-SHA',
727
- 'AES128-GCM-SHA256': 'AES128-GCM-SHA256',
728
- 'AES256-GCM-SHA384': 'AES256-GCM-SHA384',
729
- 'AES128-SHA': 'AES128-SHA',
730
- 'AES256-SHA': 'AES256-SHA'
731
- };
732
-
733
- if (suite.startsWith('GREASE_')) {
734
- return '';
735
- }
736
-
737
- return cipherMap[suite] || suite;
738
- }
739
-
740
- private convertSupportedGroup(group: string): string {
741
- const groupMap: { [key: string]: string } = {
742
- 'X25519Kyber768': 'X25519',
743
- 'X25519': 'X25519',
744
- 'prime256v1': 'prime256v1',
745
- 'secp256r1': 'prime256v1',
746
- 'P-256': 'prime256v1',
747
- 'secp384r1': 'secp384r1',
748
- 'P-384': 'secp384r1',
749
- 'secp521r1': 'secp521r1',
750
- 'P-521': 'secp521r1'
751
- };
752
-
753
- if (group.startsWith('GREASE_')) {
754
- return '';
755
- }
756
-
757
- return groupMap[group] || group;
758
- }
759
-
760
- async connect(hostname: string, port: number): Promise<tls.TLSSocket> {
761
- this.startTime = Date.now();
762
- return new Promise((resolve, reject) => {
763
- const timeout = setTimeout(() => {
764
- this.destroy();
765
- reject(new Error('Connection timeout'));
766
- }, 30000);
767
-
768
- this.socket = new net.Socket();
769
- this.socket.setKeepAlive(true, 60000);
770
- this.socket.setNoDelay(true);
771
- this.timing.socket = Date.now() - this.startTime;
772
-
773
- this.socket.on('lookup', () => {
774
- this.timing.lookup = Date.now() - this.startTime;
775
- });
776
-
777
- this.socket.connect(port, hostname, () => {
778
- this.timing.connect = Date.now() - this.startTime;
779
-
780
- const ciphers = this.profile.tls.cipherSuites
781
- .map(suite => this.convertCipherSuite(suite))
782
- .filter(suite => suite !== '')
783
- .join(':');
784
-
785
- const curves = this.profile.tls.supportedGroups
786
- .map(group => this.convertSupportedGroup(group))
787
- .filter(group => group !== '')
788
- .join(':');
789
-
790
- const tlsOptions: tls.ConnectionOptions = {
791
- socket: this.socket!,
792
- servername: hostname,
793
- ALPNProtocols: this.profile.tls.alpnProtocols,
794
- ciphers: ciphers,
795
- minVersion: 'TLSv1.2' as any,
796
- maxVersion: 'TLSv1.3' as any,
797
- ecdhCurve: curves,
798
- sigalgs: this.profile.tls.signatureAlgorithms.join(':'),
799
- rejectUnauthorized: false,
800
- requestCert: false,
801
- honorCipherOrder: true,
802
- sessionTimeout: 300
803
- };
804
-
805
- this.tlsSocket = tls.connect(tlsOptions);
806
-
807
- this.tlsSocket.on('secureConnect', () => {
808
- clearTimeout(timeout);
809
- this.timing.secureConnect = Date.now() - this.startTime;
810
- resolve(this.tlsSocket!);
811
- });
812
-
813
- this.tlsSocket.on('error', (err) => {
814
- clearTimeout(timeout);
815
- this.destroy();
816
- reject(err);
817
- });
818
- });
819
-
820
- this.socket.on('error', (err) => {
821
- clearTimeout(timeout);
822
- this.destroy();
823
- reject(err);
824
- });
825
-
826
- this.socket.on('timeout', () => {
827
- clearTimeout(timeout);
828
- this.destroy();
829
- reject(new Error('Socket timeout'));
830
- });
831
- });
832
- }
833
-
834
- getTiming(): Response['timing'] {
835
- return { ...this.timing, total: Date.now() - this.startTime };
836
- }
837
-
838
- destroy(): void {
839
- if (this.tlsSocket && !this.tlsSocket.destroyed) {
840
- this.tlsSocket.removeAllListeners();
841
- this.tlsSocket.destroy();
842
- this.tlsSocket = null;
843
- }
844
- if (this.socket && !this.socket.destroyed) {
845
- this.socket.removeAllListeners();
846
- this.socket.destroy();
847
- this.socket = null;
848
- }
849
- }
850
-
851
- isConnected(): boolean {
852
- return this.tlsSocket !== null && !this.tlsSocket.destroyed;
853
- }
854
-
855
- getSocket(): tls.TLSSocket | null {
856
- return this.tlsSocket;
857
- }
858
-
859
- getCipherInfo(): any {
860
- return this.tlsSocket?.getCipher();
861
- }
862
-
863
- getProtocol(): string | undefined {
864
- return this.tlsSocket?.getProtocol();
865
- }
866
-
867
- getALPNProtocol(): string | false | null | undefined {
868
- return this.tlsSocket?.alpnProtocol;
869
- }
870
- }
871
-
872
- class HTTP2ClientManager {
873
- private client: http2.ClientHttp2Session | null = null;
874
- private profile: ChromeProfile;
875
- private tlsSocket: tls.TLSSocket;
876
-
877
- constructor(tlsSocket: tls.TLSSocket, profile: ChromeProfile) {
878
- this.tlsSocket = tlsSocket;
879
- this.profile = profile;
880
- }
881
-
882
- async createSession(): Promise<http2.ClientHttp2Session> {
883
- return new Promise((resolve, reject) => {
884
- const settingsMap: any = {};
885
-
886
- this.profile.http2.settingsOrder.forEach(settingId => {
887
- switch (settingId) {
888
- case 1:
889
- settingsMap.headerTableSize = this.profile.http2.headerTableSize;
890
- break;
891
- case 2:
892
- settingsMap.enablePush = this.profile.http2.enablePush === 1;
893
- break;
894
- case 3:
895
- if (this.profile.http2.maxConcurrentStreams !== undefined) {
896
- settingsMap.maxConcurrentStreams = this.profile.http2.maxConcurrentStreams;
897
- }
898
- break;
899
- case 4:
900
- settingsMap.initialWindowSize = this.profile.http2.initialWindowSize;
901
- break;
902
- case 5:
903
- if (this.profile.http2.maxFrameSize !== undefined) {
904
- settingsMap.maxFrameSize = this.profile.http2.maxFrameSize;
905
- }
906
- break;
907
- case 6:
908
- if (this.profile.http2.maxHeaderListSize !== undefined) {
909
- settingsMap.maxHeaderListSize = this.profile.http2.maxHeaderListSize;
910
- }
911
- break;
912
- }
913
- });
914
-
915
- const settings: http2.SecureClientSessionOptions = {
916
- createConnection: () => this.tlsSocket,
917
- settings: settingsMap
918
- };
919
-
920
- this.client = http2.connect(`https://${this.tlsSocket.servername}`, settings);
921
-
922
- this.client.on('connect', () => {
923
- resolve(this.client!);
924
- });
925
-
926
- this.client.on('error', reject);
927
- });
928
- }
929
-
930
- request(path: string, headers: Record<string, string>): http2.ClientHttp2Stream {
931
- if (!this.client) throw new Error('HTTP2 session not established');
932
-
933
- const orderedHeaders: Record<string, string> = {};
934
-
935
- this.profile.http2.pseudoHeaderOrder.forEach(pseudo => {
936
- if (headers[pseudo]) {
937
- orderedHeaders[pseudo] = headers[pseudo];
938
- }
939
- });
940
-
941
- if (this.profile.http2.headerOrder) {
942
- this.profile.http2.headerOrder.forEach(headerName => {
943
- if (headers[headerName] && !headerName.startsWith(':')) {
944
- orderedHeaders[headerName] = headers[headerName];
945
- }
946
- });
947
-
948
- Object.keys(headers).forEach(key => {
949
- if (!key.startsWith(':') && !orderedHeaders[key]) {
950
- orderedHeaders[key] = headers[key];
951
- }
952
- });
953
- } else {
954
- Object.keys(headers).forEach(key => {
955
- if (!key.startsWith(':') && !orderedHeaders[key]) {
956
- orderedHeaders[key] = headers[key];
957
- }
958
- });
959
- }
960
-
961
- const requestOptions: any = { ...orderedHeaders };
962
-
963
- if (this.profile.http2.priorityFrames && this.profile.http2.priorityFrames.length > 0) {
964
- const priority = this.profile.http2.priorityFrames[0];
965
- if (priority.weight) {
966
- requestOptions.weight = priority.weight;
967
- }
968
- if (priority.exclusive !== undefined) {
969
- requestOptions.exclusive = priority.exclusive === 1;
970
- }
971
- if (priority.dependency !== undefined && priority.dependency !== 0) {
972
- requestOptions.parent = priority.dependency;
973
- }
974
- }
975
-
976
- const stream = this.client.request(requestOptions);
977
-
978
- if (this.profile.http2.headerPriority) {
979
- try {
980
- stream.priority({
981
- parent: this.profile.http2.headerPriority.streamDep,
982
- weight: this.profile.http2.headerPriority.weight,
983
- exclusive: this.profile.http2.headerPriority.exclusive
984
- });
985
- } catch (e) {}
986
- }
987
-
988
- return stream;
989
- }
990
-
991
- getClient(): http2.ClientHttp2Session | null {
992
- return this.client;
993
- }
994
-
995
- isConnected(): boolean {
996
- return this.client !== null && !this.client.destroyed;
997
- }
998
-
999
- destroy(): void {
1000
- if (this.client && !this.client.destroyed) {
1001
- this.client.close();
1002
- this.client = null;
1003
- }
1004
- }
1005
- }
1006
-
1007
- class AdvancedTLSClient {
1008
- private profile: ChromeProfile;
1009
- private sessionCache: Map<string, { tlsManager: TLSSocketManager; http2Manager: HTTP2ClientManager; lastUsed: number }> = new Map();
1010
- private cookieJar: CookieJar = new CookieJar();
1011
- private maxCachedSessions: number = 10;
1012
- private sessionTimeout: number = 300000;
1013
- private cleanupInterval: NodeJS.Timeout | null = null;
1014
-
1015
- constructor(profileName?: string) {
1016
- this.profile = profileName ? ProfileRegistry.get(profileName) : ProfileRegistry.latest;
1017
- this.startSessionCleanup();
1018
- }
1019
-
1020
- private startSessionCleanup(): void {
1021
- this.cleanupInterval = setInterval(() => {
1022
- const now = Date.now();
1023
- for (const [key, session] of this.sessionCache) {
1024
- if (now - session.lastUsed > this.sessionTimeout) {
1025
- session.http2Manager.destroy();
1026
- session.tlsManager.destroy();
1027
- this.sessionCache.delete(key);
1028
- }
1029
- }
1030
- }, 60000);
1031
-
1032
- if (this.cleanupInterval.unref) {
1033
- this.cleanupInterval.unref();
1034
- }
1035
- }
1036
-
1037
- async request(url: string, options: RequestOptions = {}): Promise<Response> {
1038
- const maxRedirects = options.maxRedirects !== undefined ? options.maxRedirects : 5;
1039
- const followRedirects = options.followRedirects !== false;
1040
- let redirectCount = 0;
1041
- let currentUrl = url;
1042
- let response: Response;
1043
-
1044
- while (true) {
1045
- response = await this.performRequest(currentUrl, options);
1046
-
1047
- if (followRedirects && [301, 302, 303, 307, 308].includes(response.statusCode)) {
1048
- if (redirectCount >= maxRedirects) {
1049
- throw new Error(`Too many redirects (max: ${maxRedirects})`);
1050
- }
1051
-
1052
- const locationHeader = response.headers['location'];
1053
- if (!locationHeader) {
1054
- break;
1055
- }
1056
-
1057
- const location = Array.isArray(locationHeader) ? locationHeader[0] : locationHeader;
1058
- currentUrl = new URL(location, currentUrl).toString();
1059
- redirectCount++;
1060
-
1061
- if ([301, 302, 303].includes(response.statusCode)) {
1062
- options.method = 'GET';
1063
- delete options.body;
1064
- }
1065
- } else {
1066
- break;
1067
- }
1068
- }
1069
-
1070
- return response;
1071
- }
1072
-
1073
- private async performRequest(url: string, options: RequestOptions = {}): Promise<Response> {
1074
- const parsedUrl = new URL(url);
1075
- const hostname = parsedUrl.hostname;
1076
- const port = parsedUrl.port ? parseInt(parsedUrl.port) : 443;
1077
- const path = parsedUrl.pathname + parsedUrl.search;
1078
-
1079
- const cacheKey = `${hostname}:${port}`;
1080
- let session = this.sessionCache.get(cacheKey);
1081
-
1082
- if (!session || !this.isSessionAlive(session)) {
1083
- if (session) {
1084
- session.http2Manager.destroy();
1085
- session.tlsManager.destroy();
1086
- this.sessionCache.delete(cacheKey);
1087
- }
1088
-
1089
- const tlsManager = new TLSSocketManager(this.profile);
1090
- const tlsSocket = await tlsManager.connect(hostname, port);
1091
- const http2Manager = new HTTP2ClientManager(tlsSocket, this.profile);
1092
- await http2Manager.createSession();
1093
-
1094
- session = { tlsManager, http2Manager, lastUsed: Date.now() };
1095
-
1096
- if (this.sessionCache.size >= this.maxCachedSessions) {
1097
- const oldestKey = Array.from(this.sessionCache.entries())
1098
- .sort((a, b) => a[1].lastUsed - b[1].lastUsed)[0][0];
1099
- const oldest = this.sessionCache.get(oldestKey)!;
1100
- oldest.http2Manager.destroy();
1101
- oldest.tlsManager.destroy();
1102
- this.sessionCache.delete(oldestKey);
1103
- }
1104
-
1105
- this.sessionCache.set(cacheKey, session);
1106
- }
1107
-
1108
- session.lastUsed = Date.now();
1109
-
1110
- const headers = this.buildHeaders(hostname, path, options);
1111
- const stream = session.http2Manager.request(path, headers);
1112
-
1113
- return new Promise((resolve, reject) => {
1114
- const chunks: Buffer[] = [];
1115
- let responseHeaders: Record<string, string | string[]> = {};
1116
- let statusCode = 0;
1117
- const responseTime = Date.now();
1118
-
1119
- stream.on('response', (headers) => {
1120
- responseHeaders = headers;
1121
- statusCode = parseInt(headers[':status'] as string);
1122
- session!.tlsManager.getTiming().response = Date.now() - responseTime;
1123
-
1124
- const setCookieHeaders = headers['set-cookie'];
1125
- if (setCookieHeaders) {
1126
- const cookieArray = Array.isArray(setCookieHeaders) ? setCookieHeaders : [setCookieHeaders];
1127
- this.cookieJar.parseSetCookie(hostname, cookieArray);
1128
- }
1129
- });
1130
-
1131
- stream.on('data', (chunk) => chunks.push(chunk));
1132
-
1133
- stream.on('end', async () => {
1134
- stream.removeAllListeners();
1135
-
1136
- let body = Buffer.concat(chunks);
1137
- const timing = session!.tlsManager.getTiming();
1138
- timing.end = Date.now();
1139
-
1140
- if (options.decompress !== false) {
1141
- const encoding = responseHeaders['content-encoding'];
1142
- if (encoding === 'gzip') body = await gunzip(body);
1143
- else if (encoding === 'br') body = await brotliDecompress(body);
1144
- }
1145
-
1146
- let text: string | undefined;
1147
- let json: any;
1148
-
1149
- try {
1150
- text = body.toString('utf-8');
1151
- if (responseHeaders['content-type']?.toString().includes('application/json')) {
1152
- json = JSON.parse(text);
1153
- }
1154
- } catch (e) {}
1155
-
1156
- const ja3 = this.generateJA3();
1157
-
1158
- resolve({
1159
- statusCode,
1160
- headers: responseHeaders,
1161
- body,
1162
- text,
1163
- json,
1164
- timing,
1165
- fingerprints: {
1166
- ja3,
1167
- ja3Hash: crypto.createHash('md5').update(ja3).digest('hex'),
1168
- 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`
1169
- }
1170
- });
1171
- });
1172
-
1173
- stream.on('error', (err) => {
1174
- stream.removeAllListeners();
1175
- reject(err);
1176
- });
1177
-
1178
- if (options.body) {
1179
- const bodyBuffer = Buffer.isBuffer(options.body) ? options.body : Buffer.from(options.body);
1180
- stream.write(bodyBuffer);
1181
- }
1182
-
1183
- stream.end();
1184
- });
1185
- }
1186
-
1187
- private generateJA3(): string {
1188
- const version = '771';
1189
- const ciphers = this.profile.tls.cipherSuites
1190
- .filter(c => !c.startsWith('GREASE_'))
1191
- .map(c => {
1192
- const map: any = {
1193
- 'TLS_AES_128_GCM_SHA256': '4865',
1194
- 'TLS_AES_256_GCM_SHA384': '4866',
1195
- 'TLS_CHACHA20_POLY1305_SHA256': '4867',
1196
- 'ECDHE-ECDSA-AES128-GCM-SHA256': '49195',
1197
- 'ECDHE-RSA-AES128-GCM-SHA256': '49199',
1198
- 'ECDHE-ECDSA-AES256-GCM-SHA384': '49196',
1199
- 'ECDHE-RSA-AES256-GCM-SHA384': '49200',
1200
- 'ECDHE-ECDSA-CHACHA20-POLY1305': '52393',
1201
- 'ECDHE-RSA-CHACHA20-POLY1305': '52392',
1202
- 'ECDHE-RSA-AES128-SHA': '49171',
1203
- 'ECDHE-RSA-AES256-SHA': '49172',
1204
- 'AES128-GCM-SHA256': '156',
1205
- 'AES256-GCM-SHA384': '157',
1206
- 'AES128-SHA': '47',
1207
- 'AES256-SHA': '53'
1208
- };
1209
- return map[c] || '0';
1210
- }).join('-');
1211
-
1212
- const extensions = this.profile.tls.extensions
1213
- .filter(e => typeof e === 'number')
1214
- .join('-');
1215
- const groups = this.profile.tls.supportedGroups
1216
- .filter(g => !g.startsWith('GREASE_'))
1217
- .map(g => {
1218
- const map: any = {
1219
- 'X25519': '29',
1220
- 'prime256v1': '23',
1221
- 'secp384r1': '24',
1222
- 'secp521r1': '25',
1223
- 'X25519Kyber768': '25497'
1224
- };
1225
- return map[g] || '0';
1226
- }).join('-');
1227
- const ecFormats = this.profile.tls.ecPointFormats.join('-');
1228
-
1229
- return `${version},${ciphers},${extensions},${groups},${ecFormats}`;
1230
- }
1231
-
1232
- private buildHeaders(hostname: string, path: string, options: RequestOptions): Record<string, string> {
1233
- const method = (options.method || 'GET').toUpperCase();
1234
-
1235
- const headers: Record<string, string> = {
1236
- ':method': method,
1237
- ':authority': hostname,
1238
- ':scheme': 'https',
1239
- ':path': path
1240
- };
1241
-
1242
- if (this.profile.http2.headerOrder) {
1243
- const defaultHeaders: Record<string, string> = {
1244
- 'sec-ch-ua': this.profile.secChUa,
1245
- 'sec-ch-ua-mobile': this.profile.secChUaMobile,
1246
- 'sec-ch-ua-platform': this.profile.secChUaPlatform,
1247
- 'upgrade-insecure-requests': this.profile.upgradeInsecureRequests || '1',
1248
- 'user-agent': this.profile.userAgent,
1249
- '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',
1250
- 'sec-fetch-site': this.profile.secFetchSite,
1251
- 'sec-fetch-mode': this.profile.secFetchMode,
1252
- 'sec-fetch-dest': this.profile.secFetchDest,
1253
- 'accept-encoding': 'gzip, deflate, br, zstd',
1254
- 'accept-language': this.profile.acceptLanguage || 'en-US,en;q=0.9'
1255
- };
1256
-
1257
- if (this.profile.priority) {
1258
- defaultHeaders['priority'] = this.profile.priority;
1259
- }
1260
-
1261
- this.profile.http2.headerOrder.forEach(headerName => {
1262
- if (defaultHeaders[headerName]) {
1263
- headers[headerName] = defaultHeaders[headerName];
1264
- }
1265
- });
1266
- }
1267
-
1268
- const cookies = options.cookies
1269
- ? Object.entries(options.cookies).map(([k, v]) => `${k}=${v}`).join('; ')
1270
- : this.cookieJar.getCookies(hostname, path);
1271
-
1272
- if (cookies) headers['cookie'] = cookies;
1273
-
1274
- if (options.headers) Object.assign(headers, options.headers);
1275
-
1276
- if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
1277
- if (!headers['content-type']) headers['content-type'] = 'application/x-www-form-urlencoded';
1278
- if (options.body) {
1279
- const bodyLength = Buffer.isBuffer(options.body) ? options.body.length : Buffer.byteLength(options.body);
1280
- headers['content-length'] = bodyLength.toString();
1281
- }
1282
- }
1283
-
1284
- return headers;
1285
- }
1286
-
1287
- private isSessionAlive(session: { http2Manager: HTTP2ClientManager }): boolean {
1288
- try {
1289
- return session.http2Manager.isConnected();
1290
- } catch {
1291
- return false;
1292
- }
1293
- }
1294
-
1295
- getCookies(domain: string): string {
1296
- return this.cookieJar.getCookies(domain);
1297
- }
1298
-
1299
- setCookie(domain: string, name: string, value: string, options?: { expires?: Date; path?: string }): void {
1300
- this.cookieJar.setCookie(domain, name, value, options);
1301
- }
1302
-
1303
- destroy(): void {
1304
- if (this.cleanupInterval) {
1305
- clearInterval(this.cleanupInterval);
1306
- this.cleanupInterval = null;
1307
- }
1308
-
1309
- this.sessionCache.forEach(session => {
1310
- try {
1311
- session.http2Manager.destroy();
1312
- session.tlsManager.destroy();
1313
- } catch (e) {}
1314
- });
1315
- this.sessionCache.clear();
1316
- }
1317
- }
1318
-
1319
- export { AdvancedTLSClient };