@libp2p/interface-compliance-tests 3.0.6 → 3.0.7-06f4901a
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.
- package/README.md +12 -3
- package/dist/src/connection/index.d.ts +5 -0
- package/dist/src/connection/index.d.ts.map +1 -0
- package/dist/src/connection/index.js +151 -0
- package/dist/src/connection/index.js.map +1 -0
- package/dist/src/connection-encryption/index.d.ts +5 -0
- package/dist/src/connection-encryption/index.d.ts.map +1 -0
- package/dist/src/connection-encryption/index.js +71 -0
- package/dist/src/connection-encryption/index.js.map +1 -0
- package/dist/src/connection-encryption/utils/index.d.ts +3 -0
- package/dist/src/connection-encryption/utils/index.d.ts.map +1 -0
- package/dist/src/connection-encryption/utils/index.js +18 -0
- package/dist/src/connection-encryption/utils/index.js.map +1 -0
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/mocks/connection-encrypter.d.ts +3 -0
- package/dist/src/mocks/connection-encrypter.d.ts.map +1 -0
- package/dist/src/mocks/connection-encrypter.js +98 -0
- package/dist/src/mocks/connection-encrypter.js.map +1 -0
- package/dist/src/mocks/connection-gater.d.ts +3 -0
- package/dist/src/mocks/connection-gater.d.ts.map +1 -0
- package/dist/src/mocks/connection-gater.js +17 -0
- package/dist/src/mocks/connection-gater.js.map +1 -0
- package/dist/src/mocks/connection-manager.d.ts +29 -0
- package/dist/src/mocks/connection-manager.d.ts.map +1 -0
- package/dist/src/mocks/connection-manager.js +145 -0
- package/dist/src/mocks/connection-manager.js.map +1 -0
- package/dist/src/mocks/connection.d.ts +32 -0
- package/dist/src/mocks/connection.d.ts.map +1 -0
- package/dist/src/mocks/connection.js +162 -0
- package/dist/src/mocks/connection.js.map +1 -0
- package/dist/src/mocks/duplex.d.ts +3 -0
- package/dist/src/mocks/duplex.d.ts.map +1 -0
- package/dist/src/mocks/duplex.js +9 -0
- package/dist/src/mocks/duplex.js.map +1 -0
- package/dist/src/mocks/index.d.ts +13 -0
- package/dist/src/mocks/index.d.ts.map +1 -0
- package/dist/src/mocks/index.js +11 -0
- package/dist/src/mocks/index.js.map +1 -0
- package/dist/src/mocks/metrics.d.ts +3 -0
- package/dist/src/mocks/metrics.d.ts.map +1 -0
- package/dist/src/mocks/metrics.js +115 -0
- package/dist/src/mocks/metrics.js.map +1 -0
- package/dist/src/mocks/multiaddr-connection.d.ts +17 -0
- package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -0
- package/dist/src/mocks/multiaddr-connection.js +51 -0
- package/dist/src/mocks/multiaddr-connection.js.map +1 -0
- package/dist/src/mocks/muxer.d.ts +8 -0
- package/dist/src/mocks/muxer.d.ts.map +1 -0
- package/dist/src/mocks/muxer.js +341 -0
- package/dist/src/mocks/muxer.js.map +1 -0
- package/dist/src/mocks/peer-discovery.d.ts +22 -0
- package/dist/src/mocks/peer-discovery.d.ts.map +1 -0
- package/dist/src/mocks/peer-discovery.js +47 -0
- package/dist/src/mocks/peer-discovery.js.map +1 -0
- package/dist/src/mocks/registrar.d.ts +18 -0
- package/dist/src/mocks/registrar.d.ts.map +1 -0
- package/dist/src/mocks/registrar.js +66 -0
- package/dist/src/mocks/registrar.js.map +1 -0
- package/dist/src/mocks/upgrader.d.ts +10 -0
- package/dist/src/mocks/upgrader.d.ts.map +1 -0
- package/dist/src/mocks/upgrader.js +31 -0
- package/dist/src/mocks/upgrader.js.map +1 -0
- package/dist/src/peer-discovery/index.d.ts +5 -0
- package/dist/src/peer-discovery/index.d.ts.map +1 -0
- package/dist/src/peer-discovery/index.js +66 -0
- package/dist/src/peer-discovery/index.js.map +1 -0
- package/dist/src/pubsub/api.d.ts +6 -0
- package/dist/src/pubsub/api.d.ts.map +1 -0
- package/dist/src/pubsub/api.js +87 -0
- package/dist/src/pubsub/api.js.map +1 -0
- package/dist/src/pubsub/connection-handlers.d.ts +6 -0
- package/dist/src/pubsub/connection-handlers.d.ts.map +1 -0
- package/dist/src/pubsub/connection-handlers.js +329 -0
- package/dist/src/pubsub/connection-handlers.js.map +1 -0
- package/dist/src/pubsub/emit-self.d.ts +6 -0
- package/dist/src/pubsub/emit-self.d.ts.map +1 -0
- package/dist/src/pubsub/emit-self.js +80 -0
- package/dist/src/pubsub/emit-self.js.map +1 -0
- package/dist/src/pubsub/index.d.ts +18 -0
- package/dist/src/pubsub/index.d.ts.map +1 -0
- package/dist/src/pubsub/index.js +17 -0
- package/dist/src/pubsub/index.js.map +1 -0
- package/dist/src/pubsub/messages.d.ts +6 -0
- package/dist/src/pubsub/messages.d.ts.map +1 -0
- package/dist/src/pubsub/messages.js +48 -0
- package/dist/src/pubsub/messages.js.map +1 -0
- package/dist/src/pubsub/multiple-nodes.d.ts +6 -0
- package/dist/src/pubsub/multiple-nodes.d.ts.map +1 -0
- package/dist/src/pubsub/multiple-nodes.js +350 -0
- package/dist/src/pubsub/multiple-nodes.js.map +1 -0
- package/dist/src/pubsub/two-nodes.d.ts +6 -0
- package/dist/src/pubsub/two-nodes.d.ts.map +1 -0
- package/dist/src/pubsub/two-nodes.js +217 -0
- package/dist/src/pubsub/two-nodes.js.map +1 -0
- package/dist/src/pubsub/utils.d.ts +6 -0
- package/dist/src/pubsub/utils.d.ts.map +1 -0
- package/dist/src/pubsub/utils.js +22 -0
- package/dist/src/pubsub/utils.js.map +1 -0
- package/dist/src/stream-muxer/base-test.d.ts +5 -0
- package/dist/src/stream-muxer/base-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/base-test.js +153 -0
- package/dist/src/stream-muxer/base-test.js.map +1 -0
- package/dist/src/stream-muxer/close-test.d.ts +5 -0
- package/dist/src/stream-muxer/close-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/close-test.js +269 -0
- package/dist/src/stream-muxer/close-test.js.map +1 -0
- package/dist/src/stream-muxer/index.d.ts +5 -0
- package/dist/src/stream-muxer/index.d.ts.map +1 -0
- package/dist/src/stream-muxer/index.js +13 -0
- package/dist/src/stream-muxer/index.js.map +1 -0
- package/dist/src/stream-muxer/mega-stress-test.d.ts +5 -0
- package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/mega-stress-test.js +11 -0
- package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
- package/dist/src/stream-muxer/spawner.d.ts +4 -0
- package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
- package/dist/src/stream-muxer/spawner.js +36 -0
- package/dist/src/stream-muxer/spawner.js.map +1 -0
- package/dist/src/stream-muxer/stress-test.d.ts +5 -0
- package/dist/src/stream-muxer/stress-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/stress-test.js +23 -0
- package/dist/src/stream-muxer/stress-test.js.map +1 -0
- package/dist/src/transport/dial-test.d.ts +5 -0
- package/dist/src/transport/dial-test.d.ts.map +1 -0
- package/dist/src/transport/dial-test.js +98 -0
- package/dist/src/transport/dial-test.js.map +1 -0
- package/dist/src/transport/filter-test.d.ts +5 -0
- package/dist/src/transport/filter-test.d.ts.map +1 -0
- package/dist/src/transport/filter-test.js +18 -0
- package/dist/src/transport/filter-test.js.map +1 -0
- package/dist/src/transport/index.d.ts +15 -0
- package/dist/src/transport/index.d.ts.map +1 -0
- package/dist/src/transport/index.js +11 -0
- package/dist/src/transport/index.js.map +1 -0
- package/dist/src/transport/listen-test.d.ts +5 -0
- package/dist/src/transport/listen-test.d.ts.map +1 -0
- package/dist/src/transport/listen-test.js +152 -0
- package/dist/src/transport/listen-test.js.map +1 -0
- package/package.json +71 -95
- package/src/connection/index.ts +184 -0
- package/src/connection-encryption/index.ts +97 -0
- package/src/connection-encryption/utils/index.ts +23 -0
- package/src/index.ts +1 -1
- package/src/mocks/connection-encrypter.ts +113 -0
- package/src/mocks/connection-gater.ts +18 -0
- package/src/mocks/connection-manager.ts +211 -0
- package/src/mocks/connection.ts +218 -0
- package/src/mocks/duplex.ts +10 -0
- package/src/mocks/index.ts +12 -0
- package/src/mocks/metrics.ts +162 -0
- package/src/mocks/multiaddr-connection.ts +67 -0
- package/src/mocks/muxer.ts +447 -0
- package/src/mocks/peer-discovery.ts +60 -0
- package/src/mocks/registrar.ts +88 -0
- package/src/mocks/upgrader.ts +49 -0
- package/src/peer-discovery/index.ts +90 -0
- package/src/pubsub/api.ts +114 -0
- package/src/pubsub/connection-handlers.ts +413 -0
- package/src/pubsub/emit-self.ts +99 -0
- package/src/pubsub/index.ts +34 -0
- package/src/pubsub/messages.ts +59 -0
- package/src/pubsub/multiple-nodes.ts +440 -0
- package/src/pubsub/two-nodes.ts +273 -0
- package/src/pubsub/utils.ts +29 -0
- package/src/stream-muxer/base-test.ts +196 -0
- package/src/stream-muxer/close-test.ts +346 -0
- package/src/stream-muxer/index.ts +15 -0
- package/src/stream-muxer/mega-stress-test.ts +14 -0
- package/src/stream-muxer/spawner.ts +54 -0
- package/src/stream-muxer/stress-test.ts +27 -0
- package/src/transport/dial-test.ts +124 -0
- package/src/transport/filter-test.ts +25 -0
- package/src/transport/index.ts +25 -0
- package/src/transport/listen-test.ts +191 -0
- package/dist/typedoc-urls.json +0 -3
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { MultiaddrConnection, Stream, Connection } from '@libp2p/interface/connection'
|
|
2
|
+
import type { Metric, MetricGroup, StopTimer, Metrics, CalculatedMetricOptions, MetricOptions } from '@libp2p/interface/metrics'
|
|
3
|
+
|
|
4
|
+
class DefaultMetric implements Metric {
|
|
5
|
+
public value: number = 0
|
|
6
|
+
|
|
7
|
+
update (value: number): void {
|
|
8
|
+
this.value = value
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
increment (value: number = 1): void {
|
|
12
|
+
this.value += value
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
decrement (value: number = 1): void {
|
|
16
|
+
this.value -= value
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
reset (): void {
|
|
20
|
+
this.value = 0
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
timer (): StopTimer {
|
|
24
|
+
const start = Date.now()
|
|
25
|
+
|
|
26
|
+
return () => {
|
|
27
|
+
this.value = Date.now() - start
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
class DefaultGroupMetric implements MetricGroup {
|
|
33
|
+
public values: Record<string, number> = {}
|
|
34
|
+
|
|
35
|
+
update (values: Record<string, number>): void {
|
|
36
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
37
|
+
this.values[key] = value
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
increment (values: Record<string, number | unknown>): void {
|
|
42
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
43
|
+
this.values[key] = this.values[key] ?? 0
|
|
44
|
+
const inc = typeof value === 'number' ? value : 1
|
|
45
|
+
|
|
46
|
+
this.values[key] += Number(inc)
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
decrement (values: Record<string, number | unknown>): void {
|
|
51
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
52
|
+
this.values[key] = this.values[key] ?? 0
|
|
53
|
+
const dec = typeof value === 'number' ? value : 1
|
|
54
|
+
|
|
55
|
+
this.values[key] -= Number(dec)
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
reset (): void {
|
|
60
|
+
this.values = {}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
timer (key: string): StopTimer {
|
|
64
|
+
const start = Date.now()
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
this.values[key] = Date.now() - start
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
class MockMetrics implements Metrics {
|
|
73
|
+
public metrics = new Map<string, any>()
|
|
74
|
+
|
|
75
|
+
trackMultiaddrConnection (maConn: MultiaddrConnection): void {
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
trackProtocolStream (stream: Stream, connection: Connection): void {
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
registerMetric (name: string, opts: CalculatedMetricOptions): void
|
|
84
|
+
registerMetric (name: string, opts?: MetricOptions): Metric
|
|
85
|
+
registerMetric (name: string, opts: any): any {
|
|
86
|
+
if (name == null ?? name.trim() === '') {
|
|
87
|
+
throw new Error('Metric name is required')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (opts?.calculate != null) {
|
|
91
|
+
// calculated metric
|
|
92
|
+
this.metrics.set(name, opts.calculate)
|
|
93
|
+
return
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const metric = new DefaultMetric()
|
|
97
|
+
this.metrics.set(name, metric)
|
|
98
|
+
|
|
99
|
+
return metric
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
registerCounter (name: string, opts: CalculatedMetricOptions): void
|
|
103
|
+
registerCounter (name: string, opts?: MetricOptions): Metric
|
|
104
|
+
registerCounter (name: string, opts: any): any {
|
|
105
|
+
if (name == null ?? name.trim() === '') {
|
|
106
|
+
throw new Error('Metric name is required')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (opts?.calculate != null) {
|
|
110
|
+
// calculated metric
|
|
111
|
+
this.metrics.set(name, opts.calculate)
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const metric = new DefaultMetric()
|
|
116
|
+
this.metrics.set(name, metric)
|
|
117
|
+
|
|
118
|
+
return metric
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
registerMetricGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
|
|
122
|
+
registerMetricGroup (name: string, opts?: MetricOptions): MetricGroup
|
|
123
|
+
registerMetricGroup (name: string, opts: any): any {
|
|
124
|
+
if (name == null ?? name.trim() === '') {
|
|
125
|
+
throw new Error('Metric name is required')
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (opts?.calculate != null) {
|
|
129
|
+
// calculated metric
|
|
130
|
+
this.metrics.set(name, opts.calculate)
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const metric = new DefaultGroupMetric()
|
|
135
|
+
this.metrics.set(name, metric)
|
|
136
|
+
|
|
137
|
+
return metric
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
registerCounterGroup (name: string, opts: CalculatedMetricOptions<Record<string, number>>): void
|
|
141
|
+
registerCounterGroup (name: string, opts?: MetricOptions): MetricGroup
|
|
142
|
+
registerCounterGroup (name: string, opts: any): any {
|
|
143
|
+
if (name == null ?? name.trim() === '') {
|
|
144
|
+
throw new Error('Metric name is required')
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (opts?.calculate != null) {
|
|
148
|
+
// calculated metric
|
|
149
|
+
this.metrics.set(name, opts.calculate)
|
|
150
|
+
return
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const metric = new DefaultGroupMetric()
|
|
154
|
+
this.metrics.set(name, metric)
|
|
155
|
+
|
|
156
|
+
return metric
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function mockMetrics (): () => Metrics {
|
|
161
|
+
return () => new MockMetrics()
|
|
162
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { multiaddr } from '@multiformats/multiaddr'
|
|
2
|
+
import { abortableSource } from 'abortable-iterator'
|
|
3
|
+
import { duplexPair } from 'it-pair/duplex'
|
|
4
|
+
import type { MultiaddrConnection } from '@libp2p/interface/connection'
|
|
5
|
+
import type { PeerId } from '@libp2p/interface/peer-id'
|
|
6
|
+
import type { Multiaddr } from '@multiformats/multiaddr'
|
|
7
|
+
import type { Duplex } from 'it-stream-types'
|
|
8
|
+
|
|
9
|
+
export function mockMultiaddrConnection (source: Duplex<AsyncGenerator<Uint8Array>> & Partial<MultiaddrConnection>, peerId: PeerId): MultiaddrConnection {
|
|
10
|
+
const maConn: MultiaddrConnection = {
|
|
11
|
+
async close () {
|
|
12
|
+
|
|
13
|
+
},
|
|
14
|
+
timeline: {
|
|
15
|
+
open: Date.now()
|
|
16
|
+
},
|
|
17
|
+
remoteAddr: multiaddr(`/ip4/127.0.0.1/tcp/4001/p2p/${peerId.toString()}`),
|
|
18
|
+
...source
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return maConn
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface MockMultiaddrConnPairOptions {
|
|
25
|
+
addrs: Multiaddr[]
|
|
26
|
+
remotePeer: PeerId
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns both sides of a mocked MultiaddrConnection
|
|
31
|
+
*/
|
|
32
|
+
export function mockMultiaddrConnPair (opts: MockMultiaddrConnPairOptions): { inbound: MultiaddrConnection, outbound: MultiaddrConnection } {
|
|
33
|
+
const { addrs, remotePeer } = opts
|
|
34
|
+
const controller = new AbortController()
|
|
35
|
+
const [localAddr, remoteAddr] = addrs
|
|
36
|
+
const [inboundStream, outboundStream] = duplexPair<Uint8Array>()
|
|
37
|
+
|
|
38
|
+
const outbound: MultiaddrConnection = {
|
|
39
|
+
...outboundStream,
|
|
40
|
+
remoteAddr: remoteAddr.toString().includes(`/p2p/${remotePeer.toString()}`) ? remoteAddr : remoteAddr.encapsulate(`/p2p/${remotePeer.toString()}`),
|
|
41
|
+
timeline: {
|
|
42
|
+
open: Date.now()
|
|
43
|
+
},
|
|
44
|
+
close: async () => {
|
|
45
|
+
outbound.timeline.close = Date.now()
|
|
46
|
+
controller.abort()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const inbound: MultiaddrConnection = {
|
|
51
|
+
...inboundStream,
|
|
52
|
+
remoteAddr: localAddr,
|
|
53
|
+
timeline: {
|
|
54
|
+
open: Date.now()
|
|
55
|
+
},
|
|
56
|
+
close: async () => {
|
|
57
|
+
inbound.timeline.close = Date.now()
|
|
58
|
+
controller.abort()
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Make the sources abortable so we can close them easily
|
|
63
|
+
inbound.source = abortableSource(inbound.source, controller.signal)
|
|
64
|
+
outbound.source = abortableSource(outbound.source, controller.signal)
|
|
65
|
+
|
|
66
|
+
return { inbound, outbound }
|
|
67
|
+
}
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
import { CodeError } from '@libp2p/interface/errors'
|
|
2
|
+
import { type Logger, logger } from '@libp2p/logger'
|
|
3
|
+
import { abortableSource } from 'abortable-iterator'
|
|
4
|
+
import { anySignal } from 'any-signal'
|
|
5
|
+
import map from 'it-map'
|
|
6
|
+
import * as ndjson from 'it-ndjson'
|
|
7
|
+
import { pipe } from 'it-pipe'
|
|
8
|
+
import { type Pushable, pushable } from 'it-pushable'
|
|
9
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
10
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
11
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
12
|
+
import type { Stream } from '@libp2p/interface/connection'
|
|
13
|
+
import type { StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface/stream-muxer'
|
|
14
|
+
import type { Source } from 'it-stream-types'
|
|
15
|
+
|
|
16
|
+
let muxers = 0
|
|
17
|
+
let streams = 0
|
|
18
|
+
const MAX_MESSAGE_SIZE = 1024 * 1024
|
|
19
|
+
|
|
20
|
+
interface DataMessage {
|
|
21
|
+
id: string
|
|
22
|
+
type: 'data'
|
|
23
|
+
direction: 'initiator' | 'recipient'
|
|
24
|
+
chunk: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ResetMessage {
|
|
28
|
+
id: string
|
|
29
|
+
type: 'reset'
|
|
30
|
+
direction: 'initiator' | 'recipient'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface CloseMessage {
|
|
34
|
+
id: string
|
|
35
|
+
type: 'close'
|
|
36
|
+
direction: 'initiator' | 'recipient'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface CreateMessage {
|
|
40
|
+
id: string
|
|
41
|
+
type: 'create'
|
|
42
|
+
direction: 'initiator'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type StreamMessage = DataMessage | ResetMessage | CloseMessage | CreateMessage
|
|
46
|
+
|
|
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)
|
|
82
|
+
|
|
83
|
+
this.sourceEnded = true
|
|
84
|
+
|
|
85
|
+
if (err != null && endErr == null) {
|
|
86
|
+
endErr = err
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (this.sinkEnded) {
|
|
90
|
+
this.stream.stat.timeline.close = Date.now()
|
|
91
|
+
|
|
92
|
+
if (onEnd != null) {
|
|
93
|
+
onEnd(endErr)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
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
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
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
|
+
},
|
|
246
|
+
|
|
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: {}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
class MockMuxer implements StreamMuxer {
|
|
266
|
+
public source: AsyncGenerator<Uint8Array>
|
|
267
|
+
public input: Pushable<Uint8Array>
|
|
268
|
+
public streamInput: Pushable<StreamMessage>
|
|
269
|
+
public name: string
|
|
270
|
+
public protocol: string = '/mock-muxer/1.0.0'
|
|
271
|
+
|
|
272
|
+
private readonly closeController: AbortController
|
|
273
|
+
private readonly registryInitiatorStreams: Map<string, MuxedStream>
|
|
274
|
+
private readonly registryRecipientStreams: Map<string, MuxedStream>
|
|
275
|
+
private readonly options: StreamMuxerInit
|
|
276
|
+
|
|
277
|
+
private readonly log: Logger
|
|
278
|
+
|
|
279
|
+
constructor (init?: StreamMuxerInit) {
|
|
280
|
+
this.name = `muxer:${muxers++}`
|
|
281
|
+
this.log = logger(`libp2p:mock-muxer:${this.name}`)
|
|
282
|
+
this.registryInitiatorStreams = new Map()
|
|
283
|
+
this.registryRecipientStreams = new Map()
|
|
284
|
+
this.log('create muxer')
|
|
285
|
+
this.options = init ?? { direction: 'inbound' }
|
|
286
|
+
this.closeController = new AbortController()
|
|
287
|
+
// receives data from the muxer at the other end of the stream
|
|
288
|
+
this.source = this.input = pushable({
|
|
289
|
+
onEnd: (err) => {
|
|
290
|
+
this.close(err)
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
// receives messages from all of the muxed streams
|
|
295
|
+
this.streamInput = pushable<StreamMessage>({
|
|
296
|
+
objectMode: true
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// receive incoming messages
|
|
301
|
+
async sink (source: Source<Uint8ArrayList | Uint8Array>): Promise<void> {
|
|
302
|
+
try {
|
|
303
|
+
await pipe(
|
|
304
|
+
abortableSource(source, this.closeController.signal),
|
|
305
|
+
(source) => map(source, buf => uint8ArrayToString(buf.subarray())),
|
|
306
|
+
ndjson.parse<StreamMessage>,
|
|
307
|
+
async (source) => {
|
|
308
|
+
for await (const message of source) {
|
|
309
|
+
this.log.trace('-> %s %s %s', message.type, message.direction, message.id)
|
|
310
|
+
this.handleMessage(message)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
this.log('muxed stream ended')
|
|
316
|
+
this.input.end()
|
|
317
|
+
} catch (err: any) {
|
|
318
|
+
this.log('muxed stream errored', err)
|
|
319
|
+
this.input.end(err)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
handleMessage (message: StreamMessage): void {
|
|
324
|
+
let muxedStream: MuxedStream | undefined
|
|
325
|
+
|
|
326
|
+
const registry = message.direction === 'initiator' ? this.registryRecipientStreams : this.registryInitiatorStreams
|
|
327
|
+
|
|
328
|
+
if (message.type === 'create') {
|
|
329
|
+
if (registry.has(message.id)) {
|
|
330
|
+
throw new Error(`Already had stream for ${message.id}`)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
muxedStream = this.createStream(message.id, 'recipient')
|
|
334
|
+
registry.set(muxedStream.stream.id, muxedStream)
|
|
335
|
+
|
|
336
|
+
if (this.options.onIncomingStream != null) {
|
|
337
|
+
this.options.onIncomingStream(muxedStream.stream)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
muxedStream = registry.get(message.id)
|
|
342
|
+
|
|
343
|
+
if (muxedStream == null) {
|
|
344
|
+
this.log.error(`No stream found for ${message.id}`)
|
|
345
|
+
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (message.type === 'data') {
|
|
350
|
+
muxedStream.input.push(new Uint8ArrayList(uint8ArrayFromString(message.chunk, 'base64pad')))
|
|
351
|
+
} else if (message.type === 'reset') {
|
|
352
|
+
this.log('-> reset stream %s %s', muxedStream.type, muxedStream.stream.id)
|
|
353
|
+
muxedStream.stream.reset()
|
|
354
|
+
} else if (message.type === 'close') {
|
|
355
|
+
this.log('-> closing stream %s %s', muxedStream.type, muxedStream.stream.id)
|
|
356
|
+
muxedStream.stream.closeRead()
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
get streams (): Stream[] {
|
|
361
|
+
return Array.from(this.registryRecipientStreams.values())
|
|
362
|
+
.concat(Array.from(this.registryInitiatorStreams.values()))
|
|
363
|
+
.map(({ stream }) => stream)
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
newStream (name?: string): Stream {
|
|
367
|
+
if (this.closeController.signal.aborted) {
|
|
368
|
+
throw new Error('Muxer already closed')
|
|
369
|
+
}
|
|
370
|
+
this.log('newStream %s', name)
|
|
371
|
+
const storedStream = this.createStream(name, 'initiator')
|
|
372
|
+
this.registryInitiatorStreams.set(storedStream.stream.id, storedStream)
|
|
373
|
+
|
|
374
|
+
return storedStream.stream
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
createStream (name?: string, type: 'initiator' | 'recipient' = 'initiator'): MuxedStream {
|
|
378
|
+
const id = name ?? `${this.name}:stream:${streams++}`
|
|
379
|
+
|
|
380
|
+
this.log('createStream %s %s', type, id)
|
|
381
|
+
|
|
382
|
+
const muxedStream: MuxedStream = new MuxedStream({
|
|
383
|
+
id,
|
|
384
|
+
type,
|
|
385
|
+
push: this.streamInput,
|
|
386
|
+
onEnd: () => {
|
|
387
|
+
this.log('stream ended %s %s', type, id)
|
|
388
|
+
|
|
389
|
+
if (type === 'initiator') {
|
|
390
|
+
this.registryInitiatorStreams.delete(id)
|
|
391
|
+
} else {
|
|
392
|
+
this.registryRecipientStreams.delete(id)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (this.options.onStreamEnd != null) {
|
|
396
|
+
this.options.onStreamEnd(muxedStream.stream)
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
})
|
|
400
|
+
|
|
401
|
+
return muxedStream
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
close (err?: Error): void {
|
|
405
|
+
if (this.closeController.signal.aborted) return
|
|
406
|
+
this.log('closing muxed streams')
|
|
407
|
+
|
|
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
|
+
}
|
|
417
|
+
this.closeController.abort()
|
|
418
|
+
this.input.end(err)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
class MockMuxerFactory implements StreamMuxerFactory {
|
|
423
|
+
public protocol: string = '/mock-muxer/1.0.0'
|
|
424
|
+
|
|
425
|
+
createStreamMuxer (init?: StreamMuxerInit): StreamMuxer {
|
|
426
|
+
const mockMuxer = new MockMuxer(init)
|
|
427
|
+
|
|
428
|
+
void Promise.resolve().then(async () => {
|
|
429
|
+
void pipe(
|
|
430
|
+
mockMuxer.streamInput,
|
|
431
|
+
ndjson.stringify,
|
|
432
|
+
(source) => map(source, str => new Uint8ArrayList(uint8ArrayFromString(str))),
|
|
433
|
+
async (source) => {
|
|
434
|
+
for await (const buf of source) {
|
|
435
|
+
mockMuxer.input.push(buf.subarray())
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
)
|
|
439
|
+
})
|
|
440
|
+
|
|
441
|
+
return mockMuxer
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export function mockMuxer (): MockMuxerFactory {
|
|
446
|
+
return new MockMuxerFactory()
|
|
447
|
+
}
|