external-ips 0.0.3 → 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 +40 -16
  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
@@ -22,11 +21,22 @@ class AbstractObject {
22
21
  defaultFamily = normalizeFamily(defaultFamily);
23
22
  assert.equal(typeof agent, 'object');
24
23
  assert.equal(typeof agent.createConnection, 'function');
25
- const old = agent.createConnection;
24
+ const old = agent.createConnection;
25
+ const self = this;
26
26
  agent.createConnection = (options, callback)=>{
27
- const family = normalizeFamily(options.family) || defaultFamily;
28
- if(!family || !this.family || this.family===family){
29
- options.localAddress ||= this.next().address;
27
+ const family = options.family ? normalizeFamily(options.family)||defaultFamily : defaultFamily;
28
+ if((!family || !self.family || self.family===family) && !options.localAddress){
29
+ const ip = self.random();
30
+ if(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
+ }
30
40
  }
31
41
  return old.call(agent, options, callback);
32
42
  }
@@ -37,10 +47,16 @@ class AbstractObject {
37
47
  return family;
38
48
  }
39
49
  random(family){
40
- return this._random(family).address;
50
+ const ip = this._random(family);
51
+ if(ip){
52
+ return ip.address;
53
+ } //else console.log('!!!!!!!ip.rnd', this.constructor.name, this.length)
41
54
  }
42
55
  next(family){
43
- return this._next(family).address;
56
+ const ip = this._next(family);
57
+ if(ip){
58
+ return ip.address;
59
+ } //else console.log('!!!!!!!ip.nxt', this.constructor.name, this.length)
44
60
  }
45
61
  _random(family){
46
62
  return this.__random(this.checkFamily(family))
@@ -65,15 +81,18 @@ class IP extends AbstractObject{
65
81
  __next(){return this}
66
82
  };
67
83
 
84
+ fid = 0
68
85
  class FamilyList extends AbstractObject{
69
- ips = [];
86
+ ips = [];
87
+ lastIndex = 0;
70
88
  constructor(family){
71
89
  super(family);
72
- Object.defineProperty(this, 'lastIndex', {
73
- value: 0,
74
- enumerable: false,
75
- writable : true,
76
- })
90
+ this.fid = fid++;
91
+ //Object.defineProperty(this, 'lastIndex', {
92
+ // value: 0,
93
+ // enumerable: false,
94
+ // writable : true,
95
+ //})
77
96
  }
78
97
  push(...ips){
79
98
  ips.forEach(ip=>{
@@ -87,7 +106,11 @@ class FamilyList extends AbstractObject{
87
106
  return this.ips.length;
88
107
  }
89
108
  __random(){
90
- return this[Math.floor(this.length*Math.random())]
109
+ const pos = Math.floor(this.length*Math.random());
110
+ const res = this.ips[pos];
111
+ //console.log(this.ips);
112
+ //console.log('__random', 'length='+this.length, 'pos='+pos, res)
113
+ return res;
91
114
  }
92
115
  __next(){
93
116
  const res = this.ips[this.lastIndex];
@@ -149,12 +172,13 @@ Object.values(os.networkInterfaces()).forEach(arr=>{
149
172
  mainList.push(...arr.filter(ip=>!ip.internal).map(ip=>new IP(ip)))
150
173
  });
151
174
 
175
+ //console.log('--------------')
152
176
  //mainList.print();
177
+ //console.log('/--------------')
153
178
 
154
179
  mainList.MainList = MainList;
155
180
  mainList.IP = IP;
156
181
  mainList.AbstractObject = AbstractObject;
157
182
  mainList.FamilyList = FamilyList;
158
183
 
159
-
160
- module.exports = mainList;
184
+ module.exports = mainList;
package/package.json CHANGED
@@ -1,27 +1,29 @@
1
1
  {
2
- "name": "external-ips",
3
- "version": "0.0.3",
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
+ });