@libp2p/interface-compliance-tests 3.0.7-5315f7bc → 3.0.7-562f9b08

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 (50) hide show
  1. package/dist/src/connection/index.d.ts.map +1 -1
  2. package/dist/src/connection/index.js +24 -25
  3. package/dist/src/connection/index.js.map +1 -1
  4. package/dist/src/connection-encryption/utils/index.d.ts.map +1 -1
  5. package/dist/src/connection-encryption/utils/index.js +1 -0
  6. package/dist/src/connection-encryption/utils/index.js.map +1 -1
  7. package/dist/src/mocks/connection-manager.d.ts.map +1 -1
  8. package/dist/src/mocks/connection.d.ts.map +1 -1
  9. package/dist/src/mocks/connection.js +42 -39
  10. package/dist/src/mocks/connection.js.map +1 -1
  11. package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -1
  12. package/dist/src/mocks/multiaddr-connection.js +9 -0
  13. package/dist/src/mocks/multiaddr-connection.js.map +1 -1
  14. package/dist/src/mocks/muxer.d.ts +28 -0
  15. package/dist/src/mocks/muxer.d.ts.map +1 -1
  16. package/dist/src/mocks/muxer.js +85 -213
  17. package/dist/src/mocks/muxer.js.map +1 -1
  18. package/dist/src/stream-muxer/base-test.js +6 -6
  19. package/dist/src/stream-muxer/base-test.js.map +1 -1
  20. package/dist/src/stream-muxer/close-test.d.ts.map +1 -1
  21. package/dist/src/stream-muxer/close-test.js +100 -12
  22. package/dist/src/stream-muxer/close-test.js.map +1 -1
  23. package/dist/src/stream-muxer/fixtures/pb/message.d.ts +13 -0
  24. package/dist/src/stream-muxer/fixtures/pb/message.d.ts.map +1 -0
  25. package/dist/src/stream-muxer/fixtures/pb/message.js +67 -0
  26. package/dist/src/stream-muxer/fixtures/pb/message.js.map +1 -0
  27. package/dist/src/stream-muxer/spawner.d.ts.map +1 -1
  28. package/dist/src/stream-muxer/spawner.js +4 -3
  29. package/dist/src/stream-muxer/spawner.js.map +1 -1
  30. package/dist/src/stream-muxer/stress-test.js +1 -1
  31. package/dist/src/stream-muxer/stress-test.js.map +1 -1
  32. package/dist/src/transport/dial-test.js +1 -1
  33. package/dist/src/transport/dial-test.js.map +1 -1
  34. package/dist/src/transport/listen-test.js +4 -4
  35. package/dist/src/transport/listen-test.js.map +1 -1
  36. package/package.json +15 -11
  37. package/src/connection/index.ts +24 -26
  38. package/src/connection-encryption/utils/index.ts +1 -0
  39. package/src/mocks/connection-manager.ts +2 -2
  40. package/src/mocks/connection.ts +46 -40
  41. package/src/mocks/multiaddr-connection.ts +9 -0
  42. package/src/mocks/muxer.ts +105 -249
  43. package/src/stream-muxer/base-test.ts +6 -6
  44. package/src/stream-muxer/close-test.ts +110 -14
  45. package/src/stream-muxer/fixtures/pb/message.proto +7 -0
  46. package/src/stream-muxer/fixtures/pb/message.ts +87 -0
  47. package/src/stream-muxer/spawner.ts +3 -2
  48. package/src/stream-muxer/stress-test.ts +1 -1
  49. package/src/transport/dial-test.ts +1 -1
  50. package/src/transport/listen-test.ts +4 -4
@@ -1,7 +1,6 @@
1
- import { CodeError } from '@libp2p/interface/errors'
1
+ import { AbstractStream, type AbstractStreamInit } from '@libp2p/interface/stream-muxer/stream'
2
2
  import { type Logger, logger } from '@libp2p/logger'
3
3
  import { abortableSource } from 'abortable-iterator'
4
- import { anySignal } from 'any-signal'
5
4
  import map from 'it-map'
6
5
  import * as ndjson from 'it-ndjson'
7
6
  import { pipe } from 'it-pipe'
@@ -9,256 +8,94 @@ import { type Pushable, pushable } from 'it-pushable'
9
8
  import { Uint8ArrayList } from 'uint8arraylist'
