rezo 1.0.36 → 1.0.37

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.
Files changed (43) hide show
  1. package/dist/adapters/curl.cjs +320 -9
  2. package/dist/adapters/curl.js +320 -9
  3. package/dist/adapters/entries/curl.d.ts +20 -2
  4. package/dist/adapters/entries/fetch.d.ts +20 -2
  5. package/dist/adapters/entries/http.d.ts +20 -2
  6. package/dist/adapters/entries/http2.d.ts +20 -2
  7. package/dist/adapters/entries/react-native.d.ts +20 -2
  8. package/dist/adapters/entries/xhr.d.ts +20 -2
  9. package/dist/adapters/fetch.cjs +10 -2
  10. package/dist/adapters/fetch.js +10 -2
  11. package/dist/adapters/http.cjs +204 -35
  12. package/dist/adapters/http.js +204 -35
  13. package/dist/adapters/http2.cjs +10 -2
  14. package/dist/adapters/http2.js +10 -2
  15. package/dist/adapters/index.cjs +6 -6
  16. package/dist/adapters/react-native.cjs +10 -2
  17. package/dist/adapters/react-native.js +10 -2
  18. package/dist/adapters/xhr.cjs +9 -1
  19. package/dist/adapters/xhr.js +9 -1
  20. package/dist/cache/index.cjs +13 -13
  21. package/dist/crawler.d.ts +20 -2
  22. package/dist/entries/crawler.cjs +5 -5
  23. package/dist/index.cjs +24 -24
  24. package/dist/index.d.ts +20 -2
  25. package/dist/platform/browser.d.ts +20 -2
  26. package/dist/platform/bun.d.ts +20 -2
  27. package/dist/platform/deno.d.ts +20 -2
  28. package/dist/platform/node.d.ts +20 -2
  29. package/dist/platform/react-native.d.ts +20 -2
  30. package/dist/platform/worker.d.ts +20 -2
  31. package/dist/plugin/index.cjs +36 -36
  32. package/dist/proxy/index.cjs +4 -4
  33. package/dist/queue/index.cjs +8 -8
  34. package/dist/responses/universal/index.cjs +11 -11
  35. package/dist/utils/agent-pool.cjs +204 -0
  36. package/dist/utils/agent-pool.js +201 -0
  37. package/dist/utils/http-config.cjs +24 -7
  38. package/dist/utils/http-config.js +24 -7
  39. package/dist/utils/index.cjs +2 -0
  40. package/dist/utils/index.js +2 -0
  41. package/dist/utils/staged-timeout.cjs +143 -0
  42. package/dist/utils/staged-timeout.js +139 -0
  43. package/package.json +1 -1
@@ -197,7 +197,15 @@ function buildUrlTree(config, finalUrl) {
197
197
  const urlStr = typeof config.url === "string" ? config.url : config.url.toString();
198
198
  urls.push(urlStr);
199
199
  }
