@libp2p/interface-compliance-tests 6.5.0-6059227cb → 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 (132) 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 -254
  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 +205 -235
  97. package/dist/src/transport/index.js.map +1 -1
  98. package/dist/src/transport/utils.d.ts.map +1 -1
  99. package/dist/src/transport/utils.js +2 -3
  100. package/dist/src/transport/utils.js.map +1 -1
  101. package/package.json +51 -23
  102. package/src/connection-encryption/index.ts +20 -27
  103. package/src/connection-encryption/utils/index.ts +27 -0
  104. package/src/matchers.ts +18 -0
  105. package/src/mocks/connection-manager.ts +216 -0
  106. package/src/mocks/connection.ts +309 -0
  107. package/src/mocks/duplex.ts +11 -0
  108. package/src/mocks/index.ts +11 -0
  109. package/src/mocks/multiaddr-connection.ts +80 -0
  110. package/src/mocks/muxer.ts +331 -0
  111. package/src/mocks/registrar.ts +86 -0
  112. package/src/mocks/upgrader.ts +65 -0
  113. package/src/pubsub/api.ts +116 -0
  114. package/src/pubsub/connection-handlers.ts +413 -0
  115. package/src/pubsub/emit-self.ts +99 -0
  116. package/src/pubsub/index.ts +34 -0
  117. package/src/pubsub/messages.ts +59 -0
  118. package/src/pubsub/multiple-nodes.ts +440 -0
  119. package/src/pubsub/two-nodes.ts +272 -0
  120. package/src/pubsub/utils.ts +34 -0
  121. package/src/stream-muxer/base-test.ts +413 -75
  122. package/src/stream-muxer/close-test.ts +343 -305
  123. package/src/stream-muxer/index.ts +2 -2
  124. package/src/stream-muxer/mega-stress-test.ts +14 -0
  125. package/src/stream-muxer/spawner.ts +57 -0
  126. package/src/stream-muxer/stress-test.ts +18 -92
  127. package/src/transport/index.ts +241 -281
  128. package/src/transport/utils.ts +2 -3
  129. package/dist/src/stream-muxer/stream-test.d.ts.map +0 -1
  130. package/dist/src/stream-muxer/stream-test.js +0 -290
  131. package/dist/src/stream-muxer/stream-test.js.map +0 -1
  132. package/src/stream-muxer/stream-test.ts +0 -381
@@ -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,25 +96,18 @@ export default (common) => {
164
96
  maxOutboundStreams: 5
165
97
  });
166
98
  }
167
- expect(connection).to.have.property('streams').that.has.lengthOf(5);
168
- if (remoteConn != null) {
169
- await pWaitFor(() => remoteConn.streams.filter(s => s.protocol === '/echo/1.0.0').length === 5, {
170
- timeout: 5_000,
171
- interval: 1_000
172
- });
173
- }
99
+ const streams = connection.streams;
174
100
  // Close the connection and verify all streams have been closed
175
- await Promise.all([
176
- pEvent(connection, 'close'),
177
- pEvent(remoteConn ?? connection, 'close'),
178
- connection.close()
179
- ]);
180
- expect(connection).to.have.property('status', 'closed');
181
- expect(connection).to.have.property('streams').that.is.empty();
101
+ await connection.close();
102
+ await pWaitFor(() => connection.streams.length === 0, {
103
+ timeout: 5000
104
+ });
182
105
  if (remoteConn != null) {
183
- expect(remoteConn).to.have.property('status', 'closed');
184
- expect(remoteConn).to.have.property('streams').that.is.empty();
106
+ await pWaitFor(() => remoteConn.streams.length === 0, {
107
+ timeout: 5000
108
+ });
185
109
  }
110
+ expect(streams.find(stream => stream.status === 'open')).to.be.undefined();
186
111
  });
187
112
  it('should not handle connection if upgradeInbound rejects', async function () {
188
113
  ({ dialer, listener, dialAddrs, dialMultiaddrMatcher } = await getSetup(common));
@@ -223,219 +148,262 @@ export default (common) => {
223
148
  }
224
149
  }
225
150
  });
