@libp2p/interface-compliance-tests 6.4.16 → 6.5.0-8484de8a2

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 (132) hide show
  1. package/dist/src/connection-encryption/index.d.ts.map +1 -1
  2. package/dist/src/connection-encryption/index.js +24 -15
  3. package/dist/src/connection-encryption/index.js.map +1 -1
  4. package/dist/src/stream-muxer/base-test.d.ts.map +1 -1
  5. package/dist/src/stream-muxer/base-test.js +62 -341
  6. package/dist/src/stream-muxer/base-test.js.map +1 -1
  7. package/dist/src/stream-muxer/close-test.d.ts.map +1 -1
  8. package/dist/src/stream-muxer/close-test.js +254 -305
  9. package/dist/src/stream-muxer/close-test.js.map +1 -1
  10. package/dist/src/stream-muxer/index.js +2 -2
  11. package/dist/src/stream-muxer/index.js.map +1 -1
  12. package/dist/src/stream-muxer/{mega-stress-test.d.ts → stream-test.d.ts} +2 -2
  13. package/dist/src/stream-muxer/stream-test.d.ts.map +1 -0
  14. package/dist/src/stream-muxer/stream-test.js +289 -0
  15. package/dist/src/stream-muxer/stream-test.js.map +1 -0
  16. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -1
  17. package/dist/src/stream-muxer/stress-test.js +70 -16
  18. package/dist/src/stream-muxer/stress-test.js.map +1 -1
  19. package/dist/src/transport/index.d.ts.map +1 -1
  20. package/dist/src/transport/index.js +232 -203
  21. package/dist/src/transport/index.js.map +1 -1
  22. package/dist/src/transport/utils.js +2 -2
  23. package/dist/src/transport/utils.js.map +1 -1
  24. package/package.json +23 -51
  25. package/src/connection-encryption/index.ts +27 -20
  26. package/src/stream-muxer/base-test.ts +75 -409
  27. package/src/stream-muxer/close-test.ts +304 -327
  28. package/src/stream-muxer/index.ts +2 -2
  29. package/src/stream-muxer/stream-test.ts +380 -0
  30. package/src/stream-muxer/stress-test.ts +92 -18
  31. package/src/transport/index.ts +280 -241
  32. package/src/transport/utils.ts +2 -2
  33. package/dist/src/connection-encryption/utils/index.d.ts +0 -3
  34. package/dist/src/connection-encryption/utils/index.d.ts.map +0 -1
  35. package/dist/src/connection-encryption/utils/index.js +0 -21
  36. package/dist/src/connection-encryption/utils/index.js.map +0 -1
  37. package/dist/src/matchers.d.ts +0 -12
  38. package/dist/src/matchers.d.ts.map +0 -1
  39. package/dist/src/matchers.js +0 -14
  40. package/dist/src/matchers.js.map +0 -1
  41. package/dist/src/mocks/connection-manager.d.ts +0 -27
  42. package/dist/src/mocks/connection-manager.d.ts.map +0 -1
  43. package/dist/src/mocks/connection-manager.js +0 -147
  44. package/dist/src/mocks/connection-manager.js.map +0 -1
  45. package/dist/src/mocks/connection.d.ts +0 -41
  46. package/dist/src/mocks/connection.d.ts.map +0 -1
  47. package/dist/src/mocks/connection.js +0 -234
  48. package/dist/src/mocks/connection.js.map +0 -1
  49. package/dist/src/mocks/duplex.d.ts +0 -4
  50. package/dist/src/mocks/duplex.d.ts.map +0 -1
  51. package/dist/src/mocks/duplex.js +0 -9
  52. package/dist/src/mocks/duplex.js.map +0 -1
  53. package/dist/src/mocks/index.d.ts +0 -12
  54. package/dist/src/mocks/index.d.ts.map +0 -1
  55. package/dist/src/mocks/index.js +0 -8
  56. package/dist/src/mocks/index.js.map +0 -1
  57. package/dist/src/mocks/multiaddr-connection.d.ts +0 -17
  58. package/dist/src/mocks/multiaddr-connection.d.ts.map +0 -1
  59. package/dist/src/mocks/multiaddr-connection.js +0 -64
  60. package/dist/src/mocks/multiaddr-connection.js.map +0 -1
  61. package/dist/src/mocks/muxer.d.ts +0 -36
  62. package/dist/src/mocks/muxer.d.ts.map +0 -1
  63. package/dist/src/mocks/muxer.js +0 -234
  64. package/dist/src/mocks/muxer.js.map +0 -1
  65. package/dist/src/mocks/registrar.d.ts +0 -16
  66. package/dist/src/mocks/registrar.d.ts.map +0 -1
  67. package/dist/src/mocks/registrar.js +0 -66
  68. package/dist/src/mocks/registrar.js.map +0 -1
  69. package/dist/src/mocks/upgrader.d.ts +0 -9
  70. package/dist/src/mocks/upgrader.d.ts.map +0 -1
  71. package/dist/src/mocks/upgrader.js +0 -46
  72. package/dist/src/mocks/upgrader.js.map +0 -1
  73. package/dist/src/pubsub/api.d.ts +0 -6
  74. package/dist/src/pubsub/api.d.ts.map +0 -1
  75. package/dist/src/pubsub/api.js +0 -88
  76. package/dist/src/pubsub/api.js.map +0 -1
  77. package/dist/src/pubsub/connection-handlers.d.ts +0 -6
  78. package/dist/src/pubsub/connection-handlers.d.ts.map +0 -1
  79. package/dist/src/pubsub/connection-handlers.js +0 -329
  80. package/dist/src/pubsub/connection-handlers.js.map +0 -1
  81. package/dist/src/pubsub/emit-self.d.ts +0 -6
  82. package/dist/src/pubsub/emit-self.d.ts.map +0 -1
  83. package/dist/src/pubsub/emit-self.js +0 -80
  84. package/dist/src/pubsub/emit-self.js.map +0 -1
  85. package/dist/src/pubsub/index.d.ts +0 -18
  86. package/dist/src/pubsub/index.d.ts.map +0 -1
  87. package/dist/src/pubsub/index.js +0 -17
  88. package/dist/src/pubsub/index.js.map +0 -1
  89. package/dist/src/pubsub/messages.d.ts +0 -6
  90. package/dist/src/pubsub/messages.d.ts.map +0 -1
  91. package/dist/src/pubsub/messages.js +0 -48
  92. package/dist/src/pubsub/messages.js.map +0 -1
  93. package/dist/src/pubsub/multiple-nodes.d.ts +0 -6
  94. package/dist/src/pubsub/multiple-nodes.d.ts.map +0 -1
  95. package/dist/src/pubsub/multiple-nodes.js +0 -350
  96. package/dist/src/pubsub/multiple-nodes.js.map +0 -1
  97. package/dist/src/pubsub/two-nodes.d.ts +0 -6
  98. package/dist/src/pubsub/two-nodes.d.ts.map +0 -1
  99. package/dist/src/pubsub/two-nodes.js +0 -216
  100. package/dist/src/pubsub/two-nodes.js.map +0 -1
  101. package/dist/src/pubsub/utils.d.ts +0 -5
  102. package/dist/src/pubsub/utils.d.ts.map +0 -1
  103. package/dist/src/pubsub/utils.js +0 -27
  104. package/dist/src/pubsub/utils.js.map +0 -1
  105. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +0 -1
  106. package/dist/src/stream-muxer/mega-stress-test.js +0 -11
  107. package/dist/src/stream-muxer/mega-stress-test.js.map +0 -1
  108. package/dist/src/stream-muxer/spawner.d.ts +0 -4
  109. package/dist/src/stream-muxer/spawner.d.ts.map +0 -1
  110. package/dist/src/stream-muxer/spawner.js +0 -37
  111. package/dist/src/stream-muxer/spawner.js.map +0 -1
  112. package/dist/typedoc-urls.json +0 -44
  113. package/src/connection-encryption/utils/index.ts +0 -27
  114. package/src/matchers.ts +0 -18
  115. package/src/mocks/connection-manager.ts +0 -216
  116. package/src/mocks/connection.ts +0 -307
  117. package/src/mocks/duplex.ts +0 -11
  118. package/src/mocks/index.ts +0 -11
  119. package/src/mocks/multiaddr-connection.ts +0 -80
  120. package/src/mocks/muxer.ts +0 -331
  121. package/src/mocks/registrar.ts +0 -86
  122. package/src/mocks/upgrader.ts +0 -65
  123. package/src/pubsub/api.ts +0 -116
  124. package/src/pubsub/connection-handlers.ts +0 -413
  125. package/src/pubsub/emit-self.ts +0 -99
  126. package/src/pubsub/index.ts +0 -34
  127. package/src/pubsub/messages.ts +0 -59
  128. package/src/pubsub/multiple-nodes.ts +0 -440
  129. package/src/pubsub/two-nodes.ts +0 -272
  130. package/src/pubsub/utils.ts +0 -34
  131. package/src/stream-muxer/mega-stress-test.ts +0 -14
  132. package/src/stream-muxer/spawner.ts +0 -55
