@libp2p/interface-compliance-tests 6.4.16 → 6.5.0-6059227cb

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