@libp2p/interface-compliance-tests 6.5.0 → 7.0.0-049bfa0fa
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/dist/src/connection-encryption/index.d.ts.map +1 -1
- package/dist/src/connection-encryption/index.js +24 -15
- package/dist/src/connection-encryption/index.js.map +1 -1
- package/dist/src/stream-muxer/base-test.d.ts.map +1 -1
- package/dist/src/stream-muxer/base-test.js +62 -345
- package/dist/src/stream-muxer/base-test.js.map +1 -1
- package/dist/src/stream-muxer/close-test.d.ts.map +1 -1
- package/dist/src/stream-muxer/close-test.js +254 -320
- package/dist/src/stream-muxer/close-test.js.map +1 -1
- package/dist/src/stream-muxer/index.js +2 -2
- package/dist/src/stream-muxer/index.js.map +1 -1
- package/dist/src/stream-muxer/{mega-stress-test.d.ts → stream-test.d.ts} +2 -2
- package/dist/src/stream-muxer/stream-test.d.ts.map +1 -0
- package/dist/src/stream-muxer/stream-test.js +290 -0
- package/dist/src/stream-muxer/stream-test.js.map +1 -0
- package/dist/src/stream-muxer/stress-test.d.ts.map +1 -1
- package/dist/src/stream-muxer/stress-test.js +70 -16
- package/dist/src/stream-muxer/stress-test.js.map +1 -1
- package/dist/src/transport/index.d.ts.map +1 -1
- package/dist/src/transport/index.js +235 -205
- package/dist/src/transport/index.js.map +1 -1
- package/dist/src/transport/utils.d.ts.map +1 -1
- package/dist/src/transport/utils.js +3 -2
- package/dist/src/transport/utils.js.map +1 -1
- package/package.json +23 -51
- package/src/connection-encryption/index.ts +27 -20
- package/src/stream-muxer/base-test.ts +75 -413
- package/src/stream-muxer/close-test.ts +305 -343
- package/src/stream-muxer/index.ts +2 -2
- package/src/stream-muxer/stream-test.ts +381 -0
- package/src/stream-muxer/stress-test.ts +92 -18
- package/src/transport/index.ts +281 -241
- package/src/transport/utils.ts +3 -2
- package/dist/src/connection-encryption/utils/index.d.ts +0 -3
- package/dist/src/connection-encryption/utils/index.d.ts.map +0 -1
- package/dist/src/connection-encryption/utils/index.js +0 -21
- package/dist/src/connection-encryption/utils/index.js.map +0 -1
- package/dist/src/matchers.d.ts +0 -12
- package/dist/src/matchers.d.ts.map +0 -1
- package/dist/src/matchers.js +0 -14
- package/dist/src/matchers.js.map +0 -1
- package/dist/src/mocks/connection-manager.d.ts +0 -27
- package/dist/src/mocks/connection-manager.d.ts.map +0 -1
- package/dist/src/mocks/connection-manager.js +0 -147
- package/dist/src/mocks/connection-manager.js.map +0 -1
- package/dist/src/mocks/connection.d.ts +0 -41
- package/dist/src/mocks/connection.d.ts.map +0 -1
- package/dist/src/mocks/connection.js +0 -236
- package/dist/src/mocks/connection.js.map +0 -1
- package/dist/src/mocks/duplex.d.ts +0 -4
- package/dist/src/mocks/duplex.d.ts.map +0 -1
- package/dist/src/mocks/duplex.js +0 -9
- package/dist/src/mocks/duplex.js.map +0 -1
- package/dist/src/mocks/index.d.ts +0 -12
- package/dist/src/mocks/index.d.ts.map +0 -1
- package/dist/src/mocks/index.js +0 -8
- package/dist/src/mocks/index.js.map +0 -1
- package/dist/src/mocks/multiaddr-connection.d.ts +0 -17
- package/dist/src/mocks/multiaddr-connection.d.ts.map +0 -1
- package/dist/src/mocks/multiaddr-connection.js +0 -64
- package/dist/src/mocks/multiaddr-connection.js.map +0 -1
- package/dist/src/mocks/muxer.d.ts +0 -36
- package/dist/src/mocks/muxer.d.ts.map +0 -1
- package/dist/src/mocks/muxer.js +0 -234
- package/dist/src/mocks/muxer.js.map +0 -1
- package/dist/src/mocks/registrar.d.ts +0 -16
- package/dist/src/mocks/registrar.d.ts.map +0 -1
- package/dist/src/mocks/registrar.js +0 -66
- package/dist/src/mocks/registrar.js.map +0 -1
- package/dist/src/mocks/upgrader.d.ts +0 -9
- package/dist/src/mocks/upgrader.d.ts.map +0 -1
- package/dist/src/mocks/upgrader.js +0 -46
- package/dist/src/mocks/upgrader.js.map +0 -1
- package/dist/src/pubsub/api.d.ts +0 -6
- package/dist/src/pubsub/api.d.ts.map +0 -1
- package/dist/src/pubsub/api.js +0 -88
- package/dist/src/pubsub/api.js.map +0 -1
- package/dist/src/pubsub/connection-handlers.d.ts +0 -6
- package/dist/src/pubsub/connection-handlers.d.ts.map +0 -1
- package/dist/src/pubsub/connection-handlers.js +0 -329
- package/dist/src/pubsub/connection-handlers.js.map +0 -1
- package/dist/src/pubsub/emit-self.d.ts +0 -6
- package/dist/src/pubsub/emit-self.d.ts.map +0 -1
- package/dist/src/pubsub/emit-self.js +0 -80
- package/dist/src/pubsub/emit-self.js.map +0 -1
- package/dist/src/pubsub/index.d.ts +0 -18
- package/dist/src/pubsub/index.d.ts.map +0 -1
- package/dist/src/pubsub/index.js +0 -17
- package/dist/src/pubsub/index.js.map +0 -1
- package/dist/src/pubsub/messages.d.ts +0 -6
- package/dist/src/pubsub/messages.d.ts.map +0 -1
- package/dist/src/pubsub/messages.js +0 -48
- package/dist/src/pubsub/messages.js.map +0 -1
- package/dist/src/pubsub/multiple-nodes.d.ts +0 -6
- package/dist/src/pubsub/multiple-nodes.d.ts.map +0 -1
- package/dist/src/pubsub/multiple-nodes.js +0 -350
- package/dist/src/pubsub/multiple-nodes.js.map +0 -1
- package/dist/src/pubsub/two-nodes.d.ts +0 -6
- package/dist/src/pubsub/two-nodes.d.ts.map +0 -1
- package/dist/src/pubsub/two-nodes.js +0 -216
- package/dist/src/pubsub/two-nodes.js.map +0 -1
- package/dist/src/pubsub/utils.d.ts +0 -5
- package/dist/src/pubsub/utils.d.ts.map +0 -1
- package/dist/src/pubsub/utils.js +0 -27
- package/dist/src/pubsub/utils.js.map +0 -1
- package/dist/src/stream-muxer/mega-stress-test.d.ts.map +0 -1
- package/dist/src/stream-muxer/mega-stress-test.js +0 -11
- package/dist/src/stream-muxer/mega-stress-test.js.map +0 -1
- package/dist/src/stream-muxer/spawner.d.ts +0 -4
- package/dist/src/stream-muxer/spawner.d.ts.map +0 -1
- package/dist/src/stream-muxer/spawner.js +0 -39
- package/dist/src/stream-muxer/spawner.js.map +0 -1
- package/dist/typedoc-urls.json +0 -44
- package/src/connection-encryption/utils/index.ts +0 -27
- package/src/matchers.ts +0 -18
- package/src/mocks/connection-manager.ts +0 -216
- package/src/mocks/connection.ts +0 -309
- package/src/mocks/duplex.ts +0 -11
- package/src/mocks/index.ts +0 -11
- package/src/mocks/multiaddr-connection.ts +0 -80
- package/src/mocks/muxer.ts +0 -331
- package/src/mocks/registrar.ts +0 -86
- package/src/mocks/upgrader.ts +0 -65
- package/src/pubsub/api.ts +0 -116
- package/src/pubsub/connection-handlers.ts +0 -413
- package/src/pubsub/emit-self.ts +0 -99
- package/src/pubsub/index.ts +0 -34
- package/src/pubsub/messages.ts +0 -59
- package/src/pubsub/multiple-nodes.ts +0 -440
- package/src/pubsub/two-nodes.ts +0 -272
- package/src/pubsub/utils.ts +0 -34
- package/src/stream-muxer/mega-stress-test.ts +0 -14
- package/src/stream-muxer/spawner.ts +0 -57
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import baseTest from './base-test.js'
|
|
2
2
|
import closeTest from './close-test.js'
|
|
3
|
-
import
|
|
3
|
+
import steamTest from './stream-test.js'
|
|
4
4
|
import stressTest from './stress-test.js'
|
|
5
5
|
import type { TestSetup } from '../index.js'
|
|
6
6
|
import type { StreamMuxerFactory } from '@libp2p/interface'
|
|
@@ -9,7 +9,7 @@ export default (common: TestSetup<StreamMuxerFactory>): void => {
|
|
|
9
9
|
describe('interface-stream-muxer', () => {
|
|
10
10
|
baseTest(common)
|
|
11
11
|
closeTest(common)
|
|
12
|
+
steamTest(common)
|
|
12
13
|
stressTest(common)
|
|
13
|
-
megaStressTest(common)
|
|
14
14
|
})
|
|
15
15
|
}
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { StreamCloseEvent, StreamMessageEvent } from '@libp2p/interface'
|
|
2
|
+
import { multiaddrConnectionPair } from '@libp2p/utils'
|
|
3
|
+
import { expect } from 'aegir/chai'
|
|
4
|
+
import delay from 'delay'
|
|
5
|
+
import all from 'it-all'
|
|
6
|
+
import { pEvent } from 'p-event'
|
|
7
|
+
import Sinon from 'sinon'
|
|
8
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
9
|
+
import { isValidTick } from '../is-valid-tick.ts'
|
|
10
|
+
import type { TestSetup } from '../index.ts'
|
|
11
|
+
import type { MultiaddrConnection, Stream, StreamMuxer, StreamMuxerFactory } from '@libp2p/interface'
|
|
12
|
+
|
|
13
|
+
export default (common: TestSetup<StreamMuxerFactory>): void => {
|
|
14
|
+
describe('streams', () => {
|
|
15
|
+
let dialer: StreamMuxer
|
|
16
|
+
let listener: StreamMuxer
|
|
17
|
+
let outboundStream: Stream
|
|
18
|
+
let inboundStream: Stream
|
|
19
|
+
let streams: [Stream, Stream]
|
|
20
|
+
let outboundConnection: MultiaddrConnection
|
|
21
|
+
let inboundConnection: MultiaddrConnection
|
|
22
|
+
|
|
23
|
+
beforeEach(async () => {
|
|
24
|
+
([outboundConnection, inboundConnection] = multiaddrConnectionPair())
|
|
25
|
+
|
|
26
|
+
const dialerFactory = await common.setup()
|
|
27
|
+
dialer = dialerFactory.createStreamMuxer(outboundConnection)
|
|
28
|
+
|
|
29
|
+
const listenerFactory = await common.setup()
|
|
30
|
+
listener = listenerFactory.createStreamMuxer(inboundConnection)
|
|
31
|
+
|
|
32
|
+
streams = await Promise.all([
|
|
33
|
+
pEvent<'stream', CustomEvent<Stream>>(listener, 'stream').then(evt => evt.detail),
|
|
34
|
+
dialer.createStream()
|
|
35
|
+
])
|
|
36
|
+
|
|
37
|
+
inboundStream = streams[0]
|
|
38
|
+
outboundStream = streams[1]
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
afterEach(async () => {
|
|
42
|
+
await dialer?.close()
|
|
43
|
+
await listener?.close()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('should have correct status after opening', () => {
|
|
47
|
+
streams.forEach(stream => {
|
|
48
|
+
expect(stream).to.have.property('status', 'open', `${stream.direction} stream status was incorrect`)
|
|
49
|
+
expect(stream).to.have.property('writeStatus', 'writable', `${stream.direction} stream writeStatus was incorrect`)
|
|
50
|
+
expect(stream).to.have.property('readStatus', 'readable', `${stream.direction} stream readStatus was incorrect`)
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should have correct timeline after opening', () => {
|
|
55
|
+
streams.forEach(stream => {
|
|
56
|
+
expect(isValidTick(stream.timeline.open)).to.equal(true, `${stream.direction} stream timeline.open was incorrect`)
|
|
57
|
+
expect(stream).to.not.have.nested.property('timeline.close', `${stream.direction} stream timeline.close was incorrect`)
|
|
58
|
+
expect(stream).to.not.have.nested.property('timeline.closeRead', `${stream.direction} stream timeline.closeRead was incorrect`)
|
|
59
|
+
expect(stream).to.not.have.nested.property('timeline.closeWrite', `${stream.direction} stream timeline.closeWrite was incorrect`)
|
|
60
|
+
expect(stream).to.not.have.nested.property('timeline.reset', `${stream.direction} stream timeline.reset was incorrect`)
|
|
61
|
+
expect(stream).to.not.have.nested.property('timeline.abort', `${stream.direction} stream timeline.abort was incorrect`)
|
|
62
|
+
})
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('outbound stream sends data', async () => {
|
|
66
|
+
const messageEventPromise = pEvent<'message', StreamMessageEvent>(inboundStream, 'message')
|
|
67
|
+
const data = Uint8Array.from([0, 1, 2, 3, 4])
|
|
68
|
+
|
|
69
|
+
outboundStream.send(data)
|
|
70
|
+
|
|
71
|
+
const evt = await messageEventPromise
|
|
72
|
+
expect(evt.data.subarray()).to.equalBytes(data)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('inbound stream sends data', async () => {
|
|
76
|
+
const messageEventPromise = pEvent<'message', StreamMessageEvent>(outboundStream, 'message')
|
|
77
|
+
const data = Uint8Array.from([0, 1, 2, 3, 4])
|
|
78
|
+
|
|
79
|
+
inboundStream.send(data)
|
|
80
|
+
|
|
81
|
+
const evt = await messageEventPromise
|
|
82
|
+
expect(evt.data.subarray()).to.equalBytes(data)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('closes', async () => {
|
|
86
|
+
const signal = AbortSignal.timeout(1_000)
|
|
87
|
+
|
|
88
|
+
void outboundStream.close({
|
|
89
|
+
signal
|
|
90
|
+
})
|
|
91
|
+
void inboundStream.close({
|
|
92
|
+
signal
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
expect(outboundStream).to.have.property('status', 'open')
|
|
96
|
+
expect(outboundStream).to.have.property('readStatus', 'readable')
|
|
97
|
+
expect(outboundStream).to.have.property('writeStatus', 'closing')
|
|
98
|
+
|
|
99
|
+
await Promise.all([
|
|
100
|
+
pEvent(outboundStream, 'close', {
|
|
101
|
+
signal
|
|
102
|
+
}),
|
|
103
|
+
pEvent(inboundStream, 'close', {
|
|
104
|
+
signal
|
|
105
|
+
})
|
|
106
|
+
])
|
|
107
|
+
|
|
108
|
+
streams.forEach(stream => {
|
|
109
|
+
expect(stream).to.have.property('status', 'closed', `${stream.direction} stream status was incorrect`)
|
|
110
|
+
expect(stream).to.have.property('writeStatus', 'closed', `${stream.direction} stream writeStatus was incorrect`)
|
|
111
|
+
expect(stream).to.have.property('readStatus', 'closed', `${stream.direction} stream readStatus was incorrect`)
|
|
112
|
+
|
|
113
|
+
expect(isValidTick(stream.timeline.open)).to.equal(true, `${stream.direction} stream timeline.open was incorrect`)
|
|
114
|
+
expect(isValidTick(stream.timeline.close)).to.equal(true, `${stream.direction} stream timeline.close was incorrect`)
|
|
115
|
+
|
|
116
|
+
expect(stream).to.not.have.nested.property('timeline.reset', `${stream.direction} stream timeline.reset was incorrect`)
|
|
117
|
+
expect(stream).to.not.have.nested.property('timeline.abort', `${stream.direction} stream timeline.abort was incorrect`)
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should send large amounts of data in both directions', async function () {
|
|
122
|
+
const timeout = 360_000
|
|
123
|
+
this.timeout(timeout)
|
|
124
|
+
|
|
125
|
+
const sent = new Array(10)
|
|
126
|
+
.fill(0)
|
|
127
|
+
.map((val, index) => new Uint8Array(1024 * 1024).fill(index))
|
|
128
|
+
|
|
129
|
+
// send data in both directions simultaneously
|
|
130
|
+
const [
|
|
131
|
+
outboundReceived,
|
|
132
|
+
inboundReceived
|
|
133
|
+
] = await Promise.all([
|
|
134
|
+
all(outboundStream),
|
|
135
|
+
all(inboundStream),
|
|
136
|
+
(async () => {
|
|
137
|
+
for (const buf of sent) {
|
|
138
|
+
if (!outboundStream.send(buf)) {
|
|
139
|
+
await pEvent(outboundStream, 'drain', {
|
|
140
|
+
rejectionEvents: [
|
|
141
|
+
'close'
|
|
142
|
+
]
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
await outboundStream.close()
|
|
148
|
+
})(),
|
|
149
|
+
(async () => {
|
|
150
|
+
for (const buf of sent) {
|
|
151
|
+
if (!inboundStream.send(buf)) {
|
|
152
|
+
await pEvent(inboundStream, 'drain', {
|
|
153
|
+
rejectionEvents: [
|
|
154
|
+
'close'
|
|
155
|
+
]
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
await inboundStream.close()
|
|
161
|
+
})()
|
|
162
|
+
])
|
|
163
|
+
|
|
164
|
+
expect(new Uint8ArrayList(...outboundReceived)).to.have.property('byteLength', new Uint8ArrayList(...sent).byteLength)
|
|
165
|
+
expect(new Uint8ArrayList(...inboundReceived)).to.have.property('byteLength', new Uint8ArrayList(...sent).byteLength)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('closes for writing', async () => {
|
|
169
|
+
const signal = AbortSignal.timeout(1_000)
|
|
170
|
+
|
|
171
|
+
const eventPromise = pEvent(inboundStream, 'remoteCloseWrite')
|
|
172
|
+
|
|
173
|
+
void outboundStream.close({
|
|
174
|
+
signal
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
expect(outboundStream).to.have.property('writeStatus', 'closing')
|
|
178
|
+
|
|
179
|
+
await delay(100)
|
|
180
|
+
|
|
181
|
+
expect(inboundStream).to.have.property('readStatus', 'readable')
|
|
182
|
+
|
|
183
|
+
await eventPromise
|
|
184
|
+
|
|
185
|
+
streams.forEach(stream => {
|
|
186
|
+
expect(stream).to.have.property('status', 'open', `${stream.direction} stream status was incorrect`)
|
|
187
|
+
|
|
188
|
+
expect(isValidTick(stream.timeline.open)).to.equal(true, `${stream.direction} stream timeline.open was incorrect`)
|
|
189
|
+
|
|
190
|
+
expect(stream).to.not.have.nested.property('timeline.close', `${stream.direction} stream timeline.close was incorrect`)
|
|
191
|
+
expect(stream).to.not.have.nested.property('timeline.reset', `${stream.direction} stream timeline.reset was incorrect`)
|
|
192
|
+
expect(stream).to.not.have.nested.property('timeline.abort', `${stream.direction} stream timeline.abort was incorrect`)
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
expect(outboundStream).to.have.property('writeStatus', 'closed', 'outbound stream writeStatus was incorrect')
|
|
196
|
+
expect(outboundStream).to.have.property('readStatus', 'readable', 'inbound stream readStatus was incorrect')
|
|
197
|
+
|
|
198
|
+
expect(outboundStream).to.not.have.nested.property('timeline.closeRead', 'inbound stream timeline.closeRead was incorrect')
|
|
199
|
+
|
|
200
|
+
expect(inboundStream).to.have.property('writeStatus', 'writable', 'inbound stream writeStatus was incorrect')
|
|
201
|
+
expect(inboundStream).to.have.property('readStatus', 'readable', 'inbound stream readStatus was incorrect')
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('aborts', async () => {
|
|
205
|
+
const eventPromises = Promise.all([
|
|
206
|
+
pEvent<'close', StreamCloseEvent>(outboundStream, 'close'),
|
|
207
|
+
pEvent<'close', StreamCloseEvent>(inboundStream, 'close')
|
|
208
|
+
])
|
|
209
|
+
|
|
210
|
+
const err = new Error('Urk!')
|
|
211
|
+
outboundStream.abort(err)
|
|
212
|
+
|
|
213
|
+
const [outboundEvent, inboundEvent] = await eventPromises
|
|
214
|
+
|
|
215
|
+
streams.forEach(stream => {
|
|
216
|
+
expect(stream).to.have.property('writeStatus', 'closed', `${stream.direction} stream writeStatus was incorrect`)
|
|
217
|
+
expect(stream).to.have.property('readStatus', 'closed', `${stream.direction} stream readStatus was incorrect`)
|
|
218
|
+
|
|
219
|
+
expect(isValidTick(stream.timeline.open)).to.equal(true, `${stream.direction} stream timeline.open was incorrect`)
|
|
220
|
+
|
|
221
|
+
expect(stream).to.not.have.nested.property('timeline.close', `${stream.direction} stream timeline.close was incorrect`)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
expect(outboundStream).to.have.property('status', 'aborted', 'outbound stream status was incorrect')
|
|
225
|
+
expect(isValidTick(outboundStream.timeline.close)).to.equal(true, 'outbound stream timeline.abort was incorrect')
|
|
226
|
+
|
|
227
|
+
expect(inboundStream).to.have.property('status', 'reset', 'inbound stream status was incorrect')
|
|
228
|
+
expect(isValidTick(inboundStream.timeline.close)).to.equal(true, 'inbound stream timeline.reset was incorrect')
|
|
229
|
+
|
|
230
|
+
expect(() => outboundStream.send(Uint8Array.from([0, 1, 2, 3]))).to.throw()
|
|
231
|
+
.with.property('name', 'StreamStateError', 'could still write to aborted stream')
|
|
232
|
+
|
|
233
|
+
expect(() => inboundStream.send(Uint8Array.from([0, 1, 2, 3]))).to.throw()
|
|
234
|
+
.with.property('name', 'StreamStateError', 'could still write to reset stream')
|
|
235
|
+
|
|
236
|
+
expect(outboundEvent).to.have.property('error', err)
|
|
237
|
+
expect(inboundEvent).to.have.nested.property('error.name', 'StreamResetError')
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('does not send close read when remote closes write', async () => {
|
|
241
|
+
// @ts-expect-error internal method of AbstractMessageStream
|
|
242
|
+
const sendCloseReadSpy = Sinon.spy(outboundStream, 'sendCloseRead')
|
|
243
|
+
|
|
244
|
+
await Promise.all([
|
|
245
|
+
pEvent(outboundStream, 'remoteCloseWrite'),
|
|
246
|
+
inboundStream.close()
|
|
247
|
+
])
|
|
248
|
+
|
|
249
|
+
await delay(100)
|
|
250
|
+
|
|
251
|
+
expect(sendCloseReadSpy.called).to.be.false()
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
it('does not send close read or write when remote resets', async () => {
|
|
255
|
+
// @ts-expect-error internal method of AbstractMessageStream
|
|
256
|
+
const sendCloseReadSpy = Sinon.spy(outboundStream, 'sendCloseRead')
|
|
257
|
+
// @ts-expect-error internal method of AbstractMessageStream
|
|
258
|
+
const sendCloseWriteSpy = Sinon.spy(outboundStream, 'sendCloseWrite')
|
|
259
|
+
|
|
260
|
+
await Promise.all([
|
|
261
|
+
pEvent(outboundStream, 'close'),
|
|
262
|
+
// eslint-disable-next-line @typescript-eslint/await-thenable
|
|
263
|
+
inboundStream.abort(new Error('Urk!'))
|
|
264
|
+
])
|
|
265
|
+
|
|
266
|
+
await delay(100)
|
|
267
|
+
|
|
268
|
+
await outboundStream.close()
|
|
269
|
+
|
|
270
|
+
await delay(100)
|
|
271
|
+
|
|
272
|
+
expect(sendCloseReadSpy.called).to.be.false()
|
|
273
|
+
expect(sendCloseWriteSpy.called).to.be.false()
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
it('should wait for sending data to finish when closing gracefully', async () => {
|
|
277
|
+
let sent = 0
|
|
278
|
+
let received = 0
|
|
279
|
+
let filledBuffer = false
|
|
280
|
+
const receivedAll = Promise.withResolvers<boolean>()
|
|
281
|
+
|
|
282
|
+
inboundStream.addEventListener('message', (evt) => {
|
|
283
|
+
received += evt.data.byteLength
|
|
284
|
+
|
|
285
|
+
if (filledBuffer && received === sent) {
|
|
286
|
+
receivedAll.resolve(true)
|
|
287
|
+
}
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
// fill the send buffer
|
|
291
|
+
while (true) {
|
|
292
|
+
const length = 1024
|
|
293
|
+
sent += length
|
|
294
|
+
const sendMore = outboundStream.send(new Uint8Array(length))
|
|
295
|
+
|
|
296
|
+
if (sendMore === false) {
|
|
297
|
+
filledBuffer = true
|
|
298
|
+
break
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
expect(outboundStream.writeStatus).to.equal('writable')
|
|
303
|
+
expect(outboundStream.writableNeedsDrain).to.be.true()
|
|
304
|
+
|
|
305
|
+
// close gracefully
|
|
306
|
+
await outboundStream.close()
|
|
307
|
+
|
|
308
|
+
await expect(receivedAll.promise).to.eventually.be.true('did not receive all data')
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
it('should wait for sending data to finish when closing the writable end gracefully', async () => {
|
|
312
|
+
let sent = 0
|
|
313
|
+
let received = 0
|
|
314
|
+
let filledBuffer = false
|
|
315
|
+
const receivedAll = Promise.withResolvers<boolean>()
|
|
316
|
+
|
|
317
|
+
inboundStream.addEventListener('message', (evt) => {
|
|
318
|
+
received += evt.data.byteLength
|
|
319
|
+
|
|
320
|
+
if (filledBuffer && received === sent) {
|
|
321
|
+
receivedAll.resolve(true)
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
// fill the send buffer
|
|
326
|
+
while (true) {
|
|
327
|
+
const length = 1024
|
|
328
|
+
sent += length
|
|
329
|
+
const sendMore = outboundStream.send(new Uint8Array(length))
|
|
330
|
+
|
|
331
|
+
if (sendMore === false) {
|
|
332
|
+
filledBuffer = true
|
|
333
|
+
break
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
expect(outboundStream.writeStatus).to.equal('writable')
|
|
338
|
+
expect(outboundStream.writableNeedsDrain).to.be.true()
|
|
339
|
+
|
|
340
|
+
// close gracefully
|
|
341
|
+
await outboundStream.close()
|
|
342
|
+
|
|
343
|
+
await expect(receivedAll.promise).to.eventually.be.true('did not receive all data')
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
it('should abort close due to timeout with slow sender', async () => {
|
|
347
|
+
const chunkSize = 1024
|
|
348
|
+
|
|
349
|
+
// make the 'drain' event slow to fire
|
|
350
|
+
// @ts-expect-error private fields
|
|
351
|
+
outboundConnection.local.delay = 1000
|
|
352
|
+
|
|
353
|
+
inboundStream = streams[0]
|
|
354
|
+
outboundStream = streams[1]
|
|
355
|
+
|
|
356
|
+
// ensure there are bytes left in the write queue
|
|
357
|
+
// @ts-expect-error private fields
|
|
358
|
+
outboundStream.maxMessageSize = chunkSize - 1
|
|
359
|
+
|
|
360
|
+
// fill the send buffer
|
|
361
|
+
while (true) {
|
|
362
|
+
const sendMore = outboundStream.send(new Uint8Array(chunkSize * 10))
|
|
363
|
+
|
|
364
|
+
if (sendMore === false) {
|
|
365
|
+
expect(outboundStream).to.have.nested.property('writeBuffer.byteLength').that.is.greaterThan(0)
|
|
366
|
+
|
|
367
|
+
break
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
expect(outboundStream.writeStatus).to.equal('writable')
|
|
372
|
+
expect(outboundStream.writableNeedsDrain).to.be.true()
|
|
373
|
+
|
|
374
|
+
// close stream, should be aborted as drain event will not have fired
|
|
375
|
+
await expect(outboundStream.close({
|
|
376
|
+
signal: AbortSignal.timeout(10)
|
|
377
|
+
})).to.eventually.be.rejected
|
|
378
|
+
.with.property('name', 'TimeoutError')
|
|
379
|
+
})
|
|
380
|
+
})
|
|
381
|
+
}
|
|
@@ -1,27 +1,101 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { multiaddrConnectionPair, echo } from '@libp2p/utils'
|
|
2
|
+
import { expect } from 'aegir/chai'
|
|
3
|
+
import { pEvent } from 'p-event'
|
|
4
|
+
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
5
|
+
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
2
6
|
import type { TestSetup } from '../index.js'
|
|
3
|
-
import type { StreamMuxerFactory,
|
|
7
|
+
import type { StreamMuxerFactory, StreamMuxer, MultiaddrConnection } from '@libp2p/interface'
|
|
8
|
+
|
|
9
|
+
async function * messages (nMsg: number): AsyncGenerator<Uint8Array> {
|
|
10
|
+
for (let i = 0; i < nMsg; i++) {
|
|
11
|
+
yield uint8ArrayFromString(`message ${i + 1}/${nMsg}`)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function spawn (createMuxer: (maConn: MultiaddrConnection) => Promise<StreamMuxer>, nStreams: number, nMsg: number): Promise<void> {
|
|
16
|
+
const [outboundConnection, inboundConnection] = multiaddrConnectionPair()
|
|
17
|
+
|
|
18
|
+
const listener = await createMuxer(inboundConnection)
|
|
19
|
+
listener.addEventListener('stream', function echoStreamHandler (evt) {
|
|
20
|
+
echo(evt.detail)
|
|
21
|
+
.catch(err => {
|
|
22
|
+
evt.detail.abort(err)
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const dialer = await createMuxer(outboundConnection)
|
|
27
|
+
|
|
28
|
+
const spawnStream = async (): Promise<void> => {
|
|
29
|
+
let receivedBytes = 0
|
|
30
|
+
let sentBytes = 0
|
|
31
|
+
|
|
32
|
+
for await (const buf of messages(nMsg)) {
|
|
33
|
+
sentBytes += buf.byteLength
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const receivedAllMessagesPromise = Promise.withResolvers<void>()
|
|
37
|
+
const outboundStream = await dialer.createStream()
|
|
38
|
+
|
|
39
|
+
outboundStream.addEventListener('message', function countMessages (evt) {
|
|
40
|
+
receivedBytes += evt.data.byteLength
|
|
41
|
+
|
|
42
|
+
outboundStream.log('%s - echoed bytes %d/%d', uint8ArrayToString(evt.data.subarray()), receivedBytes, sentBytes)
|
|
43
|
+
|
|
44
|
+
if (receivedBytes === sentBytes) {
|
|
45
|
+
outboundStream.log('received all bytes')
|
|
46
|
+
receivedAllMessagesPromise.resolve()
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
for await (const buf of messages(nMsg)) {
|
|
51
|
+
const sendMore = outboundStream.send(buf)
|
|
52
|
+
|
|
53
|
+
if (sendMore === false) {
|
|
54
|
+
await pEvent(outboundStream, 'drain', {
|
|
55
|
+
rejectionEvents: ['close']
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await receivedAllMessagesPromise.promise
|
|
61
|
+
outboundStream.log('sent and received all messages %d/%d', receivedBytes, sentBytes)
|
|
62
|
+
|
|
63
|
+
await Promise.all([
|
|
64
|
+
pEvent(outboundStream, 'close'),
|
|
65
|
+
outboundStream.close()
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
expect(receivedBytes).to.equal(sentBytes)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await Promise.all(
|
|
72
|
+
Array.from(Array(nStreams), async () => {
|
|
73
|
+
await spawnStream()
|
|
74
|
+
})
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
await listener.close()
|
|
78
|
+
await dialer.close()
|
|
79
|
+
}
|
|
4
80
|
|
|
5
81
|
export default (common: TestSetup<StreamMuxerFactory>): void => {
|
|
6
|
-
const createMuxer = async (
|
|
82
|
+
const createMuxer = async (maConn: MultiaddrConnection): Promise<StreamMuxer> => {
|
|
7
83
|
const factory = await common.setup()
|
|
8
|
-
return factory.createStreamMuxer(
|
|
84
|
+
return factory.createStreamMuxer(maConn)
|
|
9
85
|
}
|
|
10
86
|
|
|
87
|
+
const streams = [1, 10, 100, 1000]
|
|
88
|
+
const messages = [1, 10, 100]
|
|
89
|
+
|
|
11
90
|
describe('stress test', function () {
|
|
12
|
-
this.timeout(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
it('100 streams with 10 msg', async () => { await spawn(createMuxer, 100, 10) })
|
|
22
|
-
it('100 streams with 100 msg', async () => { await spawn(createMuxer, 100, 100) })
|
|
23
|
-
it('1000 streams with 1 msg', async () => { await spawn(createMuxer, 1000, 1) })
|
|
24
|
-
it('1000 streams with 10 msg', async () => { await spawn(createMuxer, 1000, 10) })
|
|
25
|
-
it('1000 streams with 100 msg', async () => { await spawn(createMuxer, 1000, 100) })
|
|
91
|
+
this.timeout(1_600_000)
|
|
92
|
+
|
|
93
|
+
for (let i = 0; i < streams.length; i++) {
|
|
94
|
+
for (let j = 0; j < messages.length; j++) {
|
|
95
|
+
it(`${streams[i]} stream(s) with ${messages[j]} msg(s)`, async () => {
|
|
96
|
+
await spawn(createMuxer, streams[i], messages[j])
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
}
|
|
26
100
|
})
|
|
27
101
|
}
|