@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.
- package/dist/src/mocks/index.d.ts +0 -2
- package/dist/src/mocks/index.d.ts.map +1 -1
- package/dist/src/mocks/index.js +0 -2
- package/dist/src/mocks/index.js.map +1 -1
- package/dist/src/mocks/upgrader.d.ts.map +1 -1
- package/dist/src/mocks/upgrader.js +0 -1
- package/dist/src/mocks/upgrader.js.map +1 -1
- package/dist/src/transport/index.d.ts +17 -8
- package/dist/src/transport/index.d.ts.map +1 -1
- package/dist/src/transport/index.js +311 -214
- package/dist/src/transport/index.js.map +1 -1
- package/dist/src/transport/utils.d.ts +1 -3
- package/dist/src/transport/utils.d.ts.map +1 -1
- package/dist/src/transport/utils.js +5 -4
- package/dist/src/transport/utils.js.map +1 -1
- package/package.json +15 -15
- package/src/mocks/index.ts +0 -2
- package/src/mocks/upgrader.ts +1 -3
- package/src/transport/index.ts +398 -232
- package/src/transport/utils.ts +6 -6
- package/dist/src/mocks/connection-gater.d.ts +0 -3
- package/dist/src/mocks/connection-gater.d.ts.map +0 -1
- package/dist/src/mocks/connection-gater.js +0 -17
- package/dist/src/mocks/connection-gater.js.map +0 -1
- package/dist/src/mocks/metrics.d.ts +0 -3
- package/dist/src/mocks/metrics.d.ts.map +0 -1
- package/dist/src/mocks/metrics.js +0 -286
- package/dist/src/mocks/metrics.js.map +0 -1
- package/dist/src/mocks/peer-discovery.d.ts +0 -21
- package/dist/src/mocks/peer-discovery.d.ts.map +0 -1
- package/dist/src/mocks/peer-discovery.js +0 -47
- package/dist/src/mocks/peer-discovery.js.map +0 -1
- package/src/mocks/connection-gater.ts +0 -18
- package/src/mocks/metrics.ts +0 -385
- package/src/mocks/peer-discovery.ts +0 -59
package/src/transport/index.ts
CHANGED
|
@@ -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,
|
|
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
|
|
23
|
+
* Addresses that will be used to dial listeners - both addresses must resolve
|
|
24
|
+
* to the same node
|
|
18
25
|
*/
|
|
19
|
-
dialAddrs
|
|
26
|
+
dialAddrs?: [Multiaddr, Multiaddr]
|
|
20
27
|
|
|
21
28
|
/**
|
|
22
|
-
*
|
|
29
|
+
* Filter out any addresses that cannot be dialed by the transport
|
|
23
30
|
*/
|
|
24
|
-
|
|
31
|
+
dialMultiaddrMatcher: MultiaddrMatcher
|
|
25
32
|
|
|
26
33
|
/**
|
|
27
|
-
*
|
|
34
|
+
* Filter out any addresses that cannot be listened on by the transport
|
|
28
35
|
*/
|
|
29
|
-
|
|
36
|
+
listenMultiaddrMatcher: MultiaddrMatcher
|
|
30
37
|
|
|
31
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
]
|
|
196
|
-
})
|
|
147
|
+
({ dialer, listener, dialAddrs } = await getSetup(common))
|
|
148
|
+
|
|
149
|
+
let incomingConnectionPromise: DeferredPromise<Connection> | undefined
|
|
197
150
|
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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(
|
|
163
|
+
const connection = await dialer.dial(dialAddrs[0])
|
|
215
164
|
let remoteConn: Connection | undefined
|
|
216
165
|
|
|
217
|
-
if (
|
|
218
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
244
|
-
return this.skip()
|
|
245
|
-
}
|
|
195
|
+
({ dialer, listener, dialAddrs, dialMultiaddrMatcher } = await getSetup(common))
|
|
246
196
|
|
|
247
|
-
|
|
248
|
-
|
|
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(
|
|
207
|
+
await expect(dialer.dial(dialAddrs[0])).to.eventually.be.rejected
|
|
271
208
|
.with.property('name', 'EncryptionFailedError')
|
|
272
209
|
|
|
273
|
-
expect(dialer.getConnections()
|
|
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
|
-
|
|
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
|
-
|
|
282
|
-
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
224
|
+
if (listener == null) {
|
|
225
|
+
return this.skip()
|
|
226
|
+
}
|
|
292
227
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
await common.teardown()
|
|
296
|
-
})
|
|
228
|
+
const tm = getTransportManager(listener)
|
|
229
|
+
const transportListeners = tm.getListeners()
|
|
297
230
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
321
|
-
const
|
|
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
|
-
|
|
271
|
+
if (listener == null) {
|
|
272
|
+
return this.skip()
|
|
273
|
+
}
|
|
324
274
|
|
|
325
|
-
|
|
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
|
|
328
|
-
|
|
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('
|
|
333
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
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
|
-
|
|
355
|
-
|
|
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
|
-
|
|
358
|
-
|
|
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
|
-
|
|
362
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
p = pEvent(listener, 'listening')
|
|
415
|
+
// remote closes write
|
|
416
|
+
remoteInputStream.end()
|
|
367
417
|
|
|
368
|
-
|
|
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
|
-
|
|
372
|
-
|
|
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
|
-
|
|
376
|
-
|
|
450
|
+
const streamHandler: StreamHandler = ({ stream }) => {
|
|
451
|
+
void Promise.resolve().then(async () => {
|
|
452
|
+
getRemoteStream.resolve(stream)
|
|
453
|
+
})
|
|
377
454
|
}
|
|
378
455
|
|
|
379
|
-
await
|
|
380
|
-
|
|
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
|
-
|
|
385
|
-
|
|
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
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
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()
|
|
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
|
+
}
|