@libp2p/interface-compliance-tests 6.5.0-8484de8a2 → 6.5.0-87bc8d4fb

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/src/connection-encryption/index.d.ts.map +1 -1
  2. package/dist/src/connection-encryption/index.js +15 -24
  3. package/dist/src/connection-encryption/index.js.map +1 -1
  4. package/dist/src/connection-encryption/utils/index.d.ts +3 -0
  5. package/dist/src/connection-encryption/utils/index.d.ts.map +1 -0
  6. package/dist/src/connection-encryption/utils/index.js +21 -0
  7. package/dist/src/connection-encryption/utils/index.js.map +1 -0
  8. package/dist/src/matchers.d.ts +12 -0
  9. package/dist/src/matchers.d.ts.map +1 -0
  10. package/dist/src/matchers.js +14 -0
  11. package/dist/src/matchers.js.map +1 -0
  12. package/dist/src/mocks/connection-manager.d.ts +27 -0
  13. package/dist/src/mocks/connection-manager.d.ts.map +1 -0
  14. package/dist/src/mocks/connection-manager.js +147 -0
  15. package/dist/src/mocks/connection-manager.js.map +1 -0
  16. package/dist/src/mocks/connection.d.ts +41 -0
  17. package/dist/src/mocks/connection.d.ts.map +1 -0
  18. package/dist/src/mocks/connection.js +236 -0
  19. package/dist/src/mocks/connection.js.map +1 -0
  20. package/dist/src/mocks/duplex.d.ts +4 -0
  21. package/dist/src/mocks/duplex.d.ts.map +1 -0
  22. package/dist/src/mocks/duplex.js +9 -0
  23. package/dist/src/mocks/duplex.js.map +1 -0
  24. package/dist/src/mocks/index.d.ts +12 -0
  25. package/dist/src/mocks/index.d.ts.map +1 -0
  26. package/dist/src/mocks/index.js +8 -0
  27. package/dist/src/mocks/index.js.map +1 -0
  28. package/dist/src/mocks/multiaddr-connection.d.ts +17 -0
  29. package/dist/src/mocks/multiaddr-connection.d.ts.map +1 -0
  30. package/dist/src/mocks/multiaddr-connection.js +64 -0
  31. package/dist/src/mocks/multiaddr-connection.js.map +1 -0
  32. package/dist/src/mocks/muxer.d.ts +36 -0
  33. package/dist/src/mocks/muxer.d.ts.map +1 -0
  34. package/dist/src/mocks/muxer.js +234 -0
  35. package/dist/src/mocks/muxer.js.map +1 -0
  36. package/dist/src/mocks/registrar.d.ts +16 -0
  37. package/dist/src/mocks/registrar.d.ts.map +1 -0
  38. package/dist/src/mocks/registrar.js +66 -0
  39. package/dist/src/mocks/registrar.js.map +1 -0
  40. package/dist/src/mocks/upgrader.d.ts +9 -0
  41. package/dist/src/mocks/upgrader.d.ts.map +1 -0
  42. package/dist/src/mocks/upgrader.js +46 -0
  43. package/dist/src/mocks/upgrader.js.map +1 -0
  44. package/dist/src/pubsub/api.d.ts +6 -0
  45. package/dist/src/pubsub/api.d.ts.map +1 -0
  46. package/dist/src/pubsub/api.js +88 -0
  47. package/dist/src/pubsub/api.js.map +1 -0
  48. package/dist/src/pubsub/connection-handlers.d.ts +6 -0
  49. package/dist/src/pubsub/connection-handlers.d.ts.map +1 -0
  50. package/dist/src/pubsub/connection-handlers.js +329 -0
  51. package/dist/src/pubsub/connection-handlers.js.map +1 -0
  52. package/dist/src/pubsub/emit-self.d.ts +6 -0
  53. package/dist/src/pubsub/emit-self.d.ts.map +1 -0
  54. package/dist/src/pubsub/emit-self.js +80 -0
  55. package/dist/src/pubsub/emit-self.js.map +1 -0
  56. package/dist/src/pubsub/index.d.ts +18 -0
  57. package/dist/src/pubsub/index.d.ts.map +1 -0
  58. package/dist/src/pubsub/index.js +17 -0
  59. package/dist/src/pubsub/index.js.map +1 -0
  60. package/dist/src/pubsub/messages.d.ts +6 -0
  61. package/dist/src/pubsub/messages.d.ts.map +1 -0
  62. package/dist/src/pubsub/messages.js +48 -0
  63. package/dist/src/pubsub/messages.js.map +1 -0
  64. package/dist/src/pubsub/multiple-nodes.d.ts +6 -0
  65. package/dist/src/pubsub/multiple-nodes.d.ts.map +1 -0
  66. package/dist/src/pubsub/multiple-nodes.js +350 -0
  67. package/dist/src/pubsub/multiple-nodes.js.map +1 -0
  68. package/dist/src/pubsub/two-nodes.d.ts +6 -0
  69. package/dist/src/pubsub/two-nodes.d.ts.map +1 -0
  70. package/dist/src/pubsub/two-nodes.js +216 -0
  71. package/dist/src/pubsub/two-nodes.js.map +1 -0
  72. package/dist/src/pubsub/utils.d.ts +5 -0
  73. package/dist/src/pubsub/utils.d.ts.map +1 -0
  74. package/dist/src/pubsub/utils.js +27 -0
  75. package/dist/src/pubsub/utils.js.map +1 -0
  76. package/dist/src/stream-muxer/base-test.d.ts.map +1 -1
  77. package/dist/src/stream-muxer/base-test.js +345 -62
  78. package/dist/src/stream-muxer/base-test.js.map +1 -1
  79. package/dist/src/stream-muxer/close-test.d.ts.map +1 -1
  80. package/dist/src/stream-muxer/close-test.js +320 -253
  81. package/dist/src/stream-muxer/close-test.js.map +1 -1
  82. package/dist/src/stream-muxer/index.js +2 -2
  83. package/dist/src/stream-muxer/index.js.map +1 -1
  84. package/dist/src/stream-muxer/{stream-test.d.ts → mega-stress-test.d.ts} +2 -2
  85. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +1 -0
  86. package/dist/src/stream-muxer/mega-stress-test.js +11 -0
  87. package/dist/src/stream-muxer/mega-stress-test.js.map +1 -0
  88. package/dist/src/stream-muxer/spawner.d.ts +4 -0
  89. package/dist/src/stream-muxer/spawner.d.ts.map +1 -0
  90. package/dist/src/stream-muxer/spawner.js +39 -0
  91. package/dist/src/stream-muxer/spawner.js.map +1 -0
  92. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -1
  93. package/dist/src/stream-muxer/stress-test.js +16 -70
  94. package/dist/src/stream-muxer/stress-test.js.map +1 -1
  95. package/dist/src/transport/index.d.ts.map +1 -1
  96. package/dist/src/transport/index.js +203 -232
  97. package/dist/src/transport/index.js.map +1 -1
  98. package/dist/src/transport/utils.js +2 -2
  99. package/dist/src/transport/utils.js.map +1 -1
  100. package/package.json +51 -23
  101. package/src/connection-encryption/index.ts +20 -27
  102. package/src/connection-encryption/utils/index.ts +27 -0
  103. package/src/matchers.ts +18 -0
  104. package/src/mocks/connection-manager.ts +216 -0
  105. package/src/mocks/connection.ts +309 -0
  106. package/src/mocks/duplex.ts +11 -0
  107. package/src/mocks/index.ts +11 -0
  108. package/src/mocks/multiaddr-connection.ts +80 -0
  109. package/src/mocks/muxer.ts +331 -0
  110. package/src/mocks/registrar.ts +86 -0
  111. package/src/mocks/upgrader.ts +65 -0
  112. package/src/pubsub/api.ts +116 -0
  113. package/src/pubsub/connection-handlers.ts +413 -0
  114. package/src/pubsub/emit-self.ts +99 -0
  115. package/src/pubsub/index.ts +34 -0
  116. package/src/pubsub/messages.ts +59 -0
  117. package/src/pubsub/multiple-nodes.ts +440 -0
  118. package/src/pubsub/two-nodes.ts +272 -0
  119. package/src/pubsub/utils.ts +34 -0
  120. package/src/stream-muxer/base-test.ts +413 -75
  121. package/src/stream-muxer/close-test.ts +343 -304
  122. package/src/stream-muxer/index.ts +2 -2
  123. package/src/stream-muxer/mega-stress-test.ts +14 -0
  124. package/src/stream-muxer/spawner.ts +57 -0
  125. package/src/stream-muxer/stress-test.ts +18 -92
  126. package/src/transport/index.ts +241 -280
  127. package/src/transport/utils.ts +2 -2
  128. package/dist/src/stream-muxer/stream-test.d.ts.map +0 -1
  129. package/dist/src/stream-muxer/stream-test.js +0 -289
  130. package/dist/src/stream-muxer/stream-test.js.map +0 -1
  131. package/src/stream-muxer/stream-test.ts +0 -380
