socketduct 0.1.25 → 0.1.27

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 (58) hide show
  1. package/AGENTS.md +2 -2
  2. package/changelog.d/20261010130509-bound-log-frame-compression-memory.md +1 -0
  3. package/changelog.d/20261025070000-reclaim-unreadable-retention-lock.md +14 -0
  4. package/docs/runbooks/jsonl-integrity-rollout.md +8 -6
  5. package/package.json +2 -2
  6. package/scripts/test.js +9 -14
  7. package/src/logging/compressed-log-writer.js +1 -1
  8. package/src/logging/log-retention.js +36 -1
  9. package/src/logging/zstd-frame-codec.js +15 -1
  10. package/test/e2e/gateway-process.test.js +93 -92
  11. package/test/e2e/logger-shutdown-process.test.js +40 -37
  12. package/test/e2e/reverse-client-recovery-process.test.js +107 -103
  13. package/test/e2e/reverse-gateway-process.test.js +328 -318
  14. package/test/integration/initial-target-recovery.test.js +333 -334
  15. package/test/integration/logging-cli.test.js +121 -119
  16. package/test/integration/logging-compressed-recovery.test.js +96 -95
  17. package/test/integration/multiplexed-compatibility.test.js +178 -177
  18. package/test/integration/multiplexed-corrections.test.js +360 -359
  19. package/test/integration/multiplexed-failures.test.js +253 -251
  20. package/test/integration/multiplexed-transport-set.test.js +334 -322
  21. package/test/integration/multiplexed-transport.test.js +135 -134
  22. package/test/integration/reverse-gateway-stream-id-reset.test.js +90 -89
  23. package/test/integration/reverse-gateway.test.js +2309 -2218
  24. package/test/integration/reverse-resume-continuity.test.js +219 -218
  25. package/test/integration/reverse-route-class.test.js +358 -341
  26. package/test/integration/socketduct-http-agent.test.js +314 -312
  27. package/test/integration/socketduct-https-agent.test.js +516 -485
  28. package/test/integration/stream-compression.test.js +862 -824
  29. package/test/integration/tcp-forwarding.test.js +2550 -2447
  30. package/test/integration/tls-forwarding.test.js +593 -592
  31. package/test/unit/auth/auth.test.js +43 -44
  32. package/test/unit/auth/static-token-store.test.js +9 -8
  33. package/test/unit/client/connect.test.js +41 -40
  34. package/test/unit/client/deferred-virtual-socket.test.js +95 -94
  35. package/test/unit/client/multiplexed-spool-budget.test.js +155 -154
  36. package/test/unit/client/multiplexed-transport-set.test.js +830 -748
  37. package/test/unit/client/socketduct-socket.test.js +768 -771
  38. package/test/unit/client/virtual-socket.test.js +198 -168
  39. package/test/unit/config/docker-gateway-files.test.js +224 -224
  40. package/test/unit/config/environment.test.js +472 -492
  41. package/test/unit/index.test.js +53 -52
  42. package/test/unit/logging/compressed-log-worker.test.js +23 -22
  43. package/test/unit/logging/compressed-log-writer.test.js +155 -154
  44. package/test/unit/logging/log-retention.test.js +71 -48
  45. package/test/unit/logging/log-segment-index.test.js +84 -81
  46. package/test/unit/logging/zstd-frame-codec.test.js +28 -19
  47. package/test/unit/protocol/compression.test.js +254 -298
  48. package/test/unit/protocol/frame.test.js +280 -276
  49. package/test/unit/protocol/v3-compression.test.js +21 -23
  50. package/test/unit/protocol/v3-frame.test.js +42 -41
  51. package/test/unit/release-configuration.test.js +15 -12
  52. package/test/unit/reverse/disconnect-attribution.test.js +65 -64
  53. package/test/unit/reverse-terminal-outcomes.test.js +53 -50
  54. package/test/unit/server/control-handler.test.js +232 -231
  55. package/test/unit/server/primary.test.js +51 -49
  56. package/test/unit/server/relay-runtime.test.js +280 -294
  57. package/test/unit/sessions/session.test.js +48 -62
  58. package/test/unit/spool/spool-file.test.js +401 -385
