rust-fetch 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -381,7 +381,7 @@ Large JSON is dominated by `JSON.parse`, which both clients pay; the remaining g
381
381
  - [ ] Upload progress tracking: not possible on top of `fetch()`, which has no upload progress events
382
382
  - [ ] WebSocket support
383
383
  - Handled by the runtime, not by rust-fetch:
384
- - **Connection pooling:** browsers pool connections for `fetch()`; in Node.js rust-fetch reuses connections through Node's default keep-alive agents.
384
+ - **Connection pooling:** browsers pool connections for `fetch()`; in Node.js rust-fetch keeps up to 256 keep-alive connections per origin and queues further requests until one is free. If you install your own `http.globalAgent` / `https.globalAgent`, or set limits on Node's, that agent is used instead.
385
385
  - **Proxies:** in Node.js, install a proxy agent as `http.globalAgent` / `https.globalAgent` (for example from `https-proxy-agent`). In browsers, use the system settings.
386
386
  - **HTTP/2:** browsers negotiate it automatically; the Node.js transport uses HTTP/1.1 with keep-alive.
387
387
 
package/node.js CHANGED
@@ -17,6 +17,35 @@ import { initSync } from './rust_fetch.js';
17
17
 
18
18
  initSync({ module: readFileSync(new URL('./rust_fetch_bg.wasm', import.meta.url)) });
19
19
 
20
+ // Node's default global agents open a socket for every request that finds no
21
+ // idle one, but keep only 256 of them idle afterwards. Under a burst the rest
22
+ // are closed and sit in TIME_WAIT, and once the ephemeral port range is used
23
+ // up every connect fails with EADDRNOTAVAIL. A bounded pool that keeps all of
24
+ // its sockets queues requests instead, reusing the same connections.
25
+ const MAX_SOCKETS = 256;
26
+ const POOL_OPTIONS = { keepAlive: true, scheduling: 'lifo', timeout: 5000, maxSockets: MAX_SOCKETS, maxFreeSockets: MAX_SOCKETS };
27
+ const pools = new Map([
28
+ [http, new http.Agent(POOL_OPTIONS)],
29
+ [https, new https.Agent(POOL_OPTIONS)],
30
+ ]);
31
+ // The options Node's own global agents are created with. Newer versions also
32
+ // list unset ones (e.g. `proxyEnv: undefined` unless NODE_USE_ENV_PROXY is on).
33
+ const NODE_AGENT_OPTIONS = { keepAlive: true, scheduling: 'lifo', timeout: 5000, noDelay: true, defaultPort: 443, protocol: 'https:' };
34
+
35
+ /**
36
+ * The bounded pool while `globalAgent` is still Node's unbounded default;
37
+ * an agent the application installed (a proxy, TLS options, its own limits)
38
+ * is used as is.
39
+ */
40
+ function agentFor(transport) {
41
+ const global = transport.globalAgent;
42
+ const isNodeDefault =
43
+ global.constructor === transport.Agent &&
44
+ global.maxSockets === Infinity &&
45
+ Object.entries(global.options).every(([key, value]) => value == null || NODE_AGENT_OPTIONS[key] === value);
46
+ return isNodeDefault ? pools.get(transport) : global;
47
+ }
48
+
20
49
  const MAX_REDIRECTS = 20;
21
50
  const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
22
51
  const ACCEPT_ENCODING = typeof zlib.zstdDecompressSync === 'function' ? 'gzip, deflate, br, zstd' : 'gzip, deflate, br';