@@ -1,13 +1,15 @@
1
- import { stop } from '@libp2p/interface'
1
+ import { stop, TimeoutError } from '@libp2p/interface'
2
+ import { prefixLogger } from '@libp2p/logger'
2
3
  import { expect } from 'aegir/chai'
3
4
  import delay from 'delay'
5
+ import all from 'it-all'
4
6
  import drain from 'it-drain'
5
7
  import { pushable } from 'it-pushable'
6
- import pDefer from 'p-defer'
7
8
  import { pEvent } from 'p-event'
8
9
  import pRetry from 'p-retry'
9
10
  import pWaitFor from 'p-wait-for'
10
11
  import { raceSignal } from 'race-signal'
12
+ import { Uint8ArrayList } from 'uint8arraylist'
11
13
  import { isValidTick } from '../is-valid-tick.js'
12
14
  import { createPeer, getTransportManager, getUpgrader } from './utils.js'
13
15
  import type { TestSetup } from '../index.js'
@@ -16,7 +18,6 @@ import type { Connection, Libp2p, Stream, StreamHandler } from '@libp2p/interfac
16
18
  import type { Multiaddr } from '@multiformats/multiaddr'
17
19
  import type { MultiaddrMatcher } from '@multiformats/multiaddr-matcher'
18
20
  import type { Libp2pInit } from 'libp2p'
