socketduct 0.1.24 → 0.1.26

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 (60) hide show
  1. package/AGENTS.md +2 -2
  2. package/changelog.d/20261010130509-bound-log-frame-compression-memory.md +1 -0
  3. package/docs/runbooks/jsonl-integrity-rollout.md +8 -6
  4. package/package.json +2 -2
  5. package/scripts/test.js +9 -14
  6. package/src/client/multiplexed-transport-set.js +20 -2
  7. package/src/logging/compressed-log-writer.js +1 -1
  8. package/src/logging/zstd-frame-codec.js +15 -1
  9. package/src/reverse/client.js +44 -1
  10. package/src/reverse/hub.js +10 -1
  11. package/src/reverse/runtime.js +11 -2
  12. package/test/e2e/gateway-process.test.js +93 -92
  13. package/test/e2e/logger-shutdown-process.test.js +40 -37
  14. package/test/e2e/reverse-client-recovery-process.test.js +107 -103
  15. package/test/e2e/reverse-gateway-process.test.js +328 -318
  16. package/test/integration/initial-target-recovery.test.js +333 -334
  17. package/test/integration/logging-cli.test.js +121 -119
  18. package/test/integration/logging-compressed-recovery.test.js +96 -95
  19. package/test/integration/multiplexed-compatibility.test.js +178 -177
  20. package/test/integration/multiplexed-corrections.test.js +360 -359
  21. package/test/integration/multiplexed-failures.test.js +253 -251
  22. package/test/integration/multiplexed-transport-set.test.js +334 -322
  23. package/test/integration/multiplexed-transport.test.js +135 -134
  24. package/test/integration/reverse-gateway-stream-id-reset.test.js +90 -89
  25. package/test/integration/reverse-gateway.test.js +2309 -2218
  26. package/test/integration/reverse-resume-continuity.test.js +219 -218
  27. package/test/integration/reverse-route-class.test.js +363 -278
  28. package/test/integration/socketduct-http-agent.test.js +314 -312
  29. package/test/integration/socketduct-https-agent.test.js +516 -485
  30. package/test/integration/stream-compression.test.js +862 -824
  31. package/test/integration/tcp-forwarding.test.js +2550 -2447
  32. package/test/integration/tls-forwarding.test.js +593 -592
  33. package/test/unit/auth/auth.test.js +43 -44
  34. package/test/unit/auth/static-token-store.test.js +9 -8
  35. package/test/unit/client/connect.test.js +41 -40
  36. package/test/unit/client/deferred-virtual-socket.test.js +95 -94
  37. package/test/unit/client/multiplexed-spool-budget.test.js +155 -154
  38. package/test/unit/client/multiplexed-transport-set.test.js +845 -732
  39. package/test/unit/client/socketduct-socket.test.js +768 -771
  40. package/test/unit/client/virtual-socket.test.js +198 -168
  41. package/test/unit/config/docker-gateway-files.test.js +224 -224
  42. package/test/unit/config/environment.test.js +472 -492
  43. package/test/unit/index.test.js +53 -52
  44. package/test/unit/logging/compressed-log-worker.test.js +23 -22
  45. package/test/unit/logging/compressed-log-writer.test.js +155 -154
  46. package/test/unit/logging/log-retention.test.js +52 -47
  47. package/test/unit/logging/log-segment-index.test.js +84 -81
  48. package/test/unit/logging/zstd-frame-codec.test.js +28 -19
  49. package/test/unit/protocol/compression.test.js +254 -298
  50. package/test/unit/protocol/frame.test.js +280 -276
  51. package/test/unit/protocol/v3-compression.test.js +21 -23
  52. package/test/unit/protocol/v3-frame.test.js +42 -41
  53. package/test/unit/release-configuration.test.js +15 -12
  54. package/test/unit/reverse/disconnect-attribution.test.js +65 -64
  55. package/test/unit/reverse-terminal-outcomes.test.js +53 -50
  56. package/test/unit/server/control-handler.test.js +232 -231
  57. package/test/unit/server/primary.test.js +51 -49
  58. package/test/unit/server/relay-runtime.test.js +280 -294
  59. package/test/unit/sessions/session.test.js +48 -62
  60. 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
  })