10
9
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
11
10
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
12
- import type { Stream } from '@libp2p/interface/connection'
11
+ import type { AbortOptions } from '@libp2p/interface'
12
+ import type { Direction, Stream } from '@libp2p/interface/connection'
13
13
  import type { StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface/stream-muxer'
14
14
  import type { Source } from 'it-stream-types'
15
15
 
16
16
  let muxers = 0
17
17
  let streams = 0
18
- const MAX_MESSAGE_SIZE = 1024 * 1024
19
18
 
20
19
  interface DataMessage {
21
20
  id: string
22
21
  type: 'data'
23
- direction: 'initiator' | 'recipient'
22
+ direction: Direction
24
23
  chunk: string
25
24
  }
26
25
 
27
26
  interface ResetMessage {
28
27
  id: string
29
28
  type: 'reset'
30
- direction: 'initiator' | 'recipient'
29
+ direction: Direction
31
30
  }
32
31
 
33
32
  interface CloseMessage {
34
33
  id: string
35
34
  type: 'close'
36
- direction: 'initiator' | 'recipient'
35
+ direction: Direction
37
36
  }
38
37
 
39
38
  interface CreateMessage {
40
39
  id: string
41
40
  type: 'create'
42
- direction: 'initiator'
41
+ direction: 'outbound'
43
42
  }
44
43
 
45
44
  type StreamMessage = DataMessage | ResetMessage | CloseMessage | CreateMessage
46
45
 
47
- class MuxedStream {
48
- public id: string
49
- public input: Pushable<Uint8ArrayList>
50
- public stream: Stream
51
- public type: 'initiator' | 'recipient'
52
-
53
- private sinkEnded: boolean
54
- private sourceEnded: boolean
55
- private readonly abortController: AbortController
56
- private readonly resetController: AbortController
57
- private readonly closeController: AbortController
58
- private readonly log: Logger
59
-
60
- constructor (init: { id: string, type: 'initiator' | 'recipient', push: Pushable<StreamMessage>, onEnd: (err?: Error) => void }) {
61
- const { id, type, push, onEnd } = init
62
-
63
- this.log = logger(`libp2p:mock-muxer:stream:${id}:${type}`)
64
-
65
- this.id = id
66
- this.type = type
67
- this.abortController = new AbortController()
68
- this.resetController = new AbortController()
69
- this.closeController = new AbortController()
70
-
71
- this.sourceEnded = false
72
- this.sinkEnded = false
73
-
74
- let endErr: Error | undefined
75
-
76
- const onSourceEnd = (err?: Error): void => {
77
- if (this.sourceEnded) {
78
- return
79
- }
80
-
81
- this.log('onSourceEnd sink ended? %s', this.sinkEnded)
46
+ export interface MockMuxedStreamInit extends AbstractStreamInit {
47
+ push: Pushable<StreamMessage>
48
+ }
82
49
 
83
- this.sourceEnded = true
50
+ class MuxedStream extends AbstractStream {
51
+ private readonly push: Pushable<StreamMessage>
84
52
 
85
- if (err != null && endErr == null) {
86
- endErr = err
87
- }
53
+ constructor (init: MockMuxedStreamInit) {
54
+ super(init)
88
55
 
89
- if (this.sinkEnded) {
90
- this.stream.stat.timeline.close = Date.now()
56
+ this.push = init.push
57
+ }
91
58
 
92
- if (onEnd != null) {
93
- onEnd(endErr)
94
- }
95
- }
59
+ sendNewStream (): void {
60
+ // If initiator, open a new stream
61
+ const createMsg: CreateMessage = {
62
+ id: this.id,
63
+ type: 'create',
64
+ direction: 'outbound'
96
65
  }
66
+ this.push.push(createMsg)
67
+ }
97
68
 
98
- const onSinkEnd = (err?: Error): void => {
99
- if (this.sinkEnded) {
100
- return
101
- }
102
-
103
- this.log('onSinkEnd source ended? %s', this.sourceEnded)
104
-
105
- this.sinkEnded = true
106
-
107
- if (err != null && endErr == null) {
108
- endErr = err
109
- }
110
-
111
- if (this.sourceEnded) {
112
- this.stream.stat.timeline.close = Date.now()
113
-
114
- if (onEnd != null) {
115
- onEnd(endErr)
116
- }
117
- }
69
+ sendData (data: Uint8ArrayList): void {
70
+ const dataMsg: DataMessage = {
71
+ id: this.id,
72
+ type: 'data',
73
+ chunk: uint8ArrayToString(data.subarray(), 'base64pad'),
74
+ direction: this.direction
118
75
  }
76
+ this.push.push(dataMsg)
77
+ }
119
78
 
120
- this.input = pushable({
121
- onEnd: onSourceEnd
122
- })
123
-
124
- this.stream = {
125
- id,
126
- sink: async (source) => {
127
- if (this.sinkEnded) {
128
- throw new CodeError('stream closed for writing', 'ERR_SINK_ENDED')
129
- }
130
-
131
- const signal = anySignal([
132
- this.abortController.signal,
133
- this.resetController.signal,
134
- this.closeController.signal
135
- ])
136
-
137
- source = abortableSource(source, signal)
138
-
139
- try {
140
- if (this.type === 'initiator') {
141
- // If initiator, open a new stream
142
- const createMsg: CreateMessage = {
143
- id: this.id,
144
- type: 'create',
145
- direction: this.type
146
- }
147
- push.push(createMsg)
148
- }
149
-
150
- const list = new Uint8ArrayList()
151
-
152
- for await (const chunk of source) {
153
- list.append(chunk)
154
-
155
- while (list.length > 0) {
156
- const available = Math.min(list.length, MAX_MESSAGE_SIZE)
157
- const dataMsg: DataMessage = {
158
- id,
159
- type: 'data',
160
- chunk: uint8ArrayToString(list.subarray(0, available), 'base64pad'),
161
- direction: this.type
162
- }
163
-
164
- push.push(dataMsg)
165
- list.consume(available)
166
- }
167
- }
168
- } catch (err: any) {
169
- if (err.type === 'aborted' && err.message === 'The operation was aborted') {
170
- if (this.closeController.signal.aborted) {
171
- return
172
- }
173
-
174
- if (this.resetController.signal.aborted) {
175
- err.message = 'stream reset'
176
- err.code = 'ERR_STREAM_RESET'
177
- }
178
-
179
- if (this.abortController.signal.aborted) {
180
- err.message = 'stream aborted'
181
- err.code = 'ERR_STREAM_ABORT'
182
- }
183
- }
184
-
185
- // Send no more data if this stream was remotely reset
186
- if (err.code !== 'ERR_STREAM_RESET') {
187
- const resetMsg: ResetMessage = {
188
- id,
189
- type: 'reset',
190
- direction: this.type
191
- }
192
- push.push(resetMsg)
193
- }
194
-
195
- this.log('sink erred', err)
196
-
197
- this.input.end(err)
198
- onSinkEnd(err)
199
- return
200
- } finally {
201
- signal.clear()
202
- }
203
-
204
- this.log('sink ended')
205
-
206
- onSinkEnd()
207
-
208
- const closeMsg: CloseMessage = {
209
- id,
210
- type: 'close',
211
- direction: this.type
212
- }
213
- push.push(closeMsg)
214
- },
215
- source: this.input,
216
-
217
- // Close for reading
218
- close: () => {
219
- this.stream.closeRead()
220
- this.stream.closeWrite()
221
- },
222
-
223
- closeRead: () => {
224
- this.input.end()
225
- },
226
-
227
- closeWrite: () => {
228
- this.closeController.abort()
229
-
230
- const closeMsg: CloseMessage = {
231
- id,
232
- type: 'close',
233
- direction: this.type
234
- }
235
- push.push(closeMsg)
236
- onSinkEnd()
237
- },
238
-
239
- // Close for reading and writing (local error)
240
- abort: (err: Error) => {
241
- // End the source with the passed error
242
- this.input.end(err)
243
- this.abortController.abort()
244
- onSinkEnd(err)
245
- },
79
+ sendReset (): void {
80
+ const resetMsg: ResetMessage = {
81
+ id: this.id,
82
+ type: 'reset',
83
+ direction: this.direction
84
+ }
85
+ this.push.push(resetMsg)
86
+ }
246
87
 
247
- // Close immediately for reading and writing (remote error)
248
- reset: () => {
249
- const err = new CodeError('stream reset', 'ERR_STREAM_RESET')
250
- this.resetController.abort()
251
- this.input.end(err)
252
- onSinkEnd(err)
253
- },
254
- stat: {
255
- direction: type === 'initiator' ? 'outbound' : 'inbound',
256
- timeline: {
257
- open: Date.now()
258
- }
259
- },
260
- metadata: {}
88
+ sendCloseWrite (): void {
89
+ const closeMsg: CloseMessage = {
90
+ id: this.id,
91
+ type: 'close',
92
+ direction: this.direction
261
93
  }
94
+ this.push.push(closeMsg)
95
+ }
96
+
97
+ sendCloseRead (): void {
98
+ // does not support close read, only close write
262
99
  }
263
100
  }
264
101
 
@@ -286,8 +123,14 @@ class MockMuxer implements StreamMuxer {
286
123
  this.closeController = new AbortController()
287
124
  // receives data from the muxer at the other end of the stream
288
125
  this.source = this.input = pushable({
289
- onEnd: (err) => {
290
- this.close(err)
126
+ onEnd: () => {
127
+ for (const stream of this.registryInitiatorStreams.values()) {
128
+ stream.destroy()
129
+ }
130
+
131
+ for (const stream of this.registryRecipientStreams.values()) {
132
+ stream.destroy()
133
+ }
291
134
  }
292
135
  })
293
136
 
@@ -323,18 +166,18 @@ class MockMuxer implements StreamMuxer {
323
166
  handleMessage (message: StreamMessage): void {
324
167
  let muxedStream: MuxedStream | undefined
325
168
 
326
- const registry = message.direction === 'initiator' ? this.registryRecipientStreams : this.registryInitiatorStreams
169
+ const registry = message.direction === 'outbound' ? this.registryRecipientStreams : this.registryInitiatorStreams
327
170
 
328
171
  if (message.type === 'create') {
329
172
  if (registry.has(message.id)) {
330
173
  throw new Error(`Already had stream for ${message.id}`)
331
174
  }
332
175
 
333
- muxedStream = this.createStream(message.id, 'recipient')
334
- registry.set(muxedStream.stream.id, muxedStream)
176
+ muxedStream = this.createStream(message.id, 'inbound')
177
+ registry.set(muxedStream.id, muxedStream)
335
178
 
336
179
  if (this.options.onIncomingStream != null) {
337
- this.options.onIncomingStream(muxedStream.stream)
180
+ this.options.onIncomingStream(muxedStream)
338
181
  }
339
182
  }
340
183
 
@@ -347,20 +190,19 @@ class MockMuxer implements StreamMuxer {
347
190
  }
348
191
 
349
192
  if (message.type === 'data') {
350
- muxedStream.input.push(new Uint8ArrayList(uint8ArrayFromString(message.chunk, 'base64pad')))
193
+ muxedStream.sourcePush(new Uint8ArrayList(uint8ArrayFromString(message.chunk, 'base64pad')))
351
194
  } else if (message.type === 'reset') {
352
- this.log('-> reset stream %s %s', muxedStream.type, muxedStream.stream.id)
353
- muxedStream.stream.reset()
195
+ this.log('-> reset stream %s %s', muxedStream.direction, muxedStream.id)
196
+ muxedStream.reset()
354
197
  } else if (message.type === 'close') {
355
- this.log('-> closing stream %s %s', muxedStream.type, muxedStream.stream.id)
356
- muxedStream.stream.closeRead()
198
+ this.log('-> closing stream %s %s', muxedStream.direction, muxedStream.id)
199
+ muxedStream.remoteCloseWrite()
357
200
  }
358
201
  }
359
202
 
360
203
  get streams (): Stream[] {
361
204
  return Array.from(this.registryRecipientStreams.values())
362
205
  .concat(Array.from(this.registryInitiatorStreams.values()))
363
- .map(({ stream }) => stream)
364
206
  }
365
207
 
366
208
  newStream (name?: string): Stream {
@@ -368,53 +210,67 @@ class MockMuxer implements StreamMuxer {
368
210
  throw new Error('Muxer already closed')
369
211
  }
370
212
  this.log('newStream %s', name)
371
- const storedStream = this.createStream(name, 'initiator')
372
- this.registryInitiatorStreams.set(storedStream.stream.id, storedStream)
213
+ const storedStream = this.createStream(name, 'outbound')
214
+ this.registryInitiatorStreams.set(storedStream.id, storedStream)
373
215
 
374
- return storedStream.stream
216
+ return storedStream
375
217
  }
376
218
 
377
- createStream (name?: string, type: 'initiator' | 'recipient' = 'initiator'): MuxedStream {
378
- const id = name ?? `${this.name}:stream:${streams++}`
219
+ createStream (name?: string, direction: Direction = 'outbound'): MuxedStream {
220
+ const id = name ?? `${streams++}`
379
221
 
380
- this.log('createStream %s %s', type, id)
222
+ this.log('createStream %s %s', direction, id)
381
223
 
382
224
  const muxedStream: MuxedStream = new MuxedStream({
383
225
  id,
384
- type,
226
+ direction,
385
227
  push: this.streamInput,
386
228
  onEnd: () => {
387
- this.log('stream ended %s %s', type, id)
229
+ this.log('stream ended')
388
230
 
389
- if (type === 'initiator') {
390
- this.registryInitiatorStreams.delete(id)
231
+ if (direction === 'outbound') {
232
+ this.registryInitiatorStreams.delete(muxedStream.id)
391
233
  } else {
392
- this.registryRecipientStreams.delete(id)
234
+ this.registryRecipientStreams.delete(muxedStream.id)
393
235
  }
394
236
 
395
237
  if (this.options.onStreamEnd != null) {
396
- this.options.onStreamEnd(muxedStream.stream)
238
+ this.options.onStreamEnd(muxedStream)
397
239
  }
398
- }
240
+ },
241
+ log: logger(`libp2p:mock-muxer:stream:${direction}:${id}`)
399
242
  })
400
243
 
401
244
  return muxedStream
402
245
  }
403
246
 
404
- close (err?: Error): void {
405
- if (this.closeController.signal.aborted) return
247
+ async close (options?: AbortOptions): Promise<void> {
248
+ if (this.closeController.signal.aborted) {
249
+ return
250
+ }
251
+
406
252
  this.log('closing muxed streams')
407
253
 
408
- if (err == null) {
409
- this.streams.forEach(s => {
410
- s.close()
411
- })
412
- } else {
413
- this.streams.forEach(s => {
414
- s.abort(err)
415
- })
416
- }
254
+ await Promise.all(
255
+ this.streams.map(async s => s.close())
256
+ )
257
+
417
258
  this.closeController.abort()
259
+ this.input.end()
260
+ }
261
+
262
+ abort (err: Error): void {
263
+ if (this.closeController.signal.aborted) {
264
+ return
265
+ }
266
+
267
+ this.log('aborting muxed streams')
268
+
269
+ this.streams.forEach(s => {
270
+ s.abort(err)
271
+ })
272
+
273
+ this.closeController.abort(err)
418
274
  this.input.end(err)
419
275
  }
420
276
  }
@@ -44,12 +44,12 @@ export default (common: TestSetup<StreamMuxerFactory>): void => {
44
44
 
45
45
  const conn = await dialer.newStream()
46
46
  expect(dialer.streams).to.include(conn)
47
- expect(isValidTick(conn.stat.timeline.open)).to.equal(true)
47
+ expect(isValidTick(conn.timeline.open)).to.equal(true)
48
48
 
49
49
  void drainAndClose(conn)
50
50
 
51
51
  const stream = await onStreamPromise.promise
52
- expect(isValidTick(stream.stat.timeline.open)).to.equal(true)
52
+ expect(isValidTick(stream.timeline.open)).to.equal(true)
53
53
  // Make sure the stream is being tracked
54
54
  expect(listener.streams).to.include(stream)
55
55
 
@@ -59,12 +59,12 @@ export default (common: TestSetup<StreamMuxerFactory>): void => {
59
59
  const endedStream = await onStreamEndPromise.promise
60
60
  expect(listener.streams).to.not.include(endedStream)
61
61
 
62
- if (endedStream.stat.timeline.close == null) {
62
+ if (endedStream.timeline.close == null) {
63
63
  throw new Error('timeline had no close time')
64
64
  }
65
65
 
66
66
  // Make sure the stream is removed from tracking
67
- expect(isValidTick(endedStream.stat.timeline.close)).to.equal(true)
67
+ expect(isValidTick(endedStream.timeline.close)).to.equal(true)
68
68
 
69
69
  await drainAndClose(dialer)
70
70
  await drainAndClose(listener)
@@ -96,9 +96,9 @@ export default (common: TestSetup<StreamMuxerFactory>): void => {
96
96
  void drainAndClose(conn)
97
97
 
98
98
  const stream = await onStreamPromise.promise
99
- expect(isValidTick(stream.stat.timeline.open)).to.equal(true)
99
+ expect(isValidTick(stream.timeline.open)).to.equal(true)
100
100
  expect(listener.streams).to.include(conn)
101
- expect(isValidTick(conn.stat.timeline.open)).to.equal(true)
101
+ expect(isValidTick(conn.timeline.open)).to.equal(true)
102
102
  void drainAndClose(stream)
103
103
 
104
104
  await drainAndClose(dialer)