@@ -1,8 +1,7 @@
1
1
  /* eslint-disable jsdoc/escape-inline-tags, jsdoc/require-param-type, jsdoc/require-returns */
2
- import assert from "node:assert/strict"
2
+ import {describe, expect, it, withTestCleanup} from "@velocious/testing"
3
3
  import {createConnection, createServer} from "node:net"
4
4
  import {Duplex} from "node:stream"
5
- import test from "node:test"
6
5
 
7
6
  import {StaticTokenStore} from "../../src/auth/static-token-store.js"
8
7
  import {MultiplexedSocketductConnection} from "../../src/client/multiplexed-connection.js"
@@ -25,7 +24,7 @@ async function listen(server) {
25
24
  server.listen(0, "127.0.0.1", () => { server.off("error", reject); resolve(undefined) })
26
25
  })
27
26
  const address = server.address()
28
- assert(address !== null && typeof address !== "string")
27
+ if (address === null || typeof address === "string") throw new Error("Expected a bound TCP address")
29
28
  return address.port
30
29
  }
31
30
 
@@ -77,263 +76,266 @@ function tokens() {
77
76
  return new StaticTokenStore([{username: "edge", token: "valid-token"}])
78
77
  }
79
78
 
80
- test("authentication and exact-target authorization fail closed", async (t) => {
81
- const relay = await startMultiplexedTcpRelay({tokens: tokens(), authorizeTarget: () => false})
82
- t.after(() => relay.close())
83
- const unauthorizedConnection = new MultiplexedSocketductConnection({
84
- relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"}
85
- })
86
- t.after(() => unauthorizedConnection.close())
87
- const denied = unauthorizedConnection.createConnection({host: "127.0.0.1", port: 9})
88
- const deniedError = await nextError(denied)
89
- assert(deniedError instanceof SocketductStreamError)
90
- assert.equal(deniedError.code, "TARGET_UNAUTHORIZED")
91
- assert.equal(relay.status().activeTargets, 0)
92
-
93
- const invalidConnection = new MultiplexedSocketductConnection({
94
- relay: {host: "127.0.0.1", port: relay.port, token: "wrong-token"},
95
- maxReconnectAttempts: 2
96
- })
97
- t.after(() => invalidConnection.close())
98
- const invalid = invalidConnection.createConnection({host: "127.0.0.1", port: 9})
99
- const authError = await nextError(invalid)
100
- assert(authError instanceof SocketductSessionLostError)
101
- assert.equal(authError.code, "AUTH_REQUIRED")
102
- await new Promise((resolve) => setTimeout(resolve, 30))
103
- assert.equal(invalidConnection.status().reconnectAttempts, 0)
104
- })
79
+ describe("multiplexed failures", () => {
80
+ it("authentication and exact-target authorization fail closed", async () => withTestCleanup(async (after) => {
81
+ const relay = await startMultiplexedTcpRelay({tokens: tokens(), authorizeTarget: () => false})
82
+ after(() => relay.close())
83
+ const unauthorizedConnection = new MultiplexedSocketductConnection({
84
+ relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"}
85
+ })
86
+ after(() => unauthorizedConnection.close())
87
+ const denied = unauthorizedConnection.createConnection({host: "127.0.0.1", port: 9})
88
+ const deniedError = await nextError(denied)
89
+ expect(deniedError instanceof SocketductStreamError).toBeTruthy()
90
+ expect(deniedError.code).toBe("TARGET_UNAUTHORIZED")
91
+ expect(relay.status().activeTargets).toBe(0)
105
92
 
106
- test("target open failure and stream reset are isolated from siblings", async (t) => {
107
- const echo = createServer((socket) => socket.on("data", (chunk) => socket.write(chunk)))
108
- const echoPort = await listen(echo)
109
- t.after(() => closeServer(echo))
110
- const unused = createServer()
111
- const failedPort = await listen(unused)
112
- await closeServer(unused)
113
- const relay = await startMultiplexedTcpRelay({
114
- tokens: tokens(),
115
- authorizeTarget: ({target}) => target.host === "127.0.0.1" && (target.port === echoPort || target.port === failedPort)
116
- })
117
- t.after(() => relay.close())
118
- const connection = new MultiplexedSocketductConnection({relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"}})
119
- t.after(() => connection.close())
120
- const failed = connection.createConnection({host: "127.0.0.1", port: failedPort})
121
- const sibling = connection.createConnection({host: "127.0.0.1", port: echoPort})
122
- const failure = await nextError(failed)
123
- assert.equal(failure.code, "TARGET_OPEN_FAILED")
124
- await onceEvent(sibling, "connect")
125
- const response = readText(sibling)
126
- sibling.write("still-open")
127
- assert.equal(await response, "still-open")
128
- assert.equal(relay.status().physicalConnections, 1)
129
- sibling.destroy()
130
- })
93
+ const invalidConnection = new MultiplexedSocketductConnection({
94
+ relay: {host: "127.0.0.1", port: relay.port, token: "wrong-token"},
95
+ maxReconnectAttempts: 2
96
+ })
97
+ after(() => invalidConnection.close())
98
+ const invalid = invalidConnection.createConnection({host: "127.0.0.1", port: 9})
99
+ const authError = await nextError(invalid)
100
+ expect(authError instanceof SocketductSessionLostError).toBeTruthy()
101
+ expect(authError.code).toBe("AUTH_REQUIRED")
102
+ await new Promise((resolve) => setTimeout(resolve, 30))
103
+ expect(invalidConnection.status().reconnectAttempts).toBe(0)
104
+ }))
131
105
 
132
- test("resume loss resets only the affected stream while its sibling resumes", async (t) => {
133
- /** @type {import('node:net').Socket[]} */
134
- const accepted = []
135
- const target = createServer((socket) => {
136
- accepted.push(socket)
137
- socket.on("data", (chunk) => socket.write(chunk))
138
- })
139
- const targetPort = await listen(target)
140
- t.after(() => closeServer(target))
141
- const relay = await startMultiplexedTcpRelay({
142
- tokens: tokens(),
143
- authorizeTarget: ({target: requested}) => requested.port === targetPort,
144
- resumeWindowMs: 2_000
145
- })
146
- t.after(() => relay.close())
147
- const connection = new MultiplexedSocketductConnection({
148
- relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"},
149
- reconnectDelayMs: 40
150
- })
151
- t.after(() => connection.close())
152
- const lost = connection.createConnection({host: "127.0.0.1", port: targetPort, sessionName: "lost"})
153
- const survivor = connection.createConnection({host: "127.0.0.1", port: targetPort, sessionName: "survivor"})
154
- await Promise.all([onceEvent(lost, "connect"), onceEvent(survivor, "connect")])
155
- assert.equal(accepted.length, 2)
156
- const lostError = nextError(lost)
157
- const survivorReady = onceEvent(survivor, "ready")
158
- await relay.closeActiveTransport()
159
- const targetToLose = accepted[0]
160
- assert(targetToLose !== undefined)
161
- targetToLose.destroy()
162
- const error = await lostError
163
- assert(error instanceof SocketductSessionLostError)
164
- assert.equal(error.code, "RESUME_SESSION_LOST")
165
- await survivorReady
166
- const response = readText(survivor)
167
- survivor.write("resumed")
168
- assert.equal(await response, "resumed")
169
- assert.equal(connection.status().reconnectAttempts, 1)
170
- survivor.destroy()
171
- })
106
+ it("target open failure and stream reset are isolated from siblings", async () => withTestCleanup(async (after) => {
107
+ const echo = createServer((socket) => socket.on("data", (chunk) => socket.write(chunk)))
108
+ const echoPort = await listen(echo)
109
+ after(() => closeServer(echo))
110
+ const unused = createServer()
111
+ const failedPort = await listen(unused)
112
+ await closeServer(unused)
113
+ const relay = await startMultiplexedTcpRelay({
114
+ tokens: tokens(),
115
+ authorizeTarget: ({target}) => target.host === "127.0.0.1" && (target.port === echoPort || target.port === failedPort)
116
+ })
117
+ after(() => relay.close())
118
+ const connection = new MultiplexedSocketductConnection({relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"}})
119
+ after(() => connection.close())
120
+ const failed = connection.createConnection({host: "127.0.0.1", port: failedPort})
121
+ const sibling = connection.createConnection({host: "127.0.0.1", port: echoPort})
122
+ const failure = await nextError(failed)
123
+ expect(failure.code).toBe("TARGET_OPEN_FAILED")
124
+ await onceEvent(sibling, "connect")
125
+ const response = readText(sibling)
126
+ sibling.write("still-open")
127
+ expect(await response).toBe("still-open")
128
+ expect(relay.status().physicalConnections).toBe(1)
129
+ sibling.destroy()
130
+ }))
172
131
 
173
- test("a drop between OPEN and OPENED resumes the same stream ID safely", async (t) => {
174
- /** @type {Duplex | undefined} */
175
- let delayedTarget
176
- const relay = await startMultiplexedTcpRelay({
177
- tokens: tokens(),
178
- authorizeTarget: () => true,
179
- createTargetConnection: () => {
180
- delayedTarget = new Duplex({
181
- read() {},
182
- write(_chunk, _encoding, callback) { callback() }
183
- })
184
- return /** @type {import('node:net').Socket} */ (/** @type {unknown} */ (delayedTarget))
185
- }
186
- })
187
- t.after(() => relay.close())
188
- const connection = new MultiplexedSocketductConnection({
189
- relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"},
190
- reconnectDelayMs: 30
191
- })
192
- t.after(() => connection.close())
193
- const socket = connection.createConnection({host: "target.internal", port: 1234})
194
- const connected = onceEvent(socket, "connect")
195
- await waitUntil(() => delayedTarget !== undefined)
196
- await relay.closeActiveTransport()
197
- delayedTarget?.emit("connect")
198
- await connected
199
- assert.equal(socket.streamId, 1)
200
- assert.equal(connection.status().reconnectAttempts, 1)
201
- assert.equal(relay.status().resumeSuccesses, 1)
202
- socket.destroy()
203
- })
132
+ it("resume loss resets only the affected stream while its sibling resumes", async () => withTestCleanup(async (after) => {
133
+ /** @type {import('node:net').Socket[]} */
134
+ const accepted = []
135
+ const target = createServer((socket) => {
136
+ accepted.push(socket)
137
+ socket.on("data", (chunk) => socket.write(chunk))
138
+ })
139
+ const targetPort = await listen(target)
140
+ after(() => closeServer(target))
141
+ const relay = await startMultiplexedTcpRelay({
142
+ tokens: tokens(),
143
+ authorizeTarget: ({target: requested}) => requested.port === targetPort,
144
+ resumeWindowMs: 2_000
145
+ })
146
+ after(() => relay.close())
147
+ const connection = new MultiplexedSocketductConnection({
148
+ relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"},
149
+ reconnectDelayMs: 40
150
+ })
151
+ after(() => connection.close())
152
+ const lost = connection.createConnection({host: "127.0.0.1", port: targetPort, sessionName: "lost"})
153
+ const survivor = connection.createConnection({host: "127.0.0.1", port: targetPort, sessionName: "survivor"})
154
+ await Promise.all([onceEvent(lost, "connect"), onceEvent(survivor, "connect")])
155
+ expect(accepted.length).toBe(2)
156
+ const lostError = nextError(lost)
157
+ const survivorReady = onceEvent(survivor, "ready")
158
+ await relay.closeActiveTransport()
159
+ const targetToLose = accepted[0]
160
+ expect(targetToLose).toBeDefined()
161
+ if (targetToLose === undefined) throw new Error("Expected an accepted socket")
162
+ targetToLose.destroy()
163
+ const error = await lostError
164
+ expect(error instanceof SocketductSessionLostError).toBeTruthy()
165
+ expect(error.code).toBe("RESUME_SESSION_LOST")
166
+ await survivorReady
167
+ const response = readText(survivor)
168
+ survivor.write("resumed")
169
+ expect(await response).toBe("resumed")
170
+ expect(connection.status().reconnectAttempts).toBe(1)
171
+ survivor.destroy()
172
+ }))
204
173
 
205
- test("bounded per-stream and aggregate queues fail only the offending stream", async (t) => {
206
- const target = createServer((socket) => socket.pause())
207
- const targetPort = await listen(target)
208
- t.after(() => closeServer(target))
209
- const relay = await startMultiplexedTcpRelay({tokens: tokens(), authorizeTarget: ({target: requested}) => requested.port === targetPort})
210
- t.after(() => relay.close())
211
- const connection = new MultiplexedSocketductConnection({
212
- relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"},
213
- maxStreamBufferBytes: 32,
214
- maxConnectionBufferBytes: 48,
215
- maxFrameBytes: 16,
216
- initialWindowBytes: 32
217
- })
218
- t.after(() => connection.close())
219
- const offender = connection.createConnection({host: "127.0.0.1", port: targetPort})
220
- const sibling = connection.createConnection({host: "127.0.0.1", port: targetPort})
221
- await Promise.all([onceEvent(offender, "connect"), onceEvent(sibling, "connect")])
222
- const errorPromise = nextError(offender)
223
- offender.write(Buffer.alloc(33))
224
- const error = await errorPromise
225
- assert.equal(error.code, "SOCKETDUCT_BUFFER_LIMIT")
226
- assert.equal(sibling.destroyed, false)
227
- assert(connection.status().dataQueuedBytes <= 48)
228
- assert(connection.status().controlQueuedBytes <= 1024 * 1024)
229
- sibling.destroy()
230
- })
174
+ it("a drop between OPEN and OPENED resumes the same stream ID safely", async () => withTestCleanup(async (after) => {
175
+ /** @type {Duplex | undefined} */
176
+ let delayedTarget
177
+ const relay = await startMultiplexedTcpRelay({
178
+ tokens: tokens(),
179
+ authorizeTarget: () => true,
180
+ createTargetConnection: () => {
181
+ delayedTarget = new Duplex({
182
+ read() {},
183
+ write(_chunk, _encoding, callback) { callback() }
184
+ })
185
+ return /** @type {import('node:net').Socket} */ (/** @type {unknown} */ (delayedTarget))
186
+ }
187
+ })
188
+ after(() => relay.close())
189
+ const connection = new MultiplexedSocketductConnection({
190
+ relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"},
191
+ reconnectDelayMs: 30
192
+ })
193
+ after(() => connection.close())
194
+ const socket = connection.createConnection({host: "target.internal", port: 1234})
195
+ const connected = onceEvent(socket, "connect")
196
+ await waitUntil(() => delayedTarget !== undefined)
197
+ await relay.closeActiveTransport()
198
+ delayedTarget?.emit("connect")
199
+ await connected
200
+ expect(socket.streamId).toBe(1)
201
+ expect(connection.status().reconnectAttempts).toBe(1)
202
+ expect(relay.status().resumeSuccesses).toBe(1)
203
+ socket.destroy()
204
+ }))
231
205
 
232
- test("round-robin data scheduling lets a small control flow pass a large upload", async (t) => {
233
- let largeBytesObserved = 0
234
- let largeCompleted = false
235
- let smallObservedAfterLargeCompleted
236
- const largeTarget = createServer((socket) => socket.on("data", (chunk) => {
237
- largeBytesObserved += chunk.length
238
- if (largeBytesObserved === 64 * 1024) largeCompleted = true
206
+ it("bounded per-stream and aggregate queues fail only the offending stream", async () => withTestCleanup(async (after) => {
207
+ const target = createServer((socket) => socket.pause())
208
+ const targetPort = await listen(target)
209
+ after(() => closeServer(target))
210
+ const relay = await startMultiplexedTcpRelay({tokens: tokens(), authorizeTarget: ({target: requested}) => requested.port === targetPort})
211
+ after(() => relay.close())
212
+ const connection = new MultiplexedSocketductConnection({
213
+ relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"},
214
+ maxStreamBufferBytes: 32,
215
+ maxConnectionBufferBytes: 48,
216
+ maxFrameBytes: 16,
217
+ initialWindowBytes: 32
218
+ })
219
+ after(() => connection.close())
220
+ const offender = connection.createConnection({host: "127.0.0.1", port: targetPort})
221
+ const sibling = connection.createConnection({host: "127.0.0.1", port: targetPort})
222
+ await Promise.all([onceEvent(offender, "connect"), onceEvent(sibling, "connect")])
223
+ const errorPromise = nextError(offender)
224
+ offender.write(Buffer.alloc(33))
225
+ const error = await errorPromise
226
+ expect(error.code).toBe("SOCKETDUCT_BUFFER_LIMIT")
227
+ expect(sibling.destroyed).toBe(false)
228
+ expect(connection.status().dataQueuedBytes <= 48).toBeTruthy()
229
+ expect(connection.status().controlQueuedBytes <= 1024 * 1024).toBeTruthy()
230
+ sibling.destroy()
239
231
  }))
240
- const largePort = await listen(largeTarget)
241
- t.after(() => closeServer(largeTarget))
242
- const smallTarget = createServer((socket) => socket.on("data", (chunk) => {
243
- smallObservedAfterLargeCompleted = largeCompleted
244
- socket.write(chunk)
232
+
233
+ it("round-robin data scheduling lets a small control flow pass a large upload", async () => withTestCleanup(async (after) => {
234
+ let largeBytesObserved = 0
235
+ let largeCompleted = false
236
+ let smallObservedAfterLargeCompleted
237
+ const largeTarget = createServer((socket) => socket.on("data", (chunk) => {
238
+ largeBytesObserved += chunk.length
239
+ if (largeBytesObserved === 64 * 1024) largeCompleted = true
240
+ }))
241
+ const largePort = await listen(largeTarget)
242
+ after(() => closeServer(largeTarget))
243
+ const smallTarget = createServer((socket) => socket.on("data", (chunk) => {
244
+ smallObservedAfterLargeCompleted = largeCompleted
245
+ socket.write(chunk)
246
+ }))
247
+ const smallPort = await listen(smallTarget)
248
+ after(() => closeServer(smallTarget))
249
+ const relay = await startMultiplexedTcpRelay({
250
+ tokens: tokens(),
251
+ authorizeTarget: ({target}) => target.port === largePort || target.port === smallPort,
252
+ maxFrameBytes: 1024
253
+ })
254
+ after(() => relay.close())
255
+ const connection = new MultiplexedSocketductConnection({
256
+ relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"},
257
+ maxFrameBytes: 1024
258
+ })
259
+ after(() => connection.close())
260
+ const large = connection.createConnection({host: "127.0.0.1", port: largePort})
261
+ const small = connection.createConnection({host: "127.0.0.1", port: smallPort})
262
+ await Promise.all([onceEvent(large, "connect"), onceEvent(small, "connect")])
263
+ const smallResponse = readText(small)
264
+ large.write(Buffer.alloc(64 * 1024))
265
+ small.write("control")
266
+ expect(await smallResponse).toBe("control")
267
+ expect(smallObservedAfterLargeCompleted).toBe(false)
268
+ expect(largeBytesObserved < 64 * 1024).toBeTruthy()
269
+ large.destroy()
270
+ small.destroy()
245
271
  }))
246
- const smallPort = await listen(smallTarget)
247
- t.after(() => closeServer(smallTarget))
248
- const relay = await startMultiplexedTcpRelay({
249
- tokens: tokens(),
250
- authorizeTarget: ({target}) => target.port === largePort || target.port === smallPort,
251
- maxFrameBytes: 1024
252
- })
253
- t.after(() => relay.close())
254
- const connection = new MultiplexedSocketductConnection({
255
- relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"},
256
- maxFrameBytes: 1024
257
- })
258
- t.after(() => connection.close())
259
- const large = connection.createConnection({host: "127.0.0.1", port: largePort})
260
- const small = connection.createConnection({host: "127.0.0.1", port: smallPort})
261
- await Promise.all([onceEvent(large, "connect"), onceEvent(small, "connect")])
262
- const smallResponse = readText(small)
263
- large.write(Buffer.alloc(64 * 1024))
264
- small.write("control")
265
- assert.equal(await smallResponse, "control")
266
- assert.equal(smallObservedAfterLargeCompleted, false)
267
- assert(largeBytesObserved < 64 * 1024)
268
- large.destroy()
269
- small.destroy()
270
- })
271
272
 
272
- test("per-stream receive windows bound a paused consumer and resume independently", async (t) => {
273
- const payload = Buffer.alloc(4 * 1024, 0x61)
274
- const target = createServer((socket) => socket.write(payload))
275
- const targetPort = await listen(target)
276
- t.after(() => closeServer(target))
277
- const relay = await startMultiplexedTcpRelay({
278
- tokens: tokens(),
279
- authorizeTarget: ({target: requested}) => requested.port === targetPort,
280
- initialWindowBytes: 1024,
281
- maxFrameBytes: 512,
282
- maxStreamBufferBytes: 8 * 1024
283
- })
284
- t.after(() => relay.close())
285
- const connection = new MultiplexedSocketductConnection({
286
- relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"},
287
- initialWindowBytes: 1024,
288
- maxFrameBytes: 512
289
- })
290
- t.after(() => connection.close())
291
- const paused = connection.createConnection({
292
- host: "127.0.0.1",
293
- port: targetPort,
294
- readableHighWaterMark: 256
295
- })
296
- await onceEvent(paused, "connect")
297
- await waitUntil(() => relay.status().queuedBytes === payload.length)
298
- assert.equal(relay.status().resets, 0)
299
- assert(relay.status().queuedBytes <= 8 * 1024)
273
+ it("per-stream receive windows bound a paused consumer and resume independently", async () => withTestCleanup(async (after) => {
274
+ const payload = Buffer.alloc(4 * 1024, 0x61)
275
+ const target = createServer((socket) => socket.write(payload))
276
+ const targetPort = await listen(target)
277
+ after(() => closeServer(target))
278
+ const relay = await startMultiplexedTcpRelay({
279
+ tokens: tokens(),
280
+ authorizeTarget: ({target: requested}) => requested.port === targetPort,
281
+ initialWindowBytes: 1024,
282
+ maxFrameBytes: 512,
283
+ maxStreamBufferBytes: 8 * 1024
284
+ })
285
+ after(() => relay.close())
286
+ const connection = new MultiplexedSocketductConnection({
287
+ relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"},
288
+ initialWindowBytes: 1024,
289
+ maxFrameBytes: 512
290
+ })
291
+ after(() => connection.close())
292
+ const paused = connection.createConnection({
293
+ host: "127.0.0.1",
294
+ port: targetPort,
295
+ readableHighWaterMark: 256
296
+ })
297
+ await onceEvent(paused, "connect")
298
+ await waitUntil(() => relay.status().queuedBytes === payload.length)
299
+ expect(relay.status().resets).toBe(0)
300
+ expect(relay.status().queuedBytes <= 8 * 1024).toBeTruthy()
300
301
 
301
- const received = await new Promise((resolve, reject) => {
302
- /** @type {Buffer[]} */
303
- const chunks = []
304
- let bytes = 0
305
- paused.on("data", (chunk) => {
306
- chunks.push(chunk)
307
- bytes += chunk.length
308
- if (bytes === payload.length) resolve(Buffer.concat(chunks))
302
+ const received = await new Promise((resolve, reject) => {
303
+ /** @type {Buffer[]} */
304
+ const chunks = []
305
+ let bytes = 0
306
+ paused.on("data", (chunk) => {
307
+ chunks.push(chunk)
308
+ bytes += chunk.length
309
+ if (bytes === payload.length) resolve(Buffer.concat(chunks))
310
+ })
311
+ paused.once("error", reject)
312
+ paused.resume()
309
313
  })
310
- paused.once("error", reject)
311
- paused.resume()
312
- })
313
- assert.deepEqual(received, payload)
314
- await waitUntil(() => relay.status().queuedBytes === 0)
315
- paused.destroy()
316
- })
314
+ expect(received.equals(payload)).toBe(true)
315
+ await waitUntil(() => relay.status().queuedBytes === 0)
316
+ paused.destroy()
317
+ }))
317
318
 
318
- test("relay accepts local target sockets without creating extra WAN transports", async (t) => {
319
- const target = createServer((socket) => socket.end())
320
- const port = await listen(target)
321
- t.after(() => closeServer(target))
322
- let localConnects = 0
323
- const relay = await startMultiplexedTcpRelay({
324
- tokens: tokens(),
325
- authorizeTarget: ({target: requested}) => requested.port === port,
326
- createTargetConnection: (requested) => {
327
- localConnects += 1
328
- return createConnection(requested)
329
- }
330
- })
331
- t.after(() => relay.close())
332
- const connection = new MultiplexedSocketductConnection({relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"}})
333
- t.after(() => connection.close())
334
- const streams = Array.from({length: 4}, () => connection.createConnection({host: "127.0.0.1", port}))
335
- await Promise.all(streams.map((socket) => onceEvent(socket, "connect")))
336
- assert.equal(localConnects, 4)
337
- assert.equal(relay.status().physicalAccepts, 1)
338
- streams.forEach((socket) => socket.destroy())
319
+ it("relay accepts local target sockets without creating extra WAN transports", async () => withTestCleanup(async (after) => {
320
+ const target = createServer((socket) => socket.end())
321
+ const port = await listen(target)
322
+ after(() => closeServer(target))
323
+ let localConnects = 0
324
+ const relay = await startMultiplexedTcpRelay({
325
+ tokens: tokens(),
326
+ authorizeTarget: ({target: requested}) => requested.port === port,
327
+ createTargetConnection: (requested) => {
328
+ localConnects += 1
329
+ return createConnection(requested)
330
+ }
331
+ })
332
+ after(() => relay.close())
333
+ const connection = new MultiplexedSocketductConnection({relay: {host: "127.0.0.1", port: relay.port, token: "valid-token"}})
334
+ after(() => connection.close())
335
+ const streams = Array.from({length: 4}, () => connection.createConnection({host: "127.0.0.1", port}))
336
+ await Promise.all(streams.map((socket) => onceEvent(socket, "connect")))
337
+ expect(localConnects).toBe(4)
338
+ expect(relay.status().physicalAccepts).toBe(1)
339
+ streams.forEach((socket) => socket.destroy())
340
+ }))
339
341
  })