@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
|
@@ -1,43 +1,46 @@
|
|
|
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';
|
|
13
|
+
async function getSetup(common) {
|
|
14
|
+
const setup = await common.setup();
|
|
15
|
+
const dialer = await createPeer(setup.dialer);
|
|
16
|
+
let listener;
|
|
17
|
+
if (setup.listener != null) {
|
|
18
|
+
listener = await createPeer(setup.listener);
|
|
19
|
+
}
|
|
20
|
+
const dialAddrs = listener?.getMultiaddrs() ?? setup.dialAddrs;
|
|
21
|
+
if (dialAddrs == null) {
|
|
22
|
+
throw new Error('Listener config or dial addresses must be specified');
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
dialer,
|
|
26
|
+
listener,
|
|
27
|
+
dialAddrs: dialAddrs.filter(ma => setup.dialMultiaddrMatcher.exactMatch(ma)),
|
|
28
|
+
dialMultiaddrMatcher: setup.dialMultiaddrMatcher,
|
|
29
|
+
listenMultiaddrMatcher: setup.listenMultiaddrMatcher
|
|
30
|
+
};
|
|
31
|
+
}
|
|
10
32
|
export default (common) => {
|
|
11
33
|
describe('interface-transport', () => {
|
|
12
|
-
let dialAddrs;
|
|
13
|
-
let listenAddrs;
|
|
14
|
-
let transport;
|
|
15
34
|
let dialer;
|
|
16
35
|
let listener;
|
|
17
|
-
let
|
|
18
|
-
|
|
19
|
-
({ dialAddrs, listenAddrs = dialAddrs, transport, dialOnly = false } = await common.setup());
|
|
20
|
-
});
|
|
36
|
+
let dialAddrs;
|
|
37
|
+
let dialMultiaddrMatcher;
|
|
21
38
|
afterEach(async () => {
|
|
22
39
|
await stop(dialer, listener);
|
|
23
40
|
await common.teardown();
|
|
24
41
|
});
|
|
25
42
|
it('simple', async () => {
|
|
26
|
-
dialer = await
|
|
27
|
-
transports: [
|
|
28
|
-
transport
|
|
29
|
-
]
|
|
30
|
-
});
|
|
31
|
-
if (!dialOnly) {
|
|
32
|
-
listener = await createPeer({
|
|
33
|
-
addresses: {
|
|
34
|
-
listen: [listenAddrs[0].toString()]
|
|
35
|
-
},
|
|
36
|
-
transports: [
|
|
37
|
-
transport
|
|
38
|
-
]
|
|
39
|
-
});
|
|
40
|
-
}
|
|
43
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
41
44
|
const input = Uint8Array.from([0, 1, 2, 3, 4]);
|
|
42
45
|
const output = await dialer.services.echo.echo(dialAddrs[0], input, {
|
|
43
46
|
signal: AbortSignal.timeout(5000)
|
|
@@ -45,24 +48,7 @@ export default (common) => {
|
|
|
45
48
|
expect(output).to.equalBytes(input);
|
|
46
49
|
});
|
|
47
50
|
it('should listen on multiple addresses', async () => {
|
|
48
|
-
dialer = await
|
|
49
|
-
transports: [
|
|
50
|
-
transport
|
|
51
|
-
]
|
|
52
|
-
});
|
|
53
|
-
if (!dialOnly) {
|
|
54
|
-
listener = await createPeer({
|
|
55
|
-
addresses: {
|
|
56
|
-
listen: [
|
|
57
|
-
listenAddrs[0].toString(),
|
|
58
|
-
listenAddrs[1].toString()
|
|
59
|
-
]
|
|
60
|
-
},
|
|
61
|
-
transports: [
|
|
62
|
-
transport
|
|
63
|
-
]
|
|
64
|
-
});
|
|
65
|
-
}
|
|
51
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
66
52
|
const input = Uint8Array.from([0, 1, 2, 3, 4]);
|
|
67
53
|
await expect(dialer.services.echo.echo(dialAddrs[0], input, {
|
|
68
54
|
signal: AbortSignal.timeout(5000)
|
|
@@ -72,21 +58,7 @@ export default (common) => {
|
|
|
72
58
|
})).to.eventually.deep.equal(input);
|
|
73
59
|
});
|
|
74
60
|
it('can close connections', async () => {
|
|
75
|
-
dialer = await
|
|
76
|
-
transports: [
|
|
77
|
-
transport
|
|
78
|
-
]
|
|
79
|
-
});
|
|
80
|
-
if (!dialOnly) {
|
|
81
|
-
listener = await createPeer({
|
|
82
|
-
addresses: {
|
|
83
|
-
listen: [listenAddrs[0].toString()]
|
|
84
|
-
},
|
|
85
|
-
transports: [
|
|
86
|
-
transport
|
|
87
|
-
]
|
|
88
|
-
});
|
|
89
|
-
}
|
|
61
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
90
62
|
const conn = await dialer.dial(dialAddrs[0], {
|
|
91
63
|
signal: AbortSignal.timeout(5000)
|
|
92
64
|
});
|
|
@@ -94,21 +66,7 @@ export default (common) => {
|
|
|
94
66
|
expect(isValidTick(conn.timeline.close)).to.equal(true);
|
|
95
67
|
});
|
|
96
68
|
it('abort before dialing throws AbortError', async () => {
|
|
97
|
-
dialer = await
|
|
98
|
-
transports: [
|
|
99
|
-
transport
|
|
100
|
-
]
|
|
101
|
-
});
|
|
102
|
-
if (!dialOnly) {
|
|
103
|
-
listener = await createPeer({
|
|
104
|
-
addresses: {
|
|
105
|
-
listen: [listenAddrs[0].toString()]
|
|
106
|
-
},
|
|
107
|
-
transports: [
|
|
108
|
-
transport
|
|
109
|
-
]
|
|
110
|
-
});
|
|
111
|
-
}
|
|
69
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
112
70
|
const controller = new AbortController();
|
|
113
71
|
controller.abort();
|
|
114
72
|
await expect(dialer.dial(dialAddrs[0], {
|
|
@@ -117,21 +75,7 @@ export default (common) => {
|
|
|
117
75
|
.with.property('name', 'AbortError');
|
|
118
76
|
});
|
|
119
77
|
it('abort while dialing throws AbortError', async () => {
|
|
120
|
-
dialer = await
|
|
121
|
-
transports: [
|
|
122
|
-
transport
|
|
123
|
-
]
|
|
124
|
-
});
|
|
125
|
-
if (!dialOnly) {
|
|
126
|
-
listener = await createPeer({
|
|
127
|
-
addresses: {
|
|
128
|
-
listen: [listenAddrs[0].toString()]
|
|
129
|
-
},
|
|
130
|
-
transports: [
|
|
131
|
-
transport
|
|
132
|
-
]
|
|
133
|
-
});
|
|
134
|
-
}
|
|
78
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
135
79
|
slowNetwork(dialer, 100);
|
|
136
80
|
const controller = new AbortController();
|
|
137
81
|
setTimeout(() => { controller.abort(); }, 50);
|
|
@@ -141,180 +85,325 @@ export default (common) => {
|
|
|
141
85
|
.with.property('name', 'AbortError');
|
|
142
86
|
});
|
|
143
87
|
it('should close all streams when the connection closes', async () => {
|
|
144
|
-
dialer = await
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
listen: [listenAddrs[0].toString()]
|
|
153
|
-
},
|
|
154
|
-
transports: [
|
|
155
|
-
transport
|
|
156
|
-
],
|
|
157
|
-
services: {
|
|
158
|
-
echo: echo({
|
|
159
|
-
maxInboundStreams: 5
|
|
160
|
-
})
|
|
88
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
89
|
+
let incomingConnectionPromise;
|
|
90
|
+
if (listener != null) {
|
|
91
|
+
incomingConnectionPromise = pDefer();
|
|
92
|
+
listener.addEventListener('connection:open', (event) => {
|
|
93
|
+
const conn = event.detail;
|
|
94
|
+
if (conn.remotePeer.equals(dialer.peerId)) {
|
|
95
|
+
incomingConnectionPromise?.resolve(conn);
|
|
161
96
|
}
|
|
162
97
|
});
|
|
163
98
|
}
|
|
164
|
-
const connection = await dialer.dial(
|
|
99
|
+
const connection = await dialer.dial(dialAddrs[0]);
|
|
165
100
|
let remoteConn;
|
|
166
|
-
if (
|
|
167
|
-
|
|
168
|
-
expect(remoteConnections).to.have.lengthOf(1);
|
|
169
|
-
remoteConn = remoteConnections[0];
|
|
101
|
+
if (incomingConnectionPromise != null) {
|
|
102
|
+
remoteConn = await incomingConnectionPromise.promise;
|
|
170
103
|
}
|
|
171
|
-
const streams = [];
|
|
172
104
|
for (let i = 0; i < 5; i++) {
|
|
173
|
-
|
|
105
|
+
await connection.newStream('/echo/1.0.0', {
|
|
174
106
|
maxOutboundStreams: 5
|
|
175
|
-
})
|
|
107
|
+
});
|
|
176
108
|
}
|
|
109
|
+
const streams = connection.streams;
|
|
177
110
|
// Close the connection and verify all streams have been closed
|
|
178
111
|
await connection.close();
|
|
179
|
-
await pWaitFor(() => connection.streams.length === 0
|
|
112
|
+
await pWaitFor(() => connection.streams.length === 0, {
|
|
113
|
+
timeout: 5000
|
|
114
|
+
});
|
|
180
115
|
if (remoteConn != null) {
|
|
181
|
-
await pWaitFor(() => remoteConn.streams.length === 0
|
|
116
|
+
await pWaitFor(() => remoteConn.streams.length === 0, {
|
|
117
|
+
timeout: 5000
|
|
118
|
+
});
|
|
182
119
|
}
|
|
183
|
-
expect(streams.find(stream => stream.status
|
|
120
|
+
expect(streams.find(stream => stream.status === 'open')).to.be.undefined();
|
|
184
121
|
});
|
|
185
122
|
it('should not handle connection if upgradeInbound rejects', async function () {
|
|
186
|
-
|
|
123
|
+
({ dialer, listener, dialAddrs, dialMultiaddrMatcher } = await getSetup(common));
|
|
124
|
+
if (listener == null) {
|
|
187
125
|
return this.skip();
|
|
188
126
|
}
|
|
189
|
-
dialer = await createPeer({
|
|
190
|
-
transports: [
|
|
191
|
-
transport
|
|
192
|
-
]
|
|
193
|
-
});
|
|
194
|
-
if (!dialOnly) {
|
|
195
|
-
listener = await createPeer({
|
|
196
|
-
addresses: {
|
|
197
|
-
listen: [listenAddrs[0].toString()]
|
|
198
|
-
},
|
|
199
|
-
transports: [
|
|
200
|
-
transport
|
|
201
|
-
]
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
127
|
const upgrader = getUpgrader(listener);
|
|
205
128
|
upgrader.upgradeInbound = async () => {
|
|
206
129
|
await delay(100);
|
|
207
130
|
throw new Error('Oh noes!');
|
|
208
131
|
};
|
|
209
|
-
await expect(dialer.dial(
|
|
132
|
+
await expect(dialer.dial(dialAddrs[0])).to.eventually.be.rejected
|
|
210
133
|
.with.property('name', 'EncryptionFailedError');
|
|
211
|
-
expect(dialer.getConnections()
|
|
134
|
+
expect(dialer.getConnections().filter(conn => {
|
|
135
|
+
return dialMultiaddrMatcher.exactMatch(conn.remoteAddr);
|
|
136
|
+
})).to.have.lengthOf(0);
|
|
212
137
|
if (listener != null) {
|
|
213
|
-
|
|
138
|
+
const remoteConnections = listener.getConnections(dialer.peerId)
|
|
139
|
+
.filter(conn => dialMultiaddrMatcher.exactMatch(conn.remoteAddr));
|
|
140
|
+
expect(remoteConnections).to.have.lengthOf(0);
|
|
214
141
|
}
|
|
215
142
|
});
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
let dialAddrs;
|
|
220
|
-
let transport;
|
|
221
|
-
let dialer;
|
|
222
|
-
let listener;
|
|
223
|
-
let dialOnly;
|
|
224
|
-
beforeEach(async () => {
|
|
225
|
-
({ dialAddrs, listenAddrs = dialAddrs, transport, dialOnly = false } = await common.setup());
|
|
226
|
-
});
|
|
227
|
-
afterEach(async () => {
|
|
228
|
-
await stop(dialer, listener);
|
|
229
|
-
await common.teardown();
|
|
230
|
-
});
|
|
231
|
-
it('emits connection', async function () {
|
|
232
|
-
if (dialOnly) {
|
|
143
|
+
it('should omit peerid in listening addresses', async function () {
|
|
144
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
145
|
+
if (listener == null) {
|
|
233
146
|
return this.skip();
|
|
234
147
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
148
|
+
const tm = getTransportManager(listener);
|
|
149
|
+
const transportListeners = tm.getListeners();
|
|
150
|
+
for (const transportListener of transportListeners) {
|
|
151
|
+
for (const ma of transportListener.getAddrs()) {
|
|
152
|
+
expect(ma.toString()).to.not.include(`/p2p/${listener.peerId}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
it('should handle one big write', async function () {
|
|
157
|
+
const timeout = 120_000;
|
|
158
|
+
this.timeout(timeout);
|
|
159
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
160
|
+
const input = new Uint8Array(1024 * 1024 * 10).fill(5);
|
|
161
|
+
const output = await dialer.services.echo.echo(dialAddrs[0], input, {
|
|
162
|
+
signal: AbortSignal.timeout(timeout)
|
|
239
163
|
});
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
164
|
+
expect(output).to.equalBytes(input);
|
|
165
|
+
});
|
|
166
|
+
it('should handle many small writes', async function () {
|
|
167
|
+
const timeout = 120_000;
|
|
168
|
+
this.timeout(timeout);
|
|
169
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
170
|
+
for (let i = 0; i < 2000; i++) {
|
|
171
|
+
const input = new Uint8Array(1024).fill(5);
|
|
172
|
+
const output = await dialer.services.echo.echo(dialAddrs[0], input, {
|
|
173
|
+
signal: AbortSignal.timeout(timeout)
|
|
248
174
|
});
|
|
175
|
+
expect(output).to.equalBytes(input);
|
|
249
176
|
}
|
|
250
|
-
const transportManager = getTransportManager(listener);
|
|
251
|
-
const transportListener = transportManager.getListeners()[0];
|
|
252
|
-
const p = pEvent(transportListener, 'connection');
|
|
253
|
-
await expect(dialer.dial(dialAddrs[0])).to.eventually.be.ok();
|
|
254
|
-
await raceSignal(p, AbortSignal.timeout(1000), {
|
|
255
|
-
errorMessage: 'Did not emit connection event'
|
|
256
|
-
});
|
|
257
177
|
});
|
|
258
|
-
it('
|
|
259
|
-
|
|
178
|
+
it('can close a stream for reading but send a large amount of data', async function () {
|
|
179
|
+
const timeout = 120_000;
|
|
180
|
+
this.timeout(timeout);
|
|
181
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
182
|
+
if (listener == null) {
|
|
260
183
|
return this.skip();
|
|
261
184
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
185
|
+
const protocol = '/send-data/1.0.0';
|
|
186
|
+
const chunkSize = 1024;
|
|
187
|
+
const bytes = chunkSize * 1024 * 10;
|
|
188
|
+
const deferred = pDefer();
|
|
189
|
+
await listener.handle(protocol, ({ stream }) => {
|
|
190
|
+
Promise.resolve().then(async () => {
|
|
191
|
+
let read = 0;
|
|
192
|
+
for await (const buf of stream.source) {
|
|
193
|
+
read += buf.byteLength;
|
|
194
|
+
if (read === bytes) {
|
|
195
|
+
deferred.resolve();
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
.catch(err => {
|
|
201
|
+
deferred.reject(err);
|
|
202
|
+
stream.abort(err);
|
|
203
|
+
});
|
|
266
204
|
});
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
205
|
+
const stream = await dialer.dialProtocol(dialAddrs[0], protocol);
|
|
206
|
+
await stream.closeRead();
|
|
207
|
+
await stream.sink((async function* () {
|
|
208
|
+
for (let i = 0; i < bytes; i += chunkSize) {
|
|
209
|
+
yield new Uint8Array(chunkSize);
|
|
210
|
+
}
|
|
211
|
+
})());
|
|
212
|
+
await stream.close();
|
|
213
|
+
await deferred.promise;
|
|
214
|
+
});
|
|
215
|
+
it('can close a stream for writing but receive a large amount of data', async function () {
|
|
216
|
+
const timeout = 120_000;
|
|
217
|
+
this.timeout(timeout);
|
|
218
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
219
|
+
if (listener == null) {
|
|
220
|
+
return this.skip();
|
|
221
|
+
}
|
|
222
|
+
const protocol = '/receive-data/1.0.0';
|
|
223
|
+
const chunkSize = 1024;
|
|
224
|
+
const bytes = chunkSize * 1024 * 10;
|
|
225
|
+
const deferred = pDefer();
|
|
226
|
+
await listener.handle(protocol, ({ stream }) => {
|
|
227
|
+
Promise.resolve().then(async () => {
|
|
228
|
+
await stream.sink((async function* () {
|
|
229
|
+
for (let i = 0; i < bytes; i += chunkSize) {
|
|
230
|
+
yield new Uint8Array(chunkSize);
|
|
231
|
+
}
|
|
232
|
+
})());
|
|
233
|
+
await stream.close();
|
|
234
|
+
})
|
|
235
|
+
.catch(err => {
|
|
236
|
+
deferred.reject(err);
|
|
237
|
+
stream.abort(err);
|
|
275
238
|
});
|
|
239
|
+
});
|
|
240
|
+
const stream = await dialer.dialProtocol(dialAddrs[0], protocol);
|
|
241
|
+
await stream.closeWrite();
|
|
242
|
+
let read = 0;
|
|
243
|
+
for await (const buf of stream.source) {
|
|
244
|
+
read += buf.byteLength;
|
|
276
245
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
246
|
+
expect(read).to.equal(bytes);
|
|
247
|
+
});
|
|
248
|
+
it('can close local stream for writing and reading while a remote stream is writing', async function () {
|
|
249
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
250
|
+
if (listener == null) {
|
|
251
|
+
return this.skip();
|
|
281
252
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
253
|
+
/**
|
|
254
|
+
* NodeA NodeB
|
|
255
|
+
* | <--- STOP_SENDING |
|
|
256
|
+
* | FIN ---> |
|
|
257
|
+
* | <--- FIN |
|
|
258
|
+
* | FIN_ACK ---> |
|
|
259
|
+
* | <--- FIN_ACK |
|
|
260
|
+
*/
|
|
261
|
+
const getRemoteStream = pDefer();
|
|
262
|
+
const protocol = '/close-local-while-remote-writes/1.0.0';
|
|
263
|
+
const streamHandler = ({ stream }) => {
|
|
264
|
+
void Promise.resolve().then(async () => {
|
|
265
|
+
getRemoteStream.resolve(stream);
|
|
266
|
+
});
|
|
288
267
|
};
|
|
289
|
-
await
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
268
|
+
await listener.handle(protocol, (info) => {
|
|
269
|
+
streamHandler(info);
|
|
270
|
+
}, {
|
|
271
|
+
runOnLimitedConnection: true
|
|
272
|
+
});
|
|
273
|
+
const connection = await dialer.dial(dialAddrs[0]);
|
|
274
|
+
// open a stream on the echo protocol
|
|
275
|
+
const stream = await connection.newStream(protocol, {
|
|
276
|
+
runOnLimitedConnection: true
|
|
277
|
+
});
|
|
278
|
+
// close the write end immediately
|
|
279
|
+
const p = stream.closeWrite();
|
|
280
|
+
const remoteStream = await getRemoteStream.promise;
|
|
281
|
+
// close the readable end of the remote stream
|
|
282
|
+
await remoteStream.closeRead();
|
|
283
|
+
// keep the remote write end open, this should delay the FIN_ACK reply to the local stream
|
|
284
|
+
const remoteInputStream = pushable();
|
|
285
|
+
void remoteStream.sink(remoteInputStream);
|
|
286
|
+
// wait for remote to receive local close-write
|
|
287
|
+
await pRetry(() => {
|
|
288
|
+
if (remoteStream.readStatus !== 'closed') {
|
|
289
|
+
throw new Error('Remote stream read status ' + remoteStream.readStatus);
|
|
290
|
+
}
|
|
291
|
+
}, {
|
|
292
|
+
minTimeout: 100
|
|
293
|
+
});
|
|
294
|
+
// remote closes write
|
|
295
|
+
remoteInputStream.end();
|
|
296
|
+
// wait to receive FIN_ACK
|
|
297
|
+
await p;
|
|
298
|
+
// wait for remote to notice closure
|
|
299
|
+
await pRetry(() => {
|
|
300
|
+
if (remoteStream.status !== 'closed') {
|
|
301
|
+
throw new Error('Remote stream not closed');
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
assertStreamClosed(stream);
|
|
305
|
+
assertStreamClosed(remoteStream);
|
|
306
|
+
});
|
|
307
|
+
it('can close local stream for writing and reading while a remote stream is writing using source/sink', async function () {
|
|
308
|
+
({ dialer, listener, dialAddrs } = await getSetup(common));
|
|
309
|
+
if (listener == null) {
|
|
310
|
+
return this.skip();
|
|
294
311
|
}
|
|
295
|
-
|
|
296
|
-
|
|
312
|
+
/**
|
|
313
|
+
* NodeA NodeB
|
|
314
|
+
* | FIN ---> |
|
|
315
|
+
* | <--- FIN |
|
|
316
|
+
* | FIN_ACK ---> |
|
|
317
|
+
* | <--- FIN_ACK |
|
|
318
|
+
*/
|
|
319
|
+
const getRemoteStream = pDefer();
|
|
320
|
+
const protocol = '/close-local-while-remote-reads/1.0.0';
|
|
321
|
+
const streamHandler = ({ stream }) => {
|
|
322
|
+
void Promise.resolve().then(async () => {
|
|
323
|
+
getRemoteStream.resolve(stream);
|
|
324
|
+
});
|
|
325
|
+
};
|
|
326
|
+
await listener.handle(protocol, (info) => {
|
|
327
|
+
streamHandler(info);
|
|
328
|
+
}, {
|
|
329
|
+
runOnLimitedConnection: true
|
|
330
|
+
});
|
|
331
|
+
const connection = await dialer.dial(dialAddrs[0]);
|
|
332
|
+
// open a stream on the echo protocol
|
|
333
|
+
const stream = await connection.newStream(protocol, {
|
|
334
|
+
runOnLimitedConnection: true
|
|
297
335
|
});
|
|
336
|
+
// keep the remote write end open, this should delay the FIN_ACK reply to the local stream
|
|
337
|
+
const p = stream.sink([]);
|
|
338
|
+
const remoteStream = await getRemoteStream.promise;
|
|
339
|
+
// close the readable end of the remote stream
|
|
340
|
+
await remoteStream.closeRead();
|
|
341
|
+
// readable end should finish
|
|
342
|
+
await drain(remoteStream.source);
|
|
343
|
+
// wait for remote to receive local close-write
|
|
344
|
+
await pRetry(() => {
|
|
345
|
+
if (remoteStream.readStatus !== 'closed') {
|
|
346
|
+
throw new Error('Remote stream read status ' + remoteStream.readStatus);
|
|
347
|
+
}
|
|
348
|
+
}, {
|
|
349
|
+
minTimeout: 100
|
|
350
|
+
});
|
|
351
|
+
// remote closes write
|
|
352
|
+
await remoteStream.sink([]);
|
|
353
|
+
// wait to receive FIN_ACK
|
|
354
|
+
await p;
|
|
355
|
+
// close read end of stream
|
|
356
|
+
await stream.closeRead();
|
|
357
|
+
// readable end should finish
|
|
358
|
+
await drain(stream.source);
|
|
359
|
+
// wait for remote to notice closure
|
|
360
|
+
await pRetry(() => {
|
|
361
|
+
if (remoteStream.status !== 'closed') {
|
|
362
|
+
throw new Error('Remote stream not closed');
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
assertStreamClosed(stream);
|
|
366
|
+
assertStreamClosed(remoteStream);
|
|
298
367
|
});
|
|
299
|
-
|
|
300
|
-
|
|
368
|
+
});
|
|
369
|
+
describe('events', () => {
|
|
370
|
+
let dialer;
|
|
371
|
+
let listener;
|
|
372
|
+
let listenMultiaddrMatcher;
|
|
373
|
+
afterEach(async () => {
|
|
374
|
+
await stop(dialer, listener);
|
|
375
|
+
await common.teardown();
|
|
376
|
+
});
|
|
377
|
+
it('emits listening', async function () {
|
|
378
|
+
({ dialer, listener, listenMultiaddrMatcher } = await getSetup(common));
|
|
379
|
+
if (listener == null) {
|
|
301
380
|
return this.skip();
|
|
302
381
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
382
|
+
await listener.stop();
|
|
383
|
+
const transportListeningPromise = pDefer();
|
|
384
|
+
listener.addEventListener('transport:listening', (event) => {
|
|
385
|
+
const transportListener = event.detail;
|
|
386
|
+
if (transportListener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma))) {
|
|
387
|
+
transportListeningPromise.resolve();
|
|
388
|
+
}
|
|
307
389
|
});
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
},
|
|
312
|
-
transports: [
|
|
313
|
-
transport
|
|
314
|
-
]
|
|
390
|
+
await listener.start();
|
|
391
|
+
await raceSignal(transportListeningPromise.promise, AbortSignal.timeout(1000), {
|
|
392
|
+
errorMessage: 'Did not emit listening event'
|
|
315
393
|
});
|
|
394
|
+
});
|
|
395
|
+
it('emits close', async function () {
|
|
396
|
+
({ dialer, listener } = await getSetup(common));
|
|
397
|
+
if (listener == null) {
|
|
398
|
+
return this.skip();
|
|
399
|
+
}
|
|
316
400
|
const transportManager = getTransportManager(listener);
|
|
317
|
-
const transportListener = transportManager.getListeners()
|
|
401
|
+
const transportListener = transportManager.getListeners()
|
|
402
|
+
.filter(listener => listener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma)))
|
|
403
|
+
.pop();
|
|
404
|
+
if (transportListener == null) {
|
|
405
|
+
throw new Error('Could not find address listener');
|
|
406
|
+
}
|
|
318
407
|
const p = pEvent(transportListener, 'close');
|
|
319
408
|
await listener.stop();
|
|
320
409
|
await raceSignal(p, AbortSignal.timeout(1000), {
|
|
@@ -323,4 +412,12 @@ export default (common) => {
|
|
|
323
412
|
});
|
|
324
413
|
});
|
|
325
414
|
};
|
|
415
|
+
function assertStreamClosed(stream) {
|
|
416
|
+
expect(stream.status).to.equal('closed');
|
|
417
|
+
expect(stream.readStatus).to.equal('closed');
|
|
418
|
+
expect(stream.writeStatus).to.equal('closed');
|
|
419
|
+
expect(stream.timeline.close).to.be.a('number');
|
|
420
|
+
expect(stream.timeline.closeRead).to.be.a('number');
|
|
421
|
+
expect(stream.timeline.closeWrite).to.be.a('number');
|
|
422
|
+
}
|
|
326
423
|
//# sourceMappingURL=index.js.map
|