@decentnetwork/lan 0.1.75 → 0.1.77
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.
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -126,11 +126,15 @@ export class ConnectProxy {
|
|
|
126
126
|
}
|
|
127
127
|
let bytes = 0;
|
|
128
128
|
let closed = false;
|
|
129
|
+
let lastBytes = -1;
|
|
130
|
+
let idleTimer;
|
|
129
131
|
const upstream = net.connect(port, host);
|
|
130
132
|
const tearDown = (reason) => {
|
|
131
133
|
if (closed)
|
|
132
134
|
return;
|
|
133
135
|
closed = true;
|
|
136
|
+
if (idleTimer)
|
|
137
|
+
clearInterval(idleTimer);
|
|
134
138
|
upstream.destroy();
|
|
135
139
|
clientSocket.destroy();
|
|
136
140
|
this.stats.active = Math.max(0, this.stats.active - 1);
|
|
@@ -181,6 +185,26 @@ export class ConnectProxy {
|
|
|
181
185
|
upstream.on("end", () => tearDown("upstream end"));
|
|
182
186
|
clientSocket.on("error", (err) => tearDown(`client error: ${err.message}`));
|
|
183
187
|
clientSocket.on("end", () => tearDown("client end"));
|
|
188
|
+
// "close" catches abrupt teardowns (RST / socket destroyed) that never
|
|
189
|
+
// emit "end" — without these, a half-dead tunnel leaks.
|
|
190
|
+
upstream.on("close", () => tearDown("upstream close"));
|
|
191
|
+
clientSocket.on("close", () => tearDown("client close"));
|
|
192
|
+
// Idle reaper: when the Carrier path drops mid-stream the broken side
|
|
193
|
+
// sends no FIN, so neither "end" nor "error" fires and the sockets would
|
|
194
|
+
// leak until the kernel's multi-hour TCP keepalive — they pile up under
|
|
195
|
+
// load (1080p opens many high-bandwidth tunnels; two clients on one exit
|
|
196
|
+
// compounds it) and starve the exit. If no bytes flow in EITHER direction
|
|
197
|
+
// across two idle ticks, tear it down. Active streaming keeps `bytes`
|
|
198
|
+
// climbing (and backpressure pauses a one-sided flow), so only genuinely
|
|
199
|
+
// dead tunnels are reaped. Tunable via AGENTNET_TUNNEL_IDLE_MS.
|
|
200
|
+
const idleMs = Number(process.env.AGENTNET_TUNNEL_IDLE_MS) || 120_000;
|
|
201
|
+
idleTimer = setInterval(() => {
|
|
202
|
+
if (bytes === lastBytes)
|
|
203
|
+
tearDown("idle (no data — Carrier path likely dropped)");
|
|
204
|
+
else
|
|
205
|
+
lastBytes = bytes;
|
|
206
|
+
}, idleMs);
|
|
207
|
+
idleTimer.unref?.();
|
|
184
208
|
}
|
|
185
209
|
refuse(clientSocket, src, srcName, target, code, reason) {
|
|
186
210
|
this.stats.totalRefused += 1;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentnetwork/lan",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.77",
|
|
4
4
|
"description": "Private virtual LAN for self-hosted services and AI agents, built on Elastos Carrier. NAT-traversal, name service, ACL, all over a peer-to-peer mesh — no public IP required.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|