@@ -1,102 +1,40 @@
1
- import { stop, TimeoutError } from '@libp2p/interface';
2
- import { prefixLogger } from '@libp2p/logger';
1
+ import { stop } from '@libp2p/interface';
3
2
  import { expect } from 'aegir/chai';
4
3
  import delay from 'delay';
5
- import all from 'it-all';
6
4
  import drain from 'it-drain';
7
5
  import { pushable } from 'it-pushable';
6
+ import pDefer from 'p-defer';
8
7
  import { pEvent } from 'p-event';
9
8
  import pRetry from 'p-retry';
10
9
  import pWaitFor from 'p-wait-for';
11
10
  import { raceSignal } from 'race-signal';
12
- import { Uint8ArrayList } from 'uint8arraylist';
13
11
  import { isValidTick } from '../is-valid-tick.js';
14
12
  import { createPeer, getTransportManager, getUpgrader } from './utils.js';
15
13
  async function getSetup(common) {
16
14
  const setup = await common.setup();
17
- const dialer = await createPeer({
18
- logger: prefixLogger('dialer'),
19
- ...setup.dialer
20
- });
15
+ const dialer = await createPeer(setup.dialer);
21
16
  let listener;
22
17
  if (setup.listener != null) {
23
- listener = await createPeer({
24
- logger: prefixLogger('listener'),
25
- ...setup.listener
26
- });
18
+ listener = await createPeer(setup.listener);
27
19
  }
28
- let dialAddrs = listener?.getMultiaddrs() ?? setup.dialAddrs;
20
+ const dialAddrs = listener?.getMultiaddrs() ?? setup.dialAddrs;
29
21
  if (dialAddrs == null) {
30
22
  throw new Error('Listener config or dial addresses must be specified');
31
23
  }
32
- dialAddrs = dialAddrs.filter(ma => setup.dialMultiaddrMatcher.exactMatch(ma));
33
- if (dialAddrs.length === 0) {
34
- throw new Error('Listener was not listening on any addresses that the dialMultiaddrMatcher matched');
35
- }
36
24
  return {
37
25
  dialer,
38
26
  listener,
39
- dialAddrs,
27
+ dialAddrs: dialAddrs.filter(ma => setup.dialMultiaddrMatcher.exactMatch(ma)),
40
28
  dialMultiaddrMatcher: setup.dialMultiaddrMatcher,
41
29
  listenMultiaddrMatcher: setup.listenMultiaddrMatcher
42
30
  };
43
31
  }
