external-ips 0.0.4 → 0.0.5

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 (3) hide show
  1. package/index.js +9 -4
  2. package/package.json +27 -25
  3. package/test.js +34 -0
package/index.js CHANGED
@@ -10,7 +10,6 @@ function normalizeFamily(family){
10
10
  return false;
11
11
  } else throw new Error('bad family='+family);
12
12
  }
13
-
14
13
  class AbstractObject {
15
14
  constructor(family){
16
15
  assert(this.constructor!==AbstractObject); //Abstract
@@ -29,9 +28,15 @@ class AbstractObject {
29
28
  if((!family || !self.family || self.family===family) && !options.localAddress){
30
29
  const ip = self.random();
31
30
  if(ip){
32
- //console.log('++++++', ip, ip.address);
33
- options.localAddress = ip.address ? ip.address : ip;
34
- } //else console.log('++++++---', ip);
31
+ //КОПИЯ, а не мутация. Agent уже вычислил ключ реестра (getName БЕЗ localAddress)
32
+ //и замкнул этот же объект options в свои слушатели. Если дописать localAddress
33
+ //в него, removeSocket/onFree пересчитают имя уже С адресом и попадут в другой
34
+ //список: закрытые сокеты никогда не удаляются из agent.sockets (на боевом
35
+ //mexc-гейтвее агент накопил 7744 сокета, из них 5578 мёртвых, ~1.5 ГБ/ч), а
36
+ //freeSockets кладутся под ключ, по которому их никто не ищет — keep-alive не
37
+ //переиспользуется вовсе, и каждый запрос открывает новый TLS.
38
+ return old.call(agent, { ...options, localAddress: ip.address ? ip.address : ip }, callback);
39
+ }
35
40
  }
36
41
  return old.call(agent, options, callback);
37
42
  }
package/package.json CHANGED
@@ -1,27 +1,29 @@
1
1
  {
2
- "name": "external-ips",
3
- "version": "0.0.4",
4
- "main": "./index.js",
5
- "scripts": {
6
- "test": "echo \"Error: no test specified\" && exit 1"
7
- },
8
- "author": "Andrey Belousoff <a.v.belousoff@gmail.com>",
9
- "license": "MIT",
10
- "description": "any IPs in HTTP(s) requests or WS",
11
- "keywords": ["ips", "ip"],
12
- "dependencies": {
13
- },
14
- "engines": {
15
- "node": ">=0.1.0"
16
- },
17
- "repository": {
18
- "type": "git",
19
- "url": "https://github.com/Hkey1/external-ips"
20
- },
21
- "readme":"External IPs",
22
- "readmeFilename": "README.md",
23
- "bugs": {
24
- "url": "https://github.com/Hkey1/external-ips/issues"
25
- },
26
- "homepage": "https://github.com/Hkey1/external-ips"
2
+ "name": "external-ips",
3
+ "version": "0.0.5",
4
+ "main": "./index.js",
5
+ "scripts": {
6
+ "test": "node test.js"
7
+ },
8
+ "author": "Andrey Belousoff <a.v.belousoff@gmail.com>",
9
+ "license": "MIT",
10
+ "description": "any IPs in HTTP(s) requests or WS",
11
+ "keywords": [
12
+ "ips",
13
+ "ip"
14
+ ],
15
+ "dependencies": {},
16
+ "engines": {
17
+ "node": ">=0.1.0"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/Hkey1/external-ips"
22
+ },
23
+ "readme": "External IPs",
24
+ "readmeFilename": "README.md",
25
+ "bugs": {
26
+ "url": "https://github.com/Hkey1/external-ips/issues"
27
+ },
28
+ "homepage": "https://github.com/Hkey1/external-ips"
27
29
  }
package/test.js ADDED
@@ -0,0 +1,34 @@
1
+ //node test.js — регрессия: патч не должен ломать учёт сокетов у http.Agent
2
+ const assert = require("node:assert");
3
+ const http = require("node:http");
4
+ const IPs = require("./index.js");
5
+
6
+ const server = http.createServer((req, res)=>res.end("ok"));
7
+ server.listen(0, "127.0.0.1", async()=>{
8
+ const port = server.address().port;
9
+ try {
10
+ const agent = new http.Agent({ keepAlive: true });
11
+ //как manyIPs в ccxtPatch; random() вернёт локальный адрес этой машины
12
+ IPs.v4.patchAgent(agent);
13
+ for(let i = 0; i < 8; i++){
14
+ await new Promise((res, rej)=>{
15
+ http.get({ host: "127.0.0.1", port, agent }, r=>{ r.resume(); r.on("end", res); }).on("error", rej);
16
+ });
17
+ await new Promise(r=>setTimeout(r, 30));
18
+ }
19
+ const cnt = o=>Object.values(o).reduce((a, arr)=>a + arr.length, 0);
20
+ const busy = cnt(agent.sockets);
21
+ const free = cnt(agent.freeSockets);
22
+ const dead = Object.values(agent.sockets).flat().filter(s=>s.destroyed).length;
23
+ console.log(`sockets=${busy} (мёртвых ${dead}), freeSockets=${free}`);
24
+ //до фикса: sockets=8 под одним ключом, freeSockets=8 под другим, реюза ноль
25
+ assert.equal(busy, 0, `в agent.sockets не должно оставаться сокетов, осталось ${busy}`);
26
+ assert.equal(dead, 0);
27
+ assert.ok(free >= 1 && free <= 2, `keep-alive должен переиспользоваться: freeSockets=${free}`);
28
+ console.log("ok: учёт агента цел, keep-alive переиспользуется");
29
+ agent.destroy(); server.close();
30
+ } catch(err){
31
+ console.error("FAIL:", err.message);
32
+ server.close(); process.exitCode = 1;
33
+ }
34
+ });