@@ -126,7 +155,7 @@ function withoutHeaders(headers, names) {
126
155
  function send(state, method, url, headers, body, redirects, resolve, reject) {
127
156
  if (state.cancelled) return;
128
157
  const transport = url.protocol === 'https:' ? https : http;
129
- const request = transport.request(url, { method, headers }, (response) => {
158
+ const request = transport.request(url, { method, headers, agent: agentFor(transport) }, (response) => {
130
159
  const status = response.statusCode;
131
160
  const location = response.headers.location;
132
161
 
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "Asem Qaffaf <asem@qaffaf.com>"
6
6
  ],
7
7
  "description": "A reliable HTTP client for JavaScript and TypeScript, written in Rust and compiled to WebAssembly: timeouts, retries, typed errors and interceptors, faster than fetch() in Node.js",
8
- "version": "2.1.0",
8
+ "version": "2.1.1",
9
9
  "license": "MIT OR Apache-2.0",
10
10
  "repository": {
11
11
  "type": "git",
package/rust_fetch.js CHANGED
@@ -561,7 +561,7 @@ function __wbg_get_imports() {
561
561
  const ret = clearTimeout(arg0);
562
562
  return ret;
563
563
  },
564
- __wbg_clearTimeout_2d18004c33139d44: function(arg0) {
564
+ __wbg_clearTimeout_de7e2788a4701f78: function(arg0) {
565
565
  clearTimeout(arg0);
566
566
  },
567
567
  __wbg_close_185286988f30f252: function() { return handleError(function (arg0) {
@@ -570,7 +570,7 @@ function __wbg_get_imports() {
570
570
  __wbg_close_748f26698dabe269: function() { return handleError(function (arg0) {
571
571
  arg0.close();
572
572
  }, arguments); },
573
- __wbg_decode_0be77be735437c76: function() { return handleError(function (arg0, arg1) {
573
+ __wbg_decode_0775197308e59c17: function() { return handleError(function (arg0, arg1) {
574
574
  const ret = arg0.decode(arg1);
575
575
  return ret;
576
576
  }, arguments); },
@@ -589,7 +589,7 @@ function __wbg_get_imports() {
589
589
  const ret = Object.entries(arg0);
590
590
  return ret;
591
591
  },
592
- __wbg_fetch_43bae44b7171a171: function() { return handleError(function (arg0) {
592
+ __wbg_fetch_4dd9052d4418f5a3: function() { return handleError(function (arg0) {
593
593
  const ret = fetch(arg0);
594
594
  return ret;
595
595
  }, arguments); },
@@ -734,14 +734,14 @@ function __wbg_get_imports() {
734
734
  const ret = new Object();
735
735
  return ret;
736
736
  },
737
+ __wbg_new_777668d2bcf04e9b: function() {
738
+ const ret = new TextDecoder();
739
+ return ret;
740
+ },
737
741
  __wbg_new_83ca405eae412a62: function() { return handleError(function () {
738
742
  const ret = new Headers();
739
743
  return ret;
740
744
  }, arguments); },
741
- __wbg_new_cdac6c2ae368ec54: function() {
742
- const ret = new TextDecoder();
743
- return ret;
744
- },
745
745
  __wbg_new_from_slice_9a868026ffa4208a: function(arg0, arg1) {
746
746
  const ret = new Uint8Array(getArrayU8FromWasm0(arg0, arg1));
747
747
  return ret;
@@ -796,7 +796,7 @@ function __wbg_get_imports() {
796
796
  const ret = JSON.parse(getStringFromWasm0(arg0, arg1));
797
797
  return ret;
798
798
  }, arguments); },
799
- __wbg_parse_33ce4953982abfa3: function() { return handleError(function (arg0) {
799
+ __wbg_parse_9d2d081cff35f81a: function() { return handleError(function (arg0) {
800
800
  const ret = JSON.parse(arg0);
801
801
  return ret;
802
802
  }, arguments); },
@@ -839,7 +839,7 @@ function __wbg_get_imports() {
839
839
  __wbg_respond_c102fabcc79e5ef0: function() { return handleError(function (arg0, arg1) {
840
840
  arg0.respond(arg1 >>> 0);
841
841
  }, arguments); },
842
- __wbg_setTimeout_61cdece84a7288cf: function(arg0, arg1) {
842
+ __wbg_setTimeout_36fb298700cc9ab9: function(arg0, arg1) {
843
843
  const ret = setTimeout(arg0, arg1);
844
844
  return ret;
845
845
  },
Binary file