19
- import type { DeferredPromise } from 'p-defer'
20
21
 
21
22
  export interface TransportTestFixtures {
22
23
  /**
@@ -48,34 +49,113 @@ export interface TransportTestFixtures {
48
49
 
49
50
  async function getSetup (common: TestSetup<TransportTestFixtures>): Promise<{ dialer: Libp2p<{ echo: Echo }>, listener?: Libp2p<{ echo: Echo }>, dialAddrs: Multiaddr[], dialMultiaddrMatcher: MultiaddrMatcher, listenMultiaddrMatcher: MultiaddrMatcher }> {
50
51
  const setup = await common.setup()
51
- const dialer = await createPeer(setup.dialer)
52
+ const dialer = await createPeer({
53
+ logger: prefixLogger('dialer'),
54
+ ...setup.dialer
55
+ })
52
56
  let listener
53
57
 
54
58
  if (setup.listener != null) {
55
- listener = await createPeer(setup.listener)
59
+ listener = await createPeer({
60
+ logger: prefixLogger('listener'),
61
+ ...setup.listener
62
+ })
56
63
  }
57
64
 
58
- const dialAddrs = listener?.getMultiaddrs() ?? setup.dialAddrs
65
+ let dialAddrs = listener?.getMultiaddrs() ?? setup.dialAddrs
59
66
 
60
67
  if (dialAddrs == null) {
61
68
  throw new Error('Listener config or dial addresses must be specified')
62
69
  }
63
70
 
71
+ dialAddrs = dialAddrs.filter(ma => setup.dialMultiaddrMatcher.exactMatch(ma))
72
+
73
+ if (dialAddrs.length === 0) {
74
+ throw new Error('Listener was not listening on any addresses that the dialMultiaddrMatcher matched')
75
+ }
76
+
64
77
  return {
65
78
  dialer,
66
79
  listener,
67
- dialAddrs: dialAddrs.filter(ma => setup.dialMultiaddrMatcher.exactMatch(ma)),
80
+ dialAddrs,
68
81
  dialMultiaddrMatcher: setup.dialMultiaddrMatcher,
69
82
  listenMultiaddrMatcher: setup.listenMultiaddrMatcher
70
83
  }
71
84
  }
72
85
 
73
86
  export default (common: TestSetup<TransportTestFixtures>): void => {
87
+ describe('transport events', () => {
88
+ let dialer: Libp2p<{ echo: Echo }>
89
+ let listener: Libp2p<{ echo: Echo }> | undefined
90
+ let listenMultiaddrMatcher: MultiaddrMatcher
91
+
92
+ afterEach(async () => {
93
+ await stop(dialer, listener)
94
+ await common.teardown()
95
+ })
96
+
97
+ it('emits listening', async function () {
98
+ ({ dialer, listener, listenMultiaddrMatcher } = await getSetup(common))
99
+
100
+ if (listener == null) {
101
+ return this.skip()
102
+ }
103
+
104
+ await listener.stop()
105
+
106
+ const transportListeningPromise = Promise.withResolvers<void>()
107
+
108
+ listener.addEventListener('transport:listening', (event) => {
109
+ const transportListener = event.detail
110
+
111
+ if (transportListener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma))) {
112
+ transportListeningPromise.resolve()
113
+ }
114
+ })
115
+
116
+ await listener.start()
117
+
118
+ await raceSignal(transportListeningPromise.promise, AbortSignal.timeout(1000), {
119
+ translateError: () => {
120
+ return new TimeoutError('Did not emit listening event')
121
+ }
122
+ })
123
+ })
124
+
125
+ it('emits close', async function () {
126
+ ({ dialer, listener } = await getSetup(common))
127
+
128
+ if (listener == null) {
129
+ return this.skip()
130
+ }
131
+
132
+ const transportManager = getTransportManager(listener)
133
+ const transportListener = transportManager.getListeners()
134
+ .filter(listener => listener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma)))
135
+ .pop()
136
+
137
+ if (transportListener == null) {
138
+ throw new Error('Could not find address listener')
139
+ }
140
+
141
+ const p = pEvent(transportListener, 'close')
142
+
143
+ await listener.stop()
144
+
145
+ await raceSignal(p, AbortSignal.timeout(1000), {
146
+ translateError: () => {
147
+ return new TimeoutError('Did not emit close event')
148
+ }
149
+ })
150
+ })
151
+ })
152
+
74
153
  describe('interface-transport', () => {
75
154
  let dialer: Libp2p<{ echo: Echo }>
76
155
  let listener: Libp2p<{ echo: Echo }> | undefined
77
156
  let dialAddrs: Multiaddr[]
78
157
  let dialMultiaddrMatcher: MultiaddrMatcher
158
+ let listenMultiaddrMatcher: MultiaddrMatcher
79
159
 
80
160
  afterEach(async () => {
81
161
  await stop(dialer, listener)
@@ -90,7 +170,7 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
90
170
  signal: AbortSignal.timeout(5000)
91
171
  })
92
172
 
93
- expect(output).to.equalBytes(input)
173
+ expect(output.subarray()).to.equalBytes(input)
94
174
  })
95
175
 
96
176
  it('should listen on multiple addresses', async () => {
@@ -98,13 +178,16 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
98
178
 
99
179
  const input = Uint8Array.from([0, 1, 2, 3, 4])
100
180
 
101
- await expect(dialer.services.echo.echo(dialAddrs[0], input, {
181
+ const output1 = await dialer.services.echo.echo(dialAddrs[0], input, {
102
182
  signal: AbortSignal.timeout(5000)
103
- })).to.eventually.deep.equal(input)
183
+ })
184
+ expect(output1.subarray()).to.equalBytes(input)
104
185
 
105
- await expect(dialer.services.echo.echo(dialAddrs[1], input, {
106
- signal: AbortSignal.timeout(5000)
107
- })).to.eventually.deep.equal(input)
186
+ const output2 = await dialer.services.echo.echo(dialAddrs[1], input, {
187
+ signal: AbortSignal.timeout(5000),
188
+ force: true
189
+ })
190
+ expect(output2.subarray()).to.equalBytes(input)
108
191
  })
109
192
 
110
193
  it('can close connections', async () => {
@@ -131,16 +214,20 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
131
214
  })
132
215
 
133
216
  it('should close all streams when the connection closes', async () => {
134
- ({ dialer, listener, dialAddrs } = await getSetup(common))
217
+ ({ dialer, listener, dialAddrs, listenMultiaddrMatcher } = await getSetup(common))
135
218
 
136
- let incomingConnectionPromise: DeferredPromise<Connection> | undefined
219
+ let incomingConnectionPromise: PromiseWithResolvers<Connection> | undefined
137
220
 
138
221
  if (listener != null) {
139
- incomingConnectionPromise = pDefer<Connection>()
222
+ incomingConnectionPromise = Promise.withResolvers<Connection>()
140
223
 
141
224
  listener.addEventListener('connection:open', (event) => {
142
225
  const conn = event.detail
143
226
 
227
+ if (!listenMultiaddrMatcher.matches(conn.remoteAddr)) {
228
+ return
229
+ }
230
+
144
231
  if (conn.remotePeer.equals(dialer.peerId)) {
145
232
  incomingConnectionPromise?.resolve(conn)
146
233
  }
@@ -160,22 +247,28 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
160
247
  })
161
248
  }
162
249
 
163
- const streams = connection.streams
164
-
165
- // Close the connection and verify all streams have been closed
166
- await connection.close()
167
-
168
- await pWaitFor(() => connection.streams.length === 0, {
169
- timeout: 5000
170
- })
250
+ expect(connection).to.have.property('streams').that.has.lengthOf(5)
171
251
 
172
252
  if (remoteConn != null) {
173
- await pWaitFor(() => remoteConn.streams.length === 0, {
253
+ await pWaitFor(() => remoteConn.streams.length === 5, {
174
254
  timeout: 5000
175
255
  })
176
256
  }
177
257
 
178
- expect(streams.find(stream => stream.status === 'open')).to.be.undefined()
258
+ // Close the connection and verify all streams have been closed
259
+ await Promise.all([
260
+ pEvent(connection, 'close'),
261
+ pEvent(remoteConn ?? connection, 'close'),
262
+ connection.close()
263
+ ])
264
+
265
+ expect(connection).to.have.property('status', 'closed')
266
+ expect(connection).to.have.property('streams').that.is.empty()
267
+
268
+ if (remoteConn != null) {
269
+ expect(remoteConn).to.have.property('status', 'closed')
270
+ expect(remoteConn).to.have.property('streams').that.is.empty()
271
+ }
179
272
  })
180
273
 
181
274
  it('should not handle connection if upgradeInbound rejects', async function () {
@@ -227,188 +320,137 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
227
320
  }
228
321
  })
229
322
 
230
- it('should handle one big write', async function () {
323
+ it('should handle one small write', async function () {
231
324
  const timeout = 120_000
232
325
  this.timeout(timeout);
233
326
  ({ dialer, listener, dialAddrs } = await getSetup(common))
234
327
 
235
- const input = new Uint8Array(1024 * 1024 * 10).fill(5)
236
- const output = await dialer.services.echo.echo(dialAddrs[0], input, {
328
+ const connection = await dialer.dial(dialAddrs[0])
329
+
330
+ const input = new Uint8Array(1024).fill(5)
331
+ const output = await dialer.services.echo.echo(connection.remotePeer, input, {
237
332
  signal: AbortSignal.timeout(timeout)
238
333
  })
239
334
 
240
- expect(output).to.equalBytes(input)
335
+ expect(output.subarray()).to.equalBytes(input)
336
+ expect(connection.streams.filter(s => s.protocol === dialer.services.echo.protocol)).to.be.empty()
241
337
  })
242
338
 
243
- it('should handle many small writes', async function () {
244
- const timeout = 360_000
245
- this.timeout(timeout);
246
- ({ dialer, listener, dialAddrs } = await getSetup(common))
247
-
248
- for (let i = 0; i < 2000; i++) {
249
- const input = new Uint8Array(1024).fill(5)
250
- const output = await dialer.services.echo.echo(dialAddrs[0], input, {
251
- signal: AbortSignal.timeout(timeout)
252
- })
253
-
254
- expect(output).to.equalBytes(input)
255
- }
256
- })
257
-
258
- it('can close a stream for reading but send a large amount of data', async function () {
259
- const timeout = 120_000
339
+ it('should handle one big write', async function () {
340
+ const timeout = 540_000
260
341
  this.timeout(timeout);
261
342
  ({ dialer, listener, dialAddrs } = await getSetup(common))
262
343
 
263
- if (listener == null) {
264
- return this.skip()
265
- }
266
-
267
- const protocol = '/send-data/1.0.0'
268
- const chunkSize = 1024
269
- const bytes = chunkSize * 1024 * 10
270
- const deferred = pDefer()
271
-
272
- await listener.handle(protocol, ({ stream }) => {
273
- Promise.resolve().then(async () => {
274
- let read = 0
275
-
276
- for await (const buf of stream.source) {
277
- read += buf.byteLength
344
+ const connection = await dialer.dial(dialAddrs[0])
278
345
 
279
- if (read === bytes) {
280
- deferred.resolve()
281
- break
282
- }
283
- }
284
- })
285
- .catch(err => {
286
- deferred.reject(err)
287
- stream.abort(err)
288
- })
346
+ const input = new Uint8Array(1024 * 1024 * 10).fill(5)
347
+ const output = await dialer.services.echo.echo(connection.remotePeer, input, {
348
+ signal: AbortSignal.timeout(timeout)
289
349
  })
290
350
 
291
- const stream = await dialer.dialProtocol(dialAddrs[0], protocol)
292
-
293
- await stream.closeRead()
294
-
295
- await stream.sink((async function * () {
296
- for (let i = 0; i < bytes; i += chunkSize) {
297
- yield new Uint8Array(chunkSize)
298
- }
299
- })())
300
-
301
- await stream.close()
302
-
303
- await deferred.promise
351
+ expect(output.subarray()).to.equalBytes(input)
352
+ expect(connection.streams.filter(s => s.protocol === dialer.services.echo.protocol)).to.be.empty()
304
353
  })
305
354
 
306
- it('can close a stream for writing but receive a large amount of data', async function () {
307
- const timeout = 120_000
355
+ it('should handle many small writes', async function () {
356
+ const timeout = 360_000
308
357
  this.timeout(timeout);
309
358
  ({ dialer, listener, dialAddrs } = await getSetup(common))
310
359
 
311
- if (listener == null) {
312
- return this.skip()
313
- }
314
-
315
- const protocol = '/receive-data/1.0.0'
316
- const chunkSize = 1024
317
- const bytes = chunkSize * 1024 * 10
318
- const deferred = pDefer()
319
-
320
- await listener.handle(protocol, ({ stream }) => {
321
- Promise.resolve().then(async () => {
322
- await stream.sink((async function * () {
323
- for (let i = 0; i < bytes; i += chunkSize) {
324
- yield new Uint8Array(chunkSize)
325
- }
326
- })())
360
+ const connection = await dialer.dial(dialAddrs[0])
361
+ const echoProtocol = dialer.services.echo.protocol
327
362
 
328
- await stream.close()
363
+ for (let i = 0; i < 2000; i++) {
364
+ const input = new Uint8Array(1024).fill(5)
365
+ const output = await dialer.services.echo.echo(connection.remotePeer, input, {
366
+ signal: AbortSignal.timeout(timeout)
329
367
  })
330
- .catch(err => {
331
- deferred.reject(err)
332
- stream.abort(err)
333
- })
334
- })
335
368
 
336
- const stream = await dialer.dialProtocol(dialAddrs[0], protocol)
337
-
338
- await stream.closeWrite()
339
-
340
- let read = 0
341
-
342
- for await (const buf of stream.source) {
343
- read += buf.byteLength
369
+ expect(output.subarray()).to.equalBytes(input)
370
+ expect(connection.streams.filter(s => s.protocol === echoProtocol)).to.be.empty()
344
371
  }
345
-
346
- expect(read).to.equal(bytes)
347
372
  })
348
373
 
349
- it('can close local stream for writing and reading while a remote stream is writing', async function () {
374
+ it('can close local stream while a remote stream is writing', async function () {
350
375
  ({ dialer, listener, dialAddrs } = await getSetup(common))
351
376
 
352
377
  if (listener == null) {
353
378
  return this.skip()
354
379
  }
355
380
 
381
+ // 1. remote stream close read
382
+ // 2. local stream close write
383
+ // 3. remote stream close write
384
+
356
385
  /**
357
386
  * NodeA NodeB
358
387
  * | <--- STOP_SENDING |
359
388
  * | FIN ---> |
389
+ * | <--- DATA |
360
390
  * | <--- FIN |
361
391
  * | FIN_ACK ---> |
362
392
  * | <--- FIN_ACK |
363
393
  */
364
394
 
365
- const getRemoteStream = pDefer<Stream>()
395
+ const getRemoteStream = Promise.withResolvers<Stream>()
366
396
  const protocol = '/close-local-while-remote-writes/1.0.0'
367
397
 
368
- const streamHandler: StreamHandler = ({ stream }) => {
369
- void Promise.resolve().then(async () => {
370
- getRemoteStream.resolve(stream)
371
- })
398
+ const streamHandler: StreamHandler = (stream) => {
399
+ getRemoteStream.resolve(stream)
372
400
  }
373
401
 
374
- await listener.handle(protocol, (info) => {
375
- streamHandler(info)
376
- }, {
402
+ await listener.handle(protocol, streamHandler, {
377
403
  runOnLimitedConnection: true
378
404
  })
379
405
 
380
406
  const connection = await dialer.dial(dialAddrs[0])
381
407
 
382
- // open a stream on the echo protocol
383
- const stream = await connection.newStream(protocol, {
384
- runOnLimitedConnection: true
385
- })
386
-
387
- // close the write end immediately
388
- const p = stream.closeWrite()
389
-
390
- const remoteStream = await getRemoteStream.promise
391
- // close the readable end of the remote stream
408
+ const [
409
+ localStream,
410
+ remoteStream
411
+ ] = await Promise.all([
412
+ // open a stream on the echo protocol
413
+ connection.newStream(protocol, {
414
+ runOnLimitedConnection: true
415
+ }),
416
+ getRemoteStream.promise
417
+ ])
418
+
419
+ // ignore incoming data
420
+ void drain(localStream)
421
+
422
+ // close the remote readable end
392
423
  await remoteStream.closeRead()
393
424
 
394
- // keep the remote write end open, this should delay the FIN_ACK reply to the local stream
425
+ // send data from the remote to the local
395
426
  const remoteInputStream = pushable<Uint8Array>()
396
- void remoteStream.sink(remoteInputStream)
397
-
398
- // wait for remote to receive local close-write
399
- await pRetry(() => {
400
- if (remoteStream.readStatus !== 'closed') {
401
- throw new Error('Remote stream read status ' + remoteStream.readStatus)
427
+ Promise.resolve().then(async () => {
428
+ for await (const buf of remoteInputStream) {
429
+ const sendMore = remoteStream.send(buf)
430
+
431
+ if (sendMore === false) {
432
+ await pEvent(remoteStream, 'drain', {
433
+ rejectionEvents: [
434
+ 'close'
435
+ ]
436
+ })
437
+ }
402
438
  }
403
- }, {
404
- minTimeout: 100
439
+
440
+ // close the remote writable end
441
+ await remoteStream.close()
405
442
  })
406
443
 
407
- // remote closes write
408
- remoteInputStream.end()
444
+ await Promise.all([
445
+ // wait for remote to receive local FIN
446
+ pEvent(remoteStream, 'remoteCloseWrite'),
409
447
 
410
- // wait to receive FIN_ACK
411
- await p
448
+ // wait to receive FIN_ACK
449
+ localStream.close()
450
+ ])
451
+
452
+ // stop sending remote -> local
453
+ remoteInputStream.end()
412
454
 
413
455
  // wait for remote to notice closure
414
456
  await pRetry(() => {
@@ -417,11 +459,12 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
417
459
  }
418
460
  })
419
461
 
420
- assertStreamClosed(stream)
462
+ // both ends should be closed
463
+ assertStreamClosed(localStream)
421
464
  assertStreamClosed(remoteStream)
422
465
  })
423
466
 
424
- it('can close local stream for writing and reading while a remote stream is writing using source/sink', async function () {
467
+ it('can close local stream for writing while a remote stream is reading', async function () {
425
468
  ({ dialer, listener, dialAddrs } = await getSetup(common))
426
469
 
427
470
  if (listener == null) {
@@ -430,62 +473,80 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
430
473
 
431
474
  /**
432
475
  * NodeA NodeB
476
+ * | DATA ---> |
433
477
  * | FIN ---> |
434
478
  * | <--- FIN |
435
479
  * | FIN_ACK ---> |
436
480
  * | <--- FIN_ACK |
437
481
  */
438
482
 
439
- const getRemoteStream = pDefer<Stream>()
483
+ const getRemoteStream = Promise.withResolvers<Stream>()
440
484
  const protocol = '/close-local-while-remote-reads/1.0.0'
441
485
 
442
- const streamHandler: StreamHandler = ({ stream }) => {
443
- void Promise.resolve().then(async () => {
444
- getRemoteStream.resolve(stream)
445
- })
486
+ const streamHandler: StreamHandler = (stream) => {
487
+ getRemoteStream.resolve(stream)
446
488
  }
447
489
 
448
- await listener.handle(protocol, (info) => {
449
- streamHandler(info)
450
- }, {
490
+ await listener.handle(protocol, streamHandler, {
451
491
  runOnLimitedConnection: true
452
492
  })
453
493
 
454
494
  const connection = await dialer.dial(dialAddrs[0])
455
495
 
456
- // open a stream on the echo protocol
457
- const stream = await connection.newStream(protocol, {
458
- runOnLimitedConnection: true
459
- })
460
-
461
- // keep the remote write end open, this should delay the FIN_ACK reply to the local stream
462
- const p = stream.sink([])
463
-
464
- const remoteStream = await getRemoteStream.promise
465
- // close the readable end of the remote stream
466
- await remoteStream.closeRead()
467
- // readable end should finish
468
- await drain(remoteStream.source)
469
-
470
- // wait for remote to receive local close-write
471
- await pRetry(() => {
472
- if (remoteStream.readStatus !== 'closed') {
473
- throw new Error('Remote stream read status ' + remoteStream.readStatus)
474
- }
475
- }, {
476
- minTimeout: 100
496
+ const [
497
+ localStream,
498
+ remoteStream
499
+ ] = await Promise.all([
500
+ // open a stream on the echo protocol
501
+ connection.newStream(protocol, {
502
+ runOnLimitedConnection: true
503
+ }),
504
+ getRemoteStream.promise
505
+ ])
506
+
507
+ // ignore incoming data
508
+ void drain(remoteStream)
509
+
510
+ // close the remote stream writable end when the local sends a FIN
511
+ remoteStream.addEventListener('remoteCloseWrite', () => {
512
+ remoteStream.close()
513
+ .catch(err => {
514
+ remoteStream.abort(err)
515
+ })
477
516
  })
478
517
 
479
- // remote closes write
480
- await remoteStream.sink([])
518
+ // send data to the remote then close the stream
519
+ const data = [
520
+ Uint8Array.from([0, 1, 2, 3]),
521
+ Uint8Array.from([4, 5, 6, 7]),
522
+ Uint8Array.from([8, 9, 0, 1])
523
+ ]
524
+
525
+ const [
526
+ remoteCloseEvent,
527
+ localCloseEvent
528
+ ] = await Promise.all([
529
+ // wait for the remote to close
530
+ pEvent(remoteStream, 'close'),
531
+ pEvent(localStream, 'close'),
532
+ (async () => {
533
+ for (const buf of data) {
534
+ if (!localStream.send(buf)) {
535
+ await pEvent(localStream, 'drain', {
536
+ rejectionEvents: [
537
+ 'close'
538
+ ]
539
+ })
540
+ }
541
+ }
481
542
 
482
- // wait to receive FIN_ACK
483
- await p
543
+ // close the local writable end
544
+ await localStream.close()
545
+ })()
546
+ ])
484
547
 
485
- // close read end of stream
486
- await stream.closeRead()
487
- // readable end should finish
488
- await drain(stream.source)
548
+ expect(remoteCloseEvent).to.not.have.property('error', 'remote stream did not close cleanly')
549
+ expect(localCloseEvent).to.not.have.property('error', 'local stream did not close cleanly')
489
550
 
490
551
  // wait for remote to notice closure
491
552
  await pRetry(() => {
@@ -494,70 +555,50 @@ export default (common: TestSetup<TransportTestFixtures>): void => {
494
555
  }
495
556
  })
496
557
 
497
- assertStreamClosed(stream)
558
+ // both ends should be closed
559
+ assertStreamClosed(localStream)
498
560
  assertStreamClosed(remoteStream)
499
561
  })
500
- })
501
-
502
- describe('events', () => {
503
- let dialer: Libp2p<{ echo: Echo }>
504
- let listener: Libp2p<{ echo: Echo }> | undefined
505
- let listenMultiaddrMatcher: MultiaddrMatcher
506
562
 
507
- afterEach(async () => {
508
- await stop(dialer, listener)
509
- await common.teardown()
510
- })
511
-
512
- it('emits listening', async function () {
513
- ({ dialer, listener, listenMultiaddrMatcher } = await getSetup(common))
563
+ it('can close a stream for writing but receive a large amount of data', async function () {
564
+ const timeout = 120_000
565
+ this.timeout(timeout);
566
+ ({ dialer, listener, dialAddrs } = await getSetup(common))
514
567
 
515
568
  if (listener == null) {
516
569
  return this.skip()
517
570
  }
518
571
 
519
- await listener.stop()
520
-
521
- const transportListeningPromise = pDefer()
522
-
523
- listener.addEventListener('transport:listening', (event) => {
524
- const transportListener = event.detail
572
+ const protocol = '/receive-data/1.0.0'
573
+ const chunkSize = 1024
574
+ const bytes = chunkSize * 1024 * 10
525
575
 
526
- if (transportListener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma))) {
527
- transportListeningPromise.resolve()
576
+ await listener.handle(protocol, async (stream) => {
577
+ for (let i = 0; i < bytes; i += chunkSize) {
578
+ const sendMore = stream.send(new Uint8Array(chunkSize))
579
+
580
+ if (!sendMore) {
581
+ await pEvent(stream, 'drain', {
582
+ rejectionEvents: [
583
+ 'close'
584
+ ]
585
+ })
586
+ }
528
587
  }
529
- })
530
588
 
531
- await listener.start()
532
-
533
- await raceSignal(transportListeningPromise.promise, AbortSignal.timeout(1000), {
534
- errorMessage: 'Did not emit listening event'
589
+ await stream.close()
535
590
  })
536
- })
537
-
538
- it('emits close', async function () {
539
- ({ dialer, listener } = await getSetup(common))
540
-
541
- if (listener == null) {
542
- return this.skip()
543
- }
544
591
 
545
- const transportManager = getTransportManager(listener)
546
- const transportListener = transportManager.getListeners()
547
- .filter(listener => listener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma)))
548
- .pop()
549
-
550
- if (transportListener == null) {
551
- throw new Error('Could not find address listener')
552
- }
592
+ const stream = await dialer.dialProtocol(dialAddrs[0], protocol)
553
593
 
554
- const p = pEvent(transportListener, 'close')
594
+ const [
595
+ output
596
+ ] = await Promise.all([
597
+ all(stream),
598
+ stream.close()
599
+ ])
555
600
 
556
- await listener.stop()
557
-
558
- await raceSignal(p, AbortSignal.timeout(1000), {
559
- errorMessage: 'Did not emit close event'
560
- })
601
+ expect(new Uint8ArrayList(...output).byteLength).to.equal(bytes)
561
602
  })
562
603
  })
563
604
  }
@@ -568,6 +609,4 @@ function assertStreamClosed (stream: Stream): void {
568
609
  expect(stream.writeStatus).to.equal('closed')
569
610
 
570
611
  expect(stream.timeline.close).to.be.a('number')
571
- expect(stream.timeline.closeRead).to.be.a('number')
572
- expect(stream.timeline.closeWrite).to.be.a('number')
573
612
  }