200
- if (finalUrl && (urls.length === 0 || urls[0] !== finalUrl)) {
200
+ if (config.redirectHistory && config.redirectHistory.length > 0) {
201
+ for (const redirect of config.redirectHistory) {
202
+ const redirectUrl = typeof redirect.url === "string" ? redirect.url : redirect.url?.toString?.() || "";
203
+ if (redirectUrl && urls[urls.length - 1] !== redirectUrl) {
204
+ urls.push(redirectUrl);
205
+ }
206
+ }
207
+ }
208
+ if (finalUrl && (urls.length === 0 || urls[urls.length - 1] !== finalUrl)) {
201
209
  urls.push(finalUrl);
202
210
  }
203
211
  return urls.length > 0 ? urls : [finalUrl];
@@ -264,7 +272,7 @@ async function parseCookiesFromHeaders(headers, url, config) {
264
272
  const acceptedCookieStrings = acceptedCookies.map((c) => c.toSetCookieString());
265
273
  const jar = new RezoCookieJar;
266
274
  jar.setCookiesSync(acceptedCookieStrings, url);
267
- if (config?.enableCookieJar && config?.cookieJar) {
275
+ if (!config?.disableCookieJar && config?.cookieJar) {
268
276
  config.cookieJar.setCookiesSync(acceptedCookieStrings, url);
269
277
  }
270
278
  const cookies = jar.cookies();
@@ -197,7 +197,15 @@ function buildUrlTree(config, finalUrl) {
197
197
  const urlStr = typeof config.url === "string" ? config.url : config.url.toString();
198
198
  urls.push(urlStr);
199
199
  }
200
- if (finalUrl && (urls.length === 0 || urls[0] !== finalUrl)) {
200
+ if (config.redirectHistory && config.redirectHistory.length > 0) {
201
+ for (const redirect of config.redirectHistory) {
202
+ const redirectUrl = typeof redirect.url === "string" ? redirect.url : redirect.url?.toString?.() || "";
203
+ if (redirectUrl && urls[urls.length - 1] !== redirectUrl) {
204
+ urls.push(redirectUrl);
205
+ }
206
+ }
207
+ }
208
+ if (finalUrl && (urls.length === 0 || urls[urls.length - 1] !== finalUrl)) {
201
209
  urls.push(finalUrl);
202
210
  }
203
211
  return urls.length > 0 ? urls : [finalUrl];
@@ -264,7 +272,7 @@ async function parseCookiesFromHeaders(headers, url, config) {
264
272
  const acceptedCookieStrings = acceptedCookies.map((c) => c.toSetCookieString());
265
273
  const jar = new RezoCookieJar;
266
274
  jar.setCookiesSync(acceptedCookieStrings, url);
267
- if (config?.enableCookieJar && config?.cookieJar) {
275
+ if (!config?.disableCookieJar && config?.cookieJar) {
268
276
  config.cookieJar.setCookiesSync(acceptedCookieStrings, url);
269
277
  }
270
278
  const cookies = jar.cookies();
@@ -19,6 +19,8 @@ const { buildDownloadError, buildDecompressionError, buildSmartError, builErrorF
19
19
  const { isSameDomain, RezoPerformance } = require('../utils/tools.cjs');
20
20
  const { getGlobalDNSCache } = require('../cache/dns-cache.cjs');
21
21
  const { ResponseCache } = require('../cache/response-cache.cjs');
22
+ const { getGlobalAgentPool } = require('../utils/agent-pool.cjs');
23
+ const { StagedTimeoutManager, parseStagedTimeouts } = require('../utils/staged-timeout.cjs');
22
24
  const dns = require("dns");
23
25
  const debugLog = {
24
26
  requestStart: (config, url, method) => {
@@ -185,7 +187,15 @@ function buildUrlTree(config, finalUrl) {
185
187
  const urlStr = typeof config.url === "string" ? config.url : config.url.toString();
186
188
  urls.push(urlStr);
187
189
  }
188
- if (finalUrl && (urls.length === 0 || urls[0] !== finalUrl)) {
190
+ if (config.redirectHistory && config.redirectHistory.length > 0) {
191
+ for (const redirect of config.redirectHistory) {
192
+ const redirectUrl = typeof redirect.url === "string" ? redirect.url : redirect.url?.toString?.() || "";
193
+ if (redirectUrl && urls[urls.length - 1] !== redirectUrl) {
194
+ urls.push(redirectUrl);
195
+ }
196
+ }
197
+ }
198
+ if (finalUrl && (urls.length === 0 || urls[urls.length - 1] !== finalUrl)) {
189
199
  urls.push(finalUrl);
190
200
  }
191
201
  return urls.length > 0 ? urls : [finalUrl];
@@ -466,7 +476,8 @@ async function executeHttp1Request(fetchOptions, config, options, perform, fs, s
466
476
  status: response.status,
467
477
  headers: response.headers,
468
478
  sameDomain: isSameDomain(fetchOptions.fullUrl, _stats.redirectUrl),
469
- method: fetchOptions.method.toUpperCase()
479
+ method: fetchOptions.method.toUpperCase(),
480
+ body: config.originalBody
470
481
  }) : undefined;
471
482
  if (typeof onRedirect !== "undefined") {
472
483
  if (typeof onRedirect === "boolean") {
@@ -478,7 +489,7 @@ async function executeHttp1Request(fetchOptions, config, options, perform, fs, s
478
489
  config.errors.push({ attempt: config.retryAttempts + 1, error: redirectError, duration: perform.now() });
479
490
  throw redirectError;
480
491
  }
481
- } else if (!onRedirect.redirect) {
492
+ } else if (!onRedirect.redirect && !onRedirect.withoutBody && !("body" in onRedirect)) {
482
493
  const redirectError = builErrorFromResponse("Redirect denied by user", response, config, fetchOptions);
483
494
  _stats.statusOnNext = "error";
484
495
  if (!config.errors)
@@ -517,24 +528,53 @@ async function executeHttp1Request(fetchOptions, config, options, perform, fs, s
517
528
  delete options.params;
518
529
  const fromUrl = fetchOptions.fullUrl;
519
530
  fetchOptions.fullUrl = location;
520
- if (typeof onRedirect === "object" && onRedirect.redirect) {
521
- const method = onRedirect.method || fetchOptions.method;
531
+ const normalizedRedirect = typeof onRedirect === "object" ? onRedirect.redirect || onRedirect.withoutBody || "body" in onRedirect : undefined;
532
+ if (typeof onRedirect === "object" && normalizedRedirect) {
533
+ const method = onRedirect.redirect ? onRedirect.method || fetchOptions.method : fetchOptions.method;
522
534
  config.method = method;
523
- options.fullUrl = onRedirect.url;
524
- fetchOptions.fullUrl = onRedirect.url;
535
+ if (onRedirect.redirect && onRedirect.url) {
536
+ options.fullUrl = onRedirect.url;
537
+ fetchOptions.fullUrl = onRedirect.url;
538
+ }
525
539
  if (onRedirect.withoutBody) {
526
540
  delete options.body;
527
- } else if (onRedirect.body) {
541
+ delete fetchOptions.body;
542
+ config.originalBody = undefined;
543
+ if (fetchOptions.headers instanceof RezoHeaders) {
544
+ fetchOptions.headers.delete("Content-Type");
545
+ fetchOptions.headers.delete("Content-Length");
546
+ }
547
+ } else if ("body" in onRedirect) {
528
548
  options.body = onRedirect.body;
549
+ fetchOptions.body = onRedirect.body;
550
+ config.originalBody = onRedirect.body;
551
+ } else if (redirectCode === 307 || redirectCode === 308) {
552
+ const methodUpper = method.toUpperCase();
553
+ if ((methodUpper === "POST" || methodUpper === "PUT" || methodUpper === "PATCH") && config.originalBody !== undefined) {
554
+ options.body = config.originalBody;
555
+ fetchOptions.body = config.originalBody;
556
+ }
557
+ } else {
558
+ delete options.body;
559
+ delete fetchOptions.body;
560
+ if (fetchOptions.headers instanceof RezoHeaders) {
561
+ fetchOptions.headers.delete("Content-Type");
562
+ fetchOptions.headers.delete("Content-Length");
563
+ }
529
564
  }
530
565
  debugLog.redirect(config, fromUrl, fetchOptions.fullUrl, redirectCode, method);
531
- if (onRedirect.setHeaders) {
566
+ if (onRedirect.redirect && onRedirect.setHeaders) {
532
567
  addedOptions.customHeaders = onRedirect.setHeaders;
533
568
  }
534
569
  } else if (response.status === 301 || response.status === 302 || response.status === 303) {
535
570
  debugLog.redirect(config, fromUrl, fetchOptions.fullUrl, redirectCode, "GET");
536
571
  options.method = "GET";
537
572
  delete options.body;
573
+ delete fetchOptions.body;
574
+ if (fetchOptions.headers instanceof RezoHeaders) {
575
+ fetchOptions.headers.delete("Content-Type");
576
+ fetchOptions.headers.delete("Content-Length");
577
+ }
538
578
  } else {
539
579
  debugLog.redirect(config, fromUrl, fetchOptions.fullUrl, redirectCode, fetchOptions.method);
540
580
  }
@@ -603,8 +643,17 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
603
643
  eventEmitter.emit("start", startEvent);
604
644
  }
605
645
  const requestOptions = buildHTTPOptions(fetchOptions, isSecure, url);
646
+ const stagedTimeoutConfig = parseStagedTimeouts(fetchOptions.timeout);
647
+ const timeoutManager = new StagedTimeoutManager(stagedTimeoutConfig, config, fetchOptions);
648
+ if (timeoutManager.hasPhase("total")) {
649
+ timeoutManager.startPhase("total");
650
+ }
606
651
  try {
607
652
  const req = httpModule.request(requestOptions, async (res) => {
653
+ timeoutManager.clearPhase("headers");
654
+ if (timeoutManager.hasPhase("body")) {
655
+ timeoutManager.startPhase("body");
656
+ }
608
657
  if (!timing.firstByteTime) {
609
658
  timing.firstByteTime = performance.now();
610
659
  config.timing.responseStart = timing.firstByteTime;
@@ -896,9 +945,11 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
896
945
  uploadResult.emit("done", uploadFinishEvent);
897
946
  uploadResult._markFinished();
898
947
  }
948
+ timeoutManager.clearAll();
899
949
  resolve(finalResponse);
900
950
  });
901
951
  decompressedStream.on("error", (err) => {
952
+ timeoutManager.clearAll();
902
953
  _stats.statusOnNext = "error";
903
954
  updateTiming(config, timing, contentLength || "", contentLengthCounter, res.rawHeaders);
904
955
  if (_stats.redirectUrl) {
@@ -924,12 +975,36 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
924
975
  }
925
976
  });
926
977
  req.on("error", (err) => {
978
+ timeoutManager.clearAll();
927
979
  _stats.statusOnNext = "error";
928
980
  const error = buildSmartError(config, fetchOptions, err);
929
981
  resolve(error);
930
982
  });
931
983
  req.on("socket", (socket) => {
984
+ timeoutManager.setSocket(socket);
985
+ timeoutManager.setRequest(req);
986
+ timeoutManager.setTimeoutCallback((phase, elapsed) => {
987
+ _stats.statusOnNext = "error";
988
+ const error = timeoutManager.createTimeoutError(phase, elapsed);
989
+ const eventEmitter = streamResult || downloadResult || uploadResult;
990
+ if (eventEmitter) {
991
+ eventEmitter.emit("error", error);
992
+ }
993
+ resolve(error);
994
+ });
995
+ const isAlreadyConnected = !socket.connecting && (socket.readyState === "open" || socket.writable === true);
996
+ if (isAlreadyConnected) {
997
+ timeoutManager.clearPhase("connect");
998
+ if (timeoutManager.hasPhase("headers")) {
999
+ timeoutManager.startPhase("headers");
1000
+ }
1001
+ } else {
1002
+ if (timeoutManager.hasPhase("connect")) {
1003
+ timeoutManager.startPhase("connect");
1004
+ }
1005
+ }
932
1006
  socket.on("error", (err) => {
1007
+ timeoutManager.clearAll();
933
1008
  _stats.statusOnNext = "error";
934
1009
  const error = buildSmartError(config, fetchOptions, err);
935
1010
  resolve(error);
@@ -963,6 +1038,10 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
963
1038
  }
964
1039
  });
965
1040
  socket.on("secureConnect", () => {
1041
+ timeoutManager.clearPhase("connect");
1042
+ if (timeoutManager.hasPhase("headers")) {
1043
+ timeoutManager.startPhase("headers");
1044
+ }
966
1045
  if (!timing.tlsEnd && timing.tlsStart) {
967
1046
  timing.tlsEnd = performance.now();
968
1047
  config.timing.connectEnd = timing.tlsEnd;
@@ -1015,6 +1094,12 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
1015
1094
  }
1016
1095
  });
1017
1096
  socket.on("connect", () => {
1097
+ if (!isSecure) {
1098
+ timeoutManager.clearPhase("connect");
1099
+ if (timeoutManager.hasPhase("headers")) {
1100
+ timeoutManager.startPhase("headers");
1101
+ }
1102
+ }
1018
1103
  if (!timing.tcpEnd) {
1019
1104
  timing.tcpEnd = performance.now();
1020
1105
  config.timing.connectEnd = timing.tcpEnd;
@@ -1052,6 +1137,7 @@ async function request(config, fetchOptions, requestCount, timing, _stats, respo
1052
1137
  });
1053
1138
  });
1054
1139
  req.on("error", (error) => {
1140
+ timeoutManager.clearAll();
1055
1141
  _stats.statusOnNext = "error";
1056
1142
  updateTiming(config, timing, "", 0);
1057
1143
  const e = buildSmartError(config, fetchOptions, error);
@@ -1198,20 +1284,42 @@ function buildHTTPOptions(fetchOptions, isSecure, url) {
1198
1284
  useSecureContext = true,
1199
1285
  auth,
1200
1286
  dnsCache: dnsCacheOption,
1201
- keepAlive = false,
1202
- keepAliveMsecs = 60000
1287
+ keepAlive = true,
1288
+ keepAliveMsecs = 60000,
1289
+ useAgentPool = true
1203
1290
  } = fetchOptions;
1204
- const secureContext = isSecure && useSecureContext ? new https.Agent({
1205
- secureContext: createSecureContext(),
1206
- servername: url.host,
1207
- rejectUnauthorized,
1208
- keepAlive,
1209
- keepAliveMsecs: keepAlive ? keepAliveMsecs : undefined
1210
- }) : undefined;
1211
- const customAgent = url.protocol === "https:" && httpsAgent ? httpsAgent : httpAgent ? httpAgent : undefined;
1212
- const agent = parseProxy(proxy, isSecure, rejectUnauthorized) || customAgent || secureContext;
1291
+ let agent;
1292
+ if (httpAgent || httpsAgent) {
1293
+ agent = isSecure ? httpsAgent : httpAgent;
1294
+ } else if (proxy) {
1295
+ agent = parseProxy(proxy, isSecure, rejectUnauthorized);
1296
+ } else if (useAgentPool) {
1297
+ const agentPool = getGlobalAgentPool({
1298
+ keepAlive: true,
1299
+ keepAliveMsecs,
1300
+ maxSockets: 256,
1301
+ maxFreeSockets: 64,
1302
+ dnsCache: dnsCacheOption !== false
1303
+ });
1304
+ if (isSecure) {
1305
+ agent = agentPool.getHttpsAgent({
1306
+ rejectUnauthorized,
1307
+ servername: url.hostname
1308
+ });
1309
+ } else {
1310
+ agent = agentPool.getHttpAgent();
1311
+ }
1312
+ } else if (isSecure && useSecureContext) {
1313
+ agent = new https.Agent({
1314
+ secureContext: createSecureContext(),
1315
+ servername: url.hostname,
1316
+ rejectUnauthorized,
1317
+ keepAlive,
1318
+ keepAliveMsecs: keepAlive ? keepAliveMsecs : undefined
1319
+ });
1320
+ }
1213
1321
  let lookup;
1214
- if (dnsCacheOption) {
1322
+ if (dnsCacheOption !== false && !useAgentPool) {
1215
1323
  if (!dnsCache) {
1216
1324
  const cacheOptions = typeof dnsCacheOption === "object" ? {
1217
1325
  enable: true,
@@ -1275,7 +1383,7 @@ async function setInitialConfig(config, fetchOptions, isSecure, url, httpModule,
1275
1383
  http1: true,
1276
1384
  http2: config.http2,
1277
1385
  compression: true,
1278
- cookies: config.enableCookieJar,
1386
+ cookies: !config.disableCookieJar,
1279
1387
  redirects: config.maxRedirects > 0,
1280
1388
  proxy: !!proxy,
1281
1389
  timeout: !!timeout,
@@ -1285,7 +1393,7 @@ async function setInitialConfig(config, fetchOptions, isSecure, url, httpModule,
1285
1393
  config.features = {
1286
1394
  http2: !!config.http2,
1287
1395
  compression: !!config.compression?.enabled,
1288
- cookies: !!config.enableCookieJar,
1396
+ cookies: !config.disableCookieJar,
1289
1397
  redirects: config.maxRedirects > 0,
1290
1398
  proxy: !!proxy,
1291
1399
  timeout: !!timeout,
@@ -1383,10 +1491,34 @@ function emitRedirect(emitter, headers, status, statusText, sourceUri, destinati
1383
1491
  }
1384
1492
  function createSecureContext() {
1385
1493
  return tls.createSecureContext({
1386
- ecdhCurve: "X25519:prime256v1:secp384r1:secp521r1",
1387
- honorCipherOrder: true,
1388
- ciphers: "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA",
1389
- sigalgs: "ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256:rsa_pkcs1_sha256:ecdsa_secp384r1_sha384:rsa_pss_rsae_sha384:rsa_pkcs1_sha384:ecdsa_secp521r1_sha512:rsa_pss_rsae_sha512:rsa_pkcs1_sha512",
1494
+ ecdhCurve: "X25519:prime256v1:secp384r1",
1495
+ ciphers: [
1496
+ "TLS_AES_128_GCM_SHA256",
1497
+ "TLS_AES_256_GCM_SHA384",
1498
+ "TLS_CHACHA20_POLY1305_SHA256",
1499
+ "ECDHE-ECDSA-AES128-GCM-SHA256",
1500
+ "ECDHE-RSA-AES128-GCM-SHA256",
1501
+ "ECDHE-ECDSA-AES256-GCM-SHA384",
1502
+ "ECDHE-RSA-AES256-GCM-SHA384",
1503
+ "ECDHE-ECDSA-CHACHA20-POLY1305",
1504
+ "ECDHE-RSA-CHACHA20-POLY1305",
1505
+ "ECDHE-RSA-AES128-SHA",
1506
+ "ECDHE-RSA-AES256-SHA",
1507
+ "AES128-GCM-SHA256",
1508
+ "AES256-GCM-SHA384",
1509
+ "AES128-SHA",
1510
+ "AES256-SHA"
1511
+ ].join(":"),
1512
+ sigalgs: [
1513
+ "ecdsa_secp256r1_sha256",
1514
+ "ecdsa_secp384r1_sha384",
1515
+ "rsa_pss_rsae_sha256",
1516
+ "rsa_pss_rsae_sha384",
1517
+ "rsa_pss_rsae_sha512",
1518
+ "rsa_pkcs1_sha256",
1519
+ "rsa_pkcs1_sha384",
1520
+ "rsa_pkcs1_sha512"
1521
+ ].join(":"),
1390
1522
  minVersion: "TLSv1.2",
1391
1523
  maxVersion: "TLSv1.3",
1392
1524
  sessionTimeout: 3600
@@ -1421,25 +1553,62 @@ function generateSessionId() {
1421
1553
  function generateTraceId() {
1422
1554
  return `trc_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
1423
1555
  }
1556
+ const proxyAgentCache = new Map;
1557
+ const PROXY_AGENT_EVICTION_MS = 60000;
1558
+ function buildProxyAgentKey(proxy, isSecure, rejectUnauthorized) {
1559
+ if (typeof proxy === "string") {
1560
+ return `str:${proxy}:${isSecure}:${rejectUnauthorized}`;
1561
+ }
1562
+ const p = proxy;
1563
+ const authKey = p.auth ? `${p.auth.username}:${p.auth.password}` : "";
1564
+ return `obj:${p.protocol}://${p.host}:${p.port}:${authKey}:${isSecure}:${rejectUnauthorized}`;
1565
+ }
1566
+ function evictStaleProxyAgents() {
1567
+ const now = Date.now();
1568
+ for (const [key, entry] of proxyAgentCache) {
1569
+ if (now - entry.lastUsed > PROXY_AGENT_EVICTION_MS) {
1570
+ try {
1571
+ entry.agent.destroy();
1572
+ } catch {}
1573
+ proxyAgentCache.delete(key);
1574
+ }
1575
+ }
1576
+ }
1577
+ let lastProxyEviction = 0;
1424
1578
  function parseProxy(proxy, isScure = true, rejectUnauthorized = false) {
1425
1579
  if (!proxy) {
1426
1580
  return;
1427
1581
  }
1582
+ const now = Date.now();
1583
+ if (now - lastProxyEviction > PROXY_AGENT_EVICTION_MS / 2) {
1584
+ evictStaleProxyAgents();
1585
+ lastProxyEviction = now;
1586
+ }
1587
+ const cacheKey = buildProxyAgentKey(proxy, isScure, rejectUnauthorized);
1588
+ const cached = proxyAgentCache.get(cacheKey);
1589
+ if (cached) {
1590
+ cached.lastUsed = now;
1591
+ return cached.agent;
1592
+ }
1593
+ let agent;
1428
1594
  if (typeof proxy === "string") {
1429
1595
  if (proxy.startsWith("http://")) {
1430
- return rezoProxy(`http://${proxy.slice(7)}`, "http");
1596
+ agent = rezoProxy(`http://${proxy.slice(7)}`, "http");
1431
1597
  } else if (proxy.startsWith("https://")) {
1432
- return rezoProxy(`https://${proxy.slice(8)}`, "https");
1598
+ agent = rezoProxy(`https://${proxy.slice(8)}`, "https");
1599
+ } else {
1600
+ agent = rezoProxy(proxy);
1433
1601
  }
1434
- return rezoProxy(proxy);
1435
- }
1436
- if (proxy.protocol === "http" || proxy.protocol === "https") {
1437
- return rezoProxy({
1602
+ } else if (proxy.protocol === "http" || proxy.protocol === "https") {
1603
+ agent = rezoProxy({
1438
1604
  ...proxy,
1439
1605
  client: !isScure ? "http" : "https"
1440
1606
  });
1607
+ } else {
1608
+ agent = rezoProxy(proxy);
1441
1609
  }
1442
- return rezoProxy(proxy);
1610
+ proxyAgentCache.set(cacheKey, { agent, lastUsed: now });
1611
+ return agent;
1443
1612
  }
1444
1613
  async function updateCookies(config, headers, url) {
1445
1614
  const cookies = headers["set-cookie"];
@@ -1480,7 +1649,7 @@ async function updateCookies(config, headers, url) {
1480
1649
  acceptedCookies.push(...parsedCookies.array);
1481
1650
  }
1482
1651
  const acceptedCookieStrings = acceptedCookies.map((c) => c.toSetCookieString());
1483
- if (config.enableCookieJar && config.cookieJar) {
1652
+ if (!config.disableCookieJar && config.cookieJar) {
1484
1653
  config.cookieJar.setCookiesSync(acceptedCookieStrings, url);
1485
1654
  }
1486
1655
  jar.setCookiesSync(acceptedCookieStrings, url);