226
- it('should handle one small write', async function () {
227
- const timeout = 120_000;
228
- this.timeout(timeout);
229
- ({ dialer, listener, dialAddrs } = await getSetup(common));
230
- const connection = await dialer.dial(dialAddrs[0]);
231
- const input = new Uint8Array(1024).fill(5);
232
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
233
- signal: AbortSignal.timeout(timeout)
234
- });
235
- expect(output.subarray()).to.equalBytes(input);
236
- expect(connection.streams.filter(s => s.protocol === dialer.services.echo.protocol)).to.be.empty();
237
- });
238
151
  it('should handle one big write', async function () {
239
- const timeout = 540_000;
152
+ const timeout = 120_000;
240
153
  this.timeout(timeout);
241
154
  ({ dialer, listener, dialAddrs } = await getSetup(common));
242
- const connection = await dialer.dial(dialAddrs[0]);
243
155
  const input = new Uint8Array(1024 * 1024 * 10).fill(5);
244
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
156
+ const output = await dialer.services.echo.echo(dialAddrs[0], input, {
245
157
  signal: AbortSignal.timeout(timeout)
246
158
  });
247
- expect(output.subarray()).to.equalBytes(input);
248
- expect(connection.streams.filter(s => s.protocol === dialer.services.echo.protocol)).to.be.empty();
159
+ expect(output).to.equalBytes(input);
249
160
  });
