@libp2p/interface-compliance-tests 6.1.8-32ca76fcb → 6.1.8-432955390

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