44
32
  export default (common) => {
45
- describe('transport events', () => {
46
- let dialer;
47
- let listener;
48
- let listenMultiaddrMatcher;
49
- afterEach(async () => {
50
- await stop(dialer, listener);
51
- await common.teardown();
52
- });
53
- it('emits listening', async function () {
54
- ({ dialer, listener, listenMultiaddrMatcher } = await getSetup(common));
55
- if (listener == null) {
56
- return this.skip();
57
- }
58
- await listener.stop();
59
- const transportListeningPromise = Promise.withResolvers();
60
- listener.addEventListener('transport:listening', (event) => {
61
- const transportListener = event.detail;
62
- if (transportListener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma))) {
63
- transportListeningPromise.resolve();
64
- }
65
- });
66
- await listener.start();
67
- await raceSignal(transportListeningPromise.promise, AbortSignal.timeout(1000), {
68
- translateError: () => {
69
- return new TimeoutError('Did not emit listening event');
70
- }
71
- });
72
- });
73
- it('emits close', async function () {
74
- ({ dialer, listener } = await getSetup(common));
75
- if (listener == null) {
76
- return this.skip();
77
- }
78
- const transportManager = getTransportManager(listener);
79
- const transportListener = transportManager.getListeners()
80
- .filter(listener => listener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma)))
81
- .pop();
82
- if (transportListener == null) {
83
- throw new Error('Could not find address listener');
84
- }
85
- const p = pEvent(transportListener, 'close');
86
- await listener.stop();
87
- await raceSignal(p, AbortSignal.timeout(1000), {
88
- translateError: () => {
89
- return new TimeoutError('Did not emit close event');
90
- }
91
- });
92
- });
93
- });
94
33
  describe('interface-transport', () => {
95
34
  let dialer;
96
35
  let listener;
97
36
  let dialAddrs;
98
37
  let dialMultiaddrMatcher;
99
- let listenMultiaddrMatcher;
100
38
  afterEach(async () => {
101
39
  await stop(dialer, listener);
102
40
  await common.teardown();
@@ -107,20 +45,17 @@ export default (common) => {
107
45
  const output = await dialer.services.echo.echo(dialAddrs[0], input, {
108
46
  signal: AbortSignal.timeout(5000)
109
47
  });
110
- expect(output.subarray()).to.equalBytes(input);
48
+ expect(output).to.equalBytes(input);
111
49
  });
112
50
  it('should listen on multiple addresses', async () => {
113
51
  ({ dialer, listener, dialAddrs } = await getSetup(common));
114
52
  const input = Uint8Array.from([0, 1, 2, 3, 4]);
115
- const output1 = await dialer.services.echo.echo(dialAddrs[0], input, {
53
+ await expect(dialer.services.echo.echo(dialAddrs[0], input, {
116
54
  signal: AbortSignal.timeout(5000)
117
- });
118
- expect(output1.subarray()).to.equalBytes(input);
119
- const output2 = await dialer.services.echo.echo(dialAddrs[1], input, {
120
- signal: AbortSignal.timeout(5000),
121
- force: true
122
- });
123
- expect(output2.subarray()).to.equalBytes(input);
55
+ })).to.eventually.deep.equal(input);
56
+ await expect(dialer.services.echo.echo(dialAddrs[1], input, {
57
+ signal: AbortSignal.timeout(5000)
58
+ })).to.eventually.deep.equal(input);
124
59
  });
125
60
  it('can close connections', async () => {
126
61
  ({ dialer, listener, dialAddrs } = await getSetup(common));
@@ -140,15 +75,12 @@ export default (common) => {
140
75
  .with.property('name', 'AbortError');
141
76
  });
142
77
  it('should close all streams when the connection closes', async () => {
143
- ({ dialer, listener, dialAddrs, listenMultiaddrMatcher } = await getSetup(common));
78
+ ({ dialer, listener, dialAddrs } = await getSetup(common));
144
79
  let incomingConnectionPromise;
145
80
  if (listener != null) {
146
- incomingConnectionPromise = Promise.withResolvers();
81
+ incomingConnectionPromise = pDefer();
147
82
  listener.addEventListener('connection:open', (event) => {
148
83
  const conn = event.detail;
149
- if (!listenMultiaddrMatcher.matches(conn.remoteAddr)) {
150
- return;
151
- }
152
84
  if (conn.remotePeer.equals(dialer.peerId)) {
153
85
  incomingConnectionPromise?.resolve(conn);
154
86
  }
@@ -164,24 +96,18 @@ export default (common) => {
164
96
  maxOutboundStreams: 5
165
97
  });
166
98
  }
167
- expect(connection).to.have.property('streams').that.has.lengthOf(5);
99
+ const streams = connection.streams;
100
+ // Close the connection and verify all streams have been closed
101
+ await connection.close();
102
+ await pWaitFor(() => connection.streams.length === 0, {
103
+ timeout: 5000
104
+ });
168
105
  if (remoteConn != null) {
169
- await pWaitFor(() => remoteConn.streams.length === 5, {
106
+ await pWaitFor(() => remoteConn.streams.length === 0, {
170
107
  timeout: 5000
171
108
  });
172
109
  }
173
- // Close the connection and verify all streams have been closed
174
- await Promise.all([
175
- pEvent(connection, 'close'),
176
- pEvent(remoteConn ?? connection, 'close'),
177
- connection.close()
178
- ]);
179
- expect(connection).to.have.property('status', 'closed');
180
- expect(connection).to.have.property('streams').that.is.empty();
181
- if (remoteConn != null) {
182
- expect(remoteConn).to.have.property('status', 'closed');
183
- expect(remoteConn).to.have.property('streams').that.is.empty();
184
- }
110
+ expect(streams.find(stream => stream.status === 'open')).to.be.undefined();
185
111
  });
186
112
  it('should not handle connection if upgradeInbound rejects', async function () {
187
113
  ({ dialer, listener, dialAddrs, dialMultiaddrMatcher } = await getSetup(common));
@@ -222,219 +148,262 @@ export default (common) => {
222
148
  }
223
149
  }
224
150
  });
225
- it('should handle one small write', async function () {
226
- const timeout = 120_000;
227
- this.timeout(timeout);
228
- ({ dialer, listener, dialAddrs } = await getSetup(common));
229
- const connection = await dialer.dial(dialAddrs[0]);
230
- const input = new Uint8Array(1024).fill(5);
231
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
232
- signal: AbortSignal.timeout(timeout)
233
- });
234
- expect(output.subarray()).to.equalBytes(input);
235
- expect(connection.streams.filter(s => s.protocol === dialer.services.echo.protocol)).to.be.empty();
236
- });
237
151
  it('should handle one big write', async function () {
238
- const timeout = 540_000;
152
+ const timeout = 120_000;
239
153
  this.timeout(timeout);
240
154
  ({ dialer, listener, dialAddrs } = await getSetup(common));
241
- const connection = await dialer.dial(dialAddrs[0]);
242
155
  const input = new Uint8Array(1024 * 1024 * 10).fill(5);
243
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
156
+ const output = await dialer.services.echo.echo(dialAddrs[0], input, {
244
157
  signal: AbortSignal.timeout(timeout)
245
158
  });
246
- expect(output.subarray()).to.equalBytes(input);
247
- expect(connection.streams.filter(s => s.protocol === dialer.services.echo.protocol)).to.be.empty();
159
+ expect(output).to.equalBytes(input);
248
160
  });
249
161
  it('should handle many small writes', async function () {
250
162
  const timeout = 360_000;
251
163
  this.timeout(timeout);
252
164
  ({ dialer, listener, dialAddrs } = await getSetup(common));
253
- const connection = await dialer.dial(dialAddrs[0]);
254
- const echoProtocol = dialer.services.echo.protocol;
255
165
  for (let i = 0; i < 2000; i++) {
256
166
  const input = new Uint8Array(1024).fill(5);
257
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
167
+ const output = await dialer.services.echo.echo(dialAddrs[0], input, {
258
168
  signal: AbortSignal.timeout(timeout)
259
169
  });
260
- expect(output.subarray()).to.equalBytes(input);
261
- expect(connection.streams.filter(s => s.protocol === echoProtocol)).to.be.empty();
170
+ expect(output).to.equalBytes(input);
171
+ }
172
+ });
173
+ it('can close a stream for reading but send a large amount of data', async function () {
174
+ const timeout = 120_000;
175
+ this.timeout(timeout);
176
+ ({ dialer, listener, dialAddrs } = await getSetup(common));
177
+ if (listener == null) {
178
+ return this.skip();
179
+ }
180
+ const protocol = '/send-data/1.0.0';
181
+ const chunkSize = 1024;
182
+ const bytes = chunkSize * 1024 * 10;
183
+ const deferred = pDefer();
184
+ await listener.handle(protocol, ({ stream }) => {
185
+ Promise.resolve().then(async () => {
186
+ let read = 0;
187
+ for await (const buf of stream.source) {
188
+ read += buf.byteLength;
189
+ if (read === bytes) {
190
+ deferred.resolve();
191
+ break;
192
+ }
193
+ }
194
+ })
195
+ .catch(err => {
196
+ deferred.reject(err);
197
+ stream.abort(err);
198
+ });
199
+ });
200
+ const stream = await dialer.dialProtocol(dialAddrs[0], protocol);
201
+ await stream.closeRead();
202
+ await stream.sink((async function* () {
203
+ for (let i = 0; i < bytes; i += chunkSize) {
204
+ yield new Uint8Array(chunkSize);
205
+ }
206
+ })());
207
+ await stream.close();
208
+ await deferred.promise;
209
+ });
210
+ it('can close a stream for writing but receive a large amount of data', async function () {
211
+ const timeout = 120_000;
212
+ this.timeout(timeout);
213
+ ({ dialer, listener, dialAddrs } = await getSetup(common));
214
+ if (listener == null) {
215
+ return this.skip();
216
+ }
217
+ const protocol = '/receive-data/1.0.0';
218
+ const chunkSize = 1024;
219
+ const bytes = chunkSize * 1024 * 10;
220
+ const deferred = pDefer();
221
+ await listener.handle(protocol, ({ stream }) => {
222
+ Promise.resolve().then(async () => {
223
+ await stream.sink((async function* () {
224
+ for (let i = 0; i < bytes; i += chunkSize) {
225
+ yield new Uint8Array(chunkSize);
226
+ }
227
+ })());
228
+ await stream.close();
229
+ })
230
+ .catch(err => {
231
+ deferred.reject(err);
232
+ stream.abort(err);
233
+ });
234
+ });
235
+ const stream = await dialer.dialProtocol(dialAddrs[0], protocol);
236
+ await stream.closeWrite();
237
+ let read = 0;
238
+ for await (const buf of stream.source) {
239
+ read += buf.byteLength;
262
240
  }
241
+ expect(read).to.equal(bytes);
263
242
  });
264
- it('can close local stream while a remote stream is writing', async function () {
243
+ it('can close local stream for writing and reading while a remote stream is writing', async function () {
265
244
  ({ dialer, listener, dialAddrs } = await getSetup(common));
266
245
  if (listener == null) {
267
246
  return this.skip();
268
247
  }
269
- // 1. remote stream close read
270
- // 2. local stream close write
271
- // 3. remote stream close write
272
248
  /**
273
249
  * NodeA NodeB
274
250
  * | <--- STOP_SENDING |
275
251
  * | FIN ---> |
276
- * | <--- DATA |
277
252
  * | <--- FIN |
278
253
  * | FIN_ACK ---> |
279
254
  * | <--- FIN_ACK |
280
255
  */
281
- const getRemoteStream = Promise.withResolvers();
256
+ const getRemoteStream = pDefer();
282
257
  const protocol = '/close-local-while-remote-writes/1.0.0';
283
- const streamHandler = (stream) => {
284
- getRemoteStream.resolve(stream);
258
+ const streamHandler = ({ stream }) => {
259
+ void Promise.resolve().then(async () => {
260
+ getRemoteStream.resolve(stream);
261
+ });
285
262
  };
286
- await listener.handle(protocol, streamHandler, {
263
+ await listener.handle(protocol, (info) => {
264
+ streamHandler(info);
265
+ }, {
287
266
  runOnLimitedConnection: true
288
267
  });
289
268
  const connection = await dialer.dial(dialAddrs[0]);
290
- const [localStream, remoteStream] = await Promise.all([
291
- // open a stream on the echo protocol
292
- connection.newStream(protocol, {
293
- runOnLimitedConnection: true
294
- }),
295
- getRemoteStream.promise
296
- ]);
297
- // ignore incoming data
298
- void drain(localStream);
299
- // close the remote readable end
269
+ // open a stream on the echo protocol
270
+ const stream = await connection.newStream(protocol, {
271
+ runOnLimitedConnection: true
272
+ });
273
+ // close the write end immediately
274
+ const p = stream.closeWrite();
275
+ const remoteStream = await getRemoteStream.promise;
276
+ // close the readable end of the remote stream
300
277
  await remoteStream.closeRead();
301
- // send data from the remote to the local
278
+ // keep the remote write end open, this should delay the FIN_ACK reply to the local stream
302
279
  const remoteInputStream = pushable();
303
- Promise.resolve().then(async () => {
304
- for await (const buf of remoteInputStream) {
305
- const sendMore = remoteStream.send(buf);
306
- if (sendMore === false) {
307
- await pEvent(remoteStream, 'drain', {
308
- rejectionEvents: [
309
- 'close'
310
- ]
311
- });
312
- }
280
+ void remoteStream.sink(remoteInputStream);
281
+ // wait for remote to receive local close-write
282
+ await pRetry(() => {
283
+ if (remoteStream.readStatus !== 'closed') {
284
+ throw new Error('Remote stream read status ' + remoteStream.readStatus);
313
285
  }
314
- // close the remote writable end
315
- await remoteStream.close();
286
+ }, {
287
+ minTimeout: 100
316
288
  });
317
- await Promise.all([
318
- // wait for remote to receive local FIN
319
- pEvent(remoteStream, 'remoteCloseWrite'),
320
- // wait to receive FIN_ACK
321
- localStream.close()
322
- ]);
323
- // stop sending remote -> local
289
+ // remote closes write
324
290
  remoteInputStream.end();
291
+ // wait to receive FIN_ACK
292
+ await p;
325
293
  // wait for remote to notice closure
326
294
  await pRetry(() => {
327
295
  if (remoteStream.status !== 'closed') {
328
296
  throw new Error('Remote stream not closed');
329
297
  }
330
298
  });
331
- // both ends should be closed
332
- assertStreamClosed(localStream);
299
+ assertStreamClosed(stream);
333
300
  assertStreamClosed(remoteStream);
334
301
  });
335
- it('can close local stream for writing while a remote stream is reading', async function () {
302
+ it('can close local stream for writing and reading while a remote stream is writing using source/sink', async function () {
336
303
  ({ dialer, listener, dialAddrs } = await getSetup(common));
337
304
  if (listener == null) {
338
305
  return this.skip();
339
306
  }
340
307
  /**
341
308
  * NodeA NodeB
342
- * | DATA ---> |
343
309
  * | FIN ---> |
344
310
  * | <--- FIN |
345
311
  * | FIN_ACK ---> |
346
312
  * | <--- FIN_ACK |
347
313
  */
348
- const getRemoteStream = Promise.withResolvers();
314
+ const getRemoteStream = pDefer();
349
315
  const protocol = '/close-local-while-remote-reads/1.0.0';
350
- const streamHandler = (stream) => {
351
- getRemoteStream.resolve(stream);
316
+ const streamHandler = ({ stream }) => {
317
+ void Promise.resolve().then(async () => {
318
+ getRemoteStream.resolve(stream);
319
+ });
352
320
  };
353
- await listener.handle(protocol, streamHandler, {
321
+ await listener.handle(protocol, (info) => {
322
+ streamHandler(info);
323
+ }, {
354
324
  runOnLimitedConnection: true
355
325
  });
356
326
  const connection = await dialer.dial(dialAddrs[0]);
357
- const [localStream, remoteStream] = await Promise.all([
358
- // open a stream on the echo protocol
359
- connection.newStream(protocol, {
360
- runOnLimitedConnection: true
361
- }),
362
- getRemoteStream.promise
363
- ]);
364
- // ignore incoming data
365
- void drain(remoteStream);
366
- // close the remote stream writable end when the local sends a FIN
367
- remoteStream.addEventListener('remoteCloseWrite', () => {
368
- remoteStream.close()
369
- .catch(err => {
370
- remoteStream.abort(err);
371
- });
327
+ // open a stream on the echo protocol
328
+ const stream = await connection.newStream(protocol, {
329
+ runOnLimitedConnection: true
372
330
  });
373
- // send data to the remote then close the stream
374
- const data = [
375
- Uint8Array.from([0, 1, 2, 3]),
376
- Uint8Array.from([4, 5, 6, 7]),
377
- Uint8Array.from([8, 9, 0, 1])
378
- ];
379
- const [remoteCloseEvent, localCloseEvent] = await Promise.all([
380
- // wait for the remote to close
381
- pEvent(remoteStream, 'close'),
382
- pEvent(localStream, 'close'),
383
- (async () => {
384
- for (const buf of data) {
385
- if (!localStream.send(buf)) {
386
- await pEvent(localStream, 'drain', {
387
- rejectionEvents: [
388
- 'close'
389
- ]
390
- });
391
- }
392
- }
393
- // close the local writable end
394
- await localStream.close();
395
- })()
396
- ]);
397
- expect(remoteCloseEvent).to.not.have.property('error', 'remote stream did not close cleanly');
398
- expect(localCloseEvent).to.not.have.property('error', 'local stream did not close cleanly');
331
+ // keep the remote write end open, this should delay the FIN_ACK reply to the local stream
332
+ const p = stream.sink([]);
333
+ const remoteStream = await getRemoteStream.promise;
334
+ // close the readable end of the remote stream
335
+ await remoteStream.closeRead();
336
+ // readable end should finish
337
+ await drain(remoteStream.source);
338
+ // wait for remote to receive local close-write
339
+ await pRetry(() => {
340
+ if (remoteStream.readStatus !== 'closed') {
341
+ throw new Error('Remote stream read status ' + remoteStream.readStatus);
342
+ }
343
+ }, {
344
+ minTimeout: 100
345
+ });
346
+ // remote closes write
347
+ await remoteStream.sink([]);
348
+ // wait to receive FIN_ACK
349
+ await p;
350
+ // close read end of stream
351
+ await stream.closeRead();
352
+ // readable end should finish
353
+ await drain(stream.source);
399
354
  // wait for remote to notice closure
400
355
  await pRetry(() => {
401
356
  if (remoteStream.status !== 'closed') {
402
357
  throw new Error('Remote stream not closed');
403
358
  }
404
359
  });
405
- // both ends should be closed
406
- assertStreamClosed(localStream);
360
+ assertStreamClosed(stream);
407
361
  assertStreamClosed(remoteStream);
408
362
  });
409
- it('can close a stream for writing but receive a large amount of data', async function () {
410
- const timeout = 120_000;
411
- this.timeout(timeout);
412
- ({ dialer, listener, dialAddrs } = await getSetup(common));
363
+ });
364
+ describe('events', () => {
365
+ let dialer;
366
+ let listener;
367
+ let listenMultiaddrMatcher;
368
+ afterEach(async () => {
369
+ await stop(dialer, listener);
370
+ await common.teardown();
371
+ });
372
+ it('emits listening', async function () {
373
+ ({ dialer, listener, listenMultiaddrMatcher } = await getSetup(common));
413
374
  if (listener == null) {
414
375
  return this.skip();
415
376
  }
416
- const protocol = '/receive-data/1.0.0';
417
- const chunkSize = 1024;
418
- const bytes = chunkSize * 1024 * 10;
419
- await listener.handle(protocol, async (stream) => {
420
- for (let i = 0; i < bytes; i += chunkSize) {
421
- const sendMore = stream.send(new Uint8Array(chunkSize));
422
- if (!sendMore) {
423
- await pEvent(stream, 'drain', {
424
- rejectionEvents: [
425
- 'close'
426
- ]
427
- });
428
- }
377
+ await listener.stop();
378
+ const transportListeningPromise = pDefer();
379
+ listener.addEventListener('transport:listening', (event) => {
380
+ const transportListener = event.detail;
381
+ if (transportListener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma))) {
382
+ transportListeningPromise.resolve();
429
383
  }
430
- await stream.close();
431
384
  });
432
- const stream = await dialer.dialProtocol(dialAddrs[0], protocol);
433
- const [output] = await Promise.all([
434
- all(stream),
435
- stream.close()
436
- ]);
437
- expect(new Uint8ArrayList(...output).byteLength).to.equal(bytes);
385
+ await listener.start();
386
+ await raceSignal(transportListeningPromise.promise, AbortSignal.timeout(1000), {
387
+ errorMessage: 'Did not emit listening event'
388
+ });
389
+ });
390
+ it('emits close', async function () {
391
+ ({ dialer, listener } = await getSetup(common));
392
+ if (listener == null) {
393
+ return this.skip();
394
+ }
395
+ const transportManager = getTransportManager(listener);
396
+ const transportListener = transportManager.getListeners()
397
+ .filter(listener => listener.getAddrs().some(ma => listenMultiaddrMatcher.exactMatch(ma)))
398
+ .pop();
399
+ if (transportListener == null) {
400
+ throw new Error('Could not find address listener');
401
+ }
402
+ const p = pEvent(transportListener, 'close');
403
+ await listener.stop();
404
+ await raceSignal(p, AbortSignal.timeout(1000), {
405
+ errorMessage: 'Did not emit close event'
406
+ });
438
407
  });
439
408
  });
440
409
  };
@@ -443,5 +412,7 @@ function assertStreamClosed(stream) {
443
412
  expect(stream.readStatus).to.equal('closed');
444
413
  expect(stream.writeStatus).to.equal('closed');
445
414
  expect(stream.timeline.close).to.be.a('number');
415
+ expect(stream.timeline.closeRead).to.be.a('number');
416
+ expect(stream.timeline.closeWrite).to.be.a('number');
446
417
  }
447
418
  //# sourceMappingURL=index.js.map