socketduct 0.1.17 → 0.1.18
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/changelog.d/20260925120000-reverse-stream-transport-resume.md +1 -0
- package/docs/architecture.md +12 -5
- package/docs/protocol.md +43 -11
- package/docs/reverse-gateway.md +59 -24
- package/docs/security.md +15 -5
- package/docs/session-lifecycle.md +21 -10
- package/docs/socket-api.md +12 -5
- package/package.json +1 -1
- package/src/client/multiplexed-transport-set.js +1 -0
- package/src/reverse/client.js +255 -36
- package/src/reverse/gateway.js +265 -38
- package/src/reverse/hub.js +305 -24
- package/src/reverse/runtime.js +1 -0
- package/test/integration/reverse-gateway.test.js +576 -28
- package/test/integration/reverse-route-class.test.js +16 -12
|
@@ -132,8 +132,8 @@ test("route-class clients fail closed per OPEN while unaffected routes and legac
|
|
|
132
132
|
hub: {host: "127.0.0.1", port: hub.port, token: "gateway-a-token"},
|
|
133
133
|
tls,
|
|
134
134
|
authorizeTarget: (target) => target.host === targetA.host && target.port === targetA.port,
|
|
135
|
-
reconnectDelayMs:
|
|
136
|
-
maxReconnectDelayMs:
|
|
135
|
+
reconnectDelayMs: 10,
|
|
136
|
+
maxReconnectDelayMs: 20
|
|
137
137
|
})
|
|
138
138
|
const gatewayB = new OutboundReverseGateway({
|
|
139
139
|
gatewayId: "server-b-starlink",
|
|
@@ -141,8 +141,8 @@ test("route-class clients fail closed per OPEN while unaffected routes and legac
|
|
|
141
141
|
hub: {host: "127.0.0.1", port: hub.port, token: "gateway-b-token"},
|
|
142
142
|
tls,
|
|
143
143
|
authorizeTarget: (target) => target.host === targetB.host && target.port === targetB.port,
|
|
144
|
-
reconnectDelayMs:
|
|
145
|
-
maxReconnectDelayMs:
|
|
144
|
+
reconnectDelayMs: 10,
|
|
145
|
+
maxReconnectDelayMs: 20
|
|
146
146
|
})
|
|
147
147
|
t.after(() => gatewayA.close())
|
|
148
148
|
t.after(() => gatewayB.close())
|
|
@@ -206,18 +206,22 @@ test("route-class clients fail closed per OPEN while unaffected routes and legac
|
|
|
206
206
|
await Promise.all([onceEvent(streamA, "connect"), onceEvent(streamB, "connect")])
|
|
207
207
|
assert.deepEqual(await Promise.all([exchange(streamA, "one"), exchange(streamB, "two")]), ["a:one", "b:two"])
|
|
208
208
|
|
|
209
|
-
const
|
|
209
|
+
const resumed = Promise.all([onceEvent(streamA, "ready"), onceEvent(streamB, "ready")])
|
|
210
210
|
await hub.closeActiveGateway("server-a-starlink")
|
|
211
|
-
|
|
212
|
-
await waitUntil(() => hub.status().healthyGateways ===
|
|
213
|
-
|
|
214
|
-
const disconnected = client.createConnection({host: targetA.host, port: targetA.port})
|
|
215
|
-
assert.equal((await nextError(disconnected)).code, "GATEWAY_UNAVAILABLE")
|
|
211
|
+
await withTimeout(resumed, "route-class streams resume after gateway transport replacement")
|
|
212
|
+
await waitUntil(() => hub.status().healthyGateways === 2, "gateway A re-registration")
|
|
213
|
+
assert.equal(await exchange(streamA, "resumed"), "a:resumed")
|
|
216
214
|
assert.equal(await exchange(streamB, "still-open"), "b:still-open")
|
|
215
|
+
|
|
216
|
+
const anotherA = client.createConnection({host: targetA.host, port: targetA.port})
|
|
217
|
+
await onceEvent(anotherA, "connect")
|
|
218
|
+
assert.equal(await exchange(anotherA, "new-a"), "a:new-a")
|
|
217
219
|
const anotherB = client.createConnection({host: targetB.host, port: targetB.port})
|
|
218
220
|
await onceEvent(anotherB, "connect")
|
|
219
221
|
assert.equal(await exchange(anotherB, "new"), "b:new")
|
|
220
222
|
|
|
223
|
+
streamA.destroy()
|
|
224
|
+
anotherA.destroy()
|
|
221
225
|
await gatewayA.close()
|
|
222
226
|
const replacementA = new OutboundReverseGateway({
|
|
223
227
|
gatewayId: "server-a-starlink",
|
|
@@ -232,7 +236,7 @@ test("route-class clients fail closed per OPEN while unaffected routes and legac
|
|
|
232
236
|
const rejoinedA = client.createConnection({host: targetA.host, port: targetA.port})
|
|
233
237
|
await onceEvent(rejoinedA, "connect")
|
|
234
238
|
assert.equal(await exchange(rejoinedA, "rejoined"), "a:rejoined")
|
|
235
|
-
assert
|
|
239
|
+
assert(client.status().physicalConnects >= 2)
|
|
236
240
|
assert.equal(hub.status().clientPhysicalConnections, 1)
|
|
237
241
|
streamB.destroy()
|
|
238
242
|
anotherB.destroy()
|
|
@@ -360,7 +364,7 @@ test("a backpressured gateway does not block DATA or a new OPEN for another rout
|
|
|
360
364
|
t.after(() => socket.destroy())
|
|
361
365
|
return nextError(socket)
|
|
362
366
|
})
|
|
363
|
-
assert.equal((await withTimeout(streamAError, "gateway A output limit")).code, "
|
|
367
|
+
assert.equal((await withTimeout(streamAError, "gateway A output limit")).code, "OUTPUT_BACKPRESSURE_LIMIT")
|
|
364
368
|
await withTimeout(Promise.all(queuedAErrors), "queued gateway A stream cleanup")
|
|
365
369
|
assert.equal(await exchange(streamB, "after-limit"), "b:after-limit")
|
|
366
370
|
assert.equal(hub.status().healthyGateways, 1)
|