250
161
  it('should handle many small writes', async function () {
251
162
  const timeout = 360_000;
252
163
  this.timeout(timeout);
253
164
  ({ dialer, listener, dialAddrs } = await getSetup(common));
254
- const connection = await dialer.dial(dialAddrs[0]);
255
- const echoProtocol = dialer.services.echo.protocol;
256
- for (let i = 0; i < 2_000; i++) {
165
+ for (let i = 0; i < 2000; i++) {
257
166
  const input = new Uint8Array(1024).fill(5);
258
- const output = await dialer.services.echo.echo(connection.remotePeer, input, {
167
+ const output = await dialer.services.echo.echo(dialAddrs[0], input, {
259
168
  signal: AbortSignal.timeout(timeout)
260
169
  });
261
- expect(output.subarray()).to.equalBytes(input);
262
- 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;
263
240
  }
241
+ expect(read).to.equal(bytes);
264
242
  });
265
- 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 () {
266
244
  ({ dialer, listener, dialAddrs } = await getSetup(common));
267
245
  if (listener == null) {
268
246
  return this.skip();
269
247
  }
270
- // 1. remote stream close read
271
- // 2. local stream close write
272
- // 3. remote stream close write
273
248
  /**
274
249
  * NodeA NodeB
275
250
  * | <--- STOP_SENDING |
276
251
  * | FIN ---> |
277
- * | <--- DATA |
278
252
  * | <--- FIN |
279
253
  * | FIN_ACK ---> |
280
254
  * | <--- FIN_ACK |
281
255
  */
282
- const getRemoteStream = Promise.withResolvers();
256
+ const getRemoteStream = pDefer();
283
257
  const protocol = '/close-local-while-remote-writes/1.0.0';
284
- const streamHandler = (stream) => {
285
- getRemoteStream.resolve(stream);
258
+ const streamHandler = ({ stream }) => {
259
+ void Promise.resolve().then(async () => {
260
+ getRemoteStream.resolve(stream);
261
+ });
286
262
  };
287
- await listener.handle(protocol, streamHandler, {
263
+ await listener.handle(protocol, (info) => {
264
+ streamHandler(info);
265
+ }, {
288
266
  runOnLimitedConnection: true
289
267
  });
290
268
  const connection = await dialer.dial(dialAddrs[0]);
291
- const [localStream, remoteStream] = await Promise.all([
292
- // open a stream on the echo protocol
293
- connection.newStream(protocol, {
294
- runOnLimitedConnection: true
295
- }),
296
- getRemoteStream.promise
297
- ]);
298
- // ignore incoming data
299
- void drain(localStream);
300
- // 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
301
277
  await remoteStream.closeRead();
302
- // 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
303
279
  const remoteInputStream = pushable();
304
- Promise.resolve().then(async () => {
305
- for await (const buf of remoteInputStream) {
306
- const sendMore = remoteStream.send(buf);
307
- if (sendMore === false) {
308
- await pEvent(remoteStream, 'drain', {
309
- rejectionEvents: [
310
- 'close'
311
- ]
312
- });
313
- }
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);
314
285
  }
315
- // close the remote writable end
316
- await remoteStream.close();
286
+ }, {
287
+ minTimeout: 100
317
288
  });
318
- await Promise.all([
319
- // wait for remote to receive local FIN
320
- pEvent(remoteStream, 'remoteCloseWrite'),
321
- // wait to receive FIN_ACK
322
- localStream.close()
323
- ]);
324
- // stop sending remote -> local
289
+ // remote closes write
325
290
  remoteInputStream.end();
291
+ // wait to receive FIN_ACK
292
+ await p;
326
293
  // wait for remote to notice closure
327
294
  await pRetry(() => {
328
295
  if (remoteStream.status !== 'closed') {
329
296
  throw new Error('Remote stream not closed');
330
297
  }
331
298
  });
332
- // both ends should be closed
333
- assertStreamClosed(localStream);
299
+ assertStreamClosed(stream);
334
300
  assertStreamClosed(remoteStream);
335
301
  });
336
- 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 () {
337
303
  ({ dialer, listener, dialAddrs } = await getSetup(common));
338
304
  if (listener == null) {
339
305
  return this.skip();
340
306
  }
341
307
  /**
342
308
  * NodeA NodeB
343
- * | DATA ---> |
344
309
  * | FIN ---> |
345
310
  * | <--- FIN |
346
311
  * | FIN_ACK ---> |
347
312
  * | <--- FIN_ACK |
348
313
  */
349
- const getRemoteStream = Promise.withResolvers();
314
+ const getRemoteStream = pDefer();
350
315
  const protocol = '/close-local-while-remote-reads/1.0.0';
351
- const streamHandler = (stream) => {
352
- getRemoteStream.resolve(stream);
316
+ const streamHandler = ({ stream }) => {
317
+ void Promise.resolve().then(async () => {
318
+ getRemoteStream.resolve(stream);
319
+ });
353
320
  };
354
- await listener.handle(protocol, streamHandler, {
321
+ await listener.handle(protocol, (info) => {
322
+ streamHandler(info);
323
+ }, {
355
324
  runOnLimitedConnection: true
356
325
  });
357
326
  const connection = await dialer.dial(dialAddrs[0]);
358
- const [localStream, remoteStream] = await Promise.all([
359
- // open a stream on the echo protocol
360
- connection.newStream(protocol, {
361
- runOnLimitedConnection: true
362
- }),
363
- getRemoteStream.promise
364
- ]);
365
- // ignore incoming data
366
- void drain(remoteStream);
367
- // close the remote stream writable end when the local sends a FIN
368
- remoteStream.addEventListener('remoteCloseWrite', () => {
369
- remoteStream.close()
370
- .catch(err => {
371
- remoteStream.abort(err);
372
- });
327
+ // open a stream on the echo protocol
328
+ const stream = await connection.newStream(protocol, {
329
+ runOnLimitedConnection: true
373
330
  });
374
- // send data to the remote then close the stream
375
- const data = [
376
- Uint8Array.from([0, 1, 2, 3]),
377
- Uint8Array.from([4, 5, 6, 7]),
378
- Uint8Array.from([8, 9, 0, 1])
379
- ];
380
- const [remoteCloseEvent, localCloseEvent] = await Promise.all([
381
- // wait for the remote to close
382
- pEvent(remoteStream, 'close'),
383
- pEvent(localStream, 'close'),
384
- (async () => {
385
- for (const buf of data) {
386
- if (!localStream.send(buf)) {
387
- await pEvent(localStream, 'drain', {
388
- rejectionEvents: [
389
- 'close'
390
- ]
391
- });
392
- }
393
- }
394
- // close the local writable end
395
- await localStream.close();
396
- })()
397
- ]);
398
- expect(remoteCloseEvent).to.not.have.property('error', 'remote stream did not close cleanly');
399
- 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);
400
354
  // wait for remote to notice closure
401
355
  await pRetry(() => {
402
356
  if (remoteStream.status !== 'closed') {
403
357
  throw new Error('Remote stream not closed');
404
358
  }
405
359
  });
406
- // both ends should be closed
407
- assertStreamClosed(localStream);
360
+ assertStreamClosed(stream);
408
361
  assertStreamClosed(remoteStream);
409
362
  });
410
- it('can close a stream for writing but receive a large amount of data', async function () {
411
- const timeout = 120_000;
412
- this.timeout(timeout);
413
- ({ 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));
414
374
  if (listener == null) {
415
375
  return this.skip();
416
376
  }
417
- const protocol = '/receive-data/1.0.0';
418
- const chunkSize = 1024;
419
- const bytes = chunkSize * 1024 * 10;
420
- await listener.handle(protocol, async (stream) => {
421
- for (let i = 0; i < bytes; i += chunkSize) {
422
- const sendMore = stream.send(new Uint8Array(chunkSize));
423
- if (!sendMore) {
424
- await pEvent(stream, 'drain', {
425
- rejectionEvents: [
426
- 'close'
427
- ]
428
- });
429
- }
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();
430
383
  }
431
- await stream.close();
432
384
  });
433
- const stream = await dialer.dialProtocol(dialAddrs[0], protocol);
434
- const [output] = await Promise.all([
435
- all(stream),
436
- stream.close()
437
- ]);
438
- 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
+ });
439
407
  });
440
408
  });
441
409
  };
@@ -444,5 +412,7 @@ function assertStreamClosed(stream) {
444
412
  expect(stream.readStatus).to.equal('closed');
445
413
  expect(stream.writeStatus).to.equal('closed');
446
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');
447
417
  }
448
418
  //# sourceMappingURL=index.js.map