@libp2p/interface-compliance-tests 6.5.0 → 7.0.0-55b7e5fea

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 (133) hide show
  1. package/dist/src/connection-encryption/index.d.ts.map +1 -1
  2. package/dist/src/connection-encryption/index.js +24 -15
  3. package/dist/src/connection-encryption/index.js.map +1 -1
  4. package/dist/src/stream-muxer/base-test.d.ts.map +1 -1
  5. package/dist/src/stream-muxer/base-test.js +62 -345
  6. package/dist/src/stream-muxer/base-test.js.map +1 -1
  7. package/dist/src/stream-muxer/close-test.d.ts.map +1 -1
  8. package/dist/src/stream-muxer/close-test.js +254 -320
  9. package/dist/src/stream-muxer/close-test.js.map +1 -1
  10. package/dist/src/stream-muxer/index.js +2 -2
  11. package/dist/src/stream-muxer/index.js.map +1 -1
  12. package/dist/src/stream-muxer/{mega-stress-test.d.ts → stream-test.d.ts} +2 -2
  13. package/dist/src/stream-muxer/stream-test.d.ts.map +1 -0
  14. package/dist/src/stream-muxer/stream-test.js +290 -0
  15. package/dist/src/stream-muxer/stream-test.js.map +1 -0
  16. package/dist/src/stream-muxer/stress-test.d.ts.map +1 -1
  17. package/dist/src/stream-muxer/stress-test.js +70 -16
  18. package/dist/src/stream-muxer/stress-test.js.map +1 -1
  19. package/dist/src/transport/index.d.ts.map +1 -1
  20. package/dist/src/transport/index.js +235 -205
  21. package/dist/src/transport/index.js.map +1 -1
  22. package/dist/src/transport/utils.d.ts.map +1 -1
  23. package/dist/src/transport/utils.js +3 -2
  24. package/dist/src/transport/utils.js.map +1 -1
  25. package/package.json +25 -53
  26. package/src/connection-encryption/index.ts +27 -20
  27. package/src/stream-muxer/base-test.ts +75 -413
  28. package/src/stream-muxer/close-test.ts +305 -343
  29. package/src/stream-muxer/index.ts +2 -2
  30. package/src/stream-muxer/stream-test.ts +381 -0
  31. package/src/stream-muxer/stress-test.ts +92 -18
  32. package/src/transport/index.ts +281 -241
  33. package/src/transport/utils.ts +3 -2
  34. package/dist/src/connection-encryption/utils/index.d.ts +0 -3
  35. package/dist/src/connection-encryption/utils/index.d.ts.map +0 -1
  36. package/dist/src/connection-encryption/utils/index.js +0 -21
  37. package/dist/src/connection-encryption/utils/index.js.map +0 -1
  38. package/dist/src/matchers.d.ts +0 -12
  39. package/dist/src/matchers.d.ts.map +0 -1
  40. package/dist/src/matchers.js +0 -14
  41. package/dist/src/matchers.js.map +0 -1
  42. package/dist/src/mocks/connection-manager.d.ts +0 -27
  43. package/dist/src/mocks/connection-manager.d.ts.map +0 -1
  44. package/dist/src/mocks/connection-manager.js +0 -147
  45. package/dist/src/mocks/connection-manager.js.map +0 -1
  46. package/dist/src/mocks/connection.d.ts +0 -41
  47. package/dist/src/mocks/connection.d.ts.map +0 -1
  48. package/dist/src/mocks/connection.js +0 -236
  49. package/dist/src/mocks/connection.js.map +0 -1
  50. package/dist/src/mocks/duplex.d.ts +0 -4
  51. package/dist/src/mocks/duplex.d.ts.map +0 -1
  52. package/dist/src/mocks/duplex.js +0 -9
  53. package/dist/src/mocks/duplex.js.map +0 -1
  54. package/dist/src/mocks/index.d.ts +0 -12
  55. package/dist/src/mocks/index.d.ts.map +0 -1
  56. package/dist/src/mocks/index.js +0 -8
  57. package/dist/src/mocks/index.js.map +0 -1
  58. package/dist/src/mocks/multiaddr-connection.d.ts +0 -17
  59. package/dist/src/mocks/multiaddr-connection.d.ts.map +0 -1
  60. package/dist/src/mocks/multiaddr-connection.js +0 -64
  61. package/dist/src/mocks/multiaddr-connection.js.map +0 -1
  62. package/dist/src/mocks/muxer.d.ts +0 -36
  63. package/dist/src/mocks/muxer.d.ts.map +0 -1
  64. package/dist/src/mocks/muxer.js +0 -234
  65. package/dist/src/mocks/muxer.js.map +0 -1
  66. package/dist/src/mocks/registrar.d.ts +0 -16
  67. package/dist/src/mocks/registrar.d.ts.map +0 -1
  68. package/dist/src/mocks/registrar.js +0 -66
  69. package/dist/src/mocks/registrar.js.map +0 -1
  70. package/dist/src/mocks/upgrader.d.ts +0 -9
  71. package/dist/src/mocks/upgrader.d.ts.map +0 -1
  72. package/dist/src/mocks/upgrader.js +0 -46
  73. package/dist/src/mocks/upgrader.js.map +0 -1
  74. package/dist/src/pubsub/api.d.ts +0 -6
  75. package/dist/src/pubsub/api.d.ts.map +0 -1
  76. package/dist/src/pubsub/api.js +0 -88
  77. package/dist/src/pubsub/api.js.map +0 -1
  78. package/dist/src/pubsub/connection-handlers.d.ts +0 -6
  79. package/dist/src/pubsub/connection-handlers.d.ts.map +0 -1
  80. package/dist/src/pubsub/connection-handlers.js +0 -329
  81. package/dist/src/pubsub/connection-handlers.js.map +0 -1
  82. package/dist/src/pubsub/emit-self.d.ts +0 -6
  83. package/dist/src/pubsub/emit-self.d.ts.map +0 -1
  84. package/dist/src/pubsub/emit-self.js +0 -80
  85. package/dist/src/pubsub/emit-self.js.map +0 -1
  86. package/dist/src/pubsub/index.d.ts +0 -18
  87. package/dist/src/pubsub/index.d.ts.map +0 -1
  88. package/dist/src/pubsub/index.js +0 -17
  89. package/dist/src/pubsub/index.js.map +0 -1
  90. package/dist/src/pubsub/messages.d.ts +0 -6
  91. package/dist/src/pubsub/messages.d.ts.map +0 -1
  92. package/dist/src/pubsub/messages.js +0 -48
  93. package/dist/src/pubsub/messages.js.map +0 -1
  94. package/dist/src/pubsub/multiple-nodes.d.ts +0 -6
  95. package/dist/src/pubsub/multiple-nodes.d.ts.map +0 -1
  96. package/dist/src/pubsub/multiple-nodes.js +0 -350
  97. package/dist/src/pubsub/multiple-nodes.js.map +0 -1
  98. package/dist/src/pubsub/two-nodes.d.ts +0 -6
  99. package/dist/src/pubsub/two-nodes.d.ts.map +0 -1
  100. package/dist/src/pubsub/two-nodes.js +0 -216
  101. package/dist/src/pubsub/two-nodes.js.map +0 -1
  102. package/dist/src/pubsub/utils.d.ts +0 -5
  103. package/dist/src/pubsub/utils.d.ts.map +0 -1
  104. package/dist/src/pubsub/utils.js +0 -27
  105. package/dist/src/pubsub/utils.js.map +0 -1
  106. package/dist/src/stream-muxer/mega-stress-test.d.ts.map +0 -1
  107. package/dist/src/stream-muxer/mega-stress-test.js +0 -11
  108. package/dist/src/stream-muxer/mega-stress-test.js.map +0 -1
  109. package/dist/src/stream-muxer/spawner.d.ts +0 -4
  110. package/dist/src/stream-muxer/spawner.d.ts.map +0 -1
  111. package/dist/src/stream-muxer/spawner.js +0 -39
  112. package/dist/src/stream-muxer/spawner.js.map +0 -1
  113. package/dist/typedoc-urls.json +0 -44
  114. package/src/connection-encryption/utils/index.ts +0 -27
  115. package/src/matchers.ts +0 -18
  116. package/src/mocks/connection-manager.ts +0 -216
  117. package/src/mocks/connection.ts +0 -309
  118. package/src/mocks/duplex.ts +0 -11
  119. package/src/mocks/index.ts +0 -11
  120. package/src/mocks/multiaddr-connection.ts +0 -80
  121. package/src/mocks/muxer.ts +0 -331
  122. package/src/mocks/registrar.ts +0 -86
  123. package/src/mocks/upgrader.ts +0 -65
  124. package/src/pubsub/api.ts +0 -116
  125. package/src/pubsub/connection-handlers.ts +0 -413
  126. package/src/pubsub/emit-self.ts +0 -99
  127. package/src/pubsub/index.ts +0 -34
  128. package/src/pubsub/messages.ts +0 -59
  129. package/src/pubsub/multiple-nodes.ts +0 -440
  130. package/src/pubsub/two-nodes.ts +0 -272
  131. package/src/pubsub/utils.ts +0 -34
  132. package/src/stream-muxer/mega-stress-test.ts +0 -14
  133. package/src/stream-muxer/spawner.ts +0 -57
@@ -1,40 +1,102 @@
1
- import { stop } from '@libp2p/interface';
1
+ import { stop, TimeoutError } from '@libp2p/interface';
2
+ import { prefixLogger } from '@libp2p/logger';
2
3
  import { expect } from 'aegir/chai';
3
4
  import delay from 'delay';
5
+ import all from 'it-all';
4
6
  import drain from 'it-drain';
5
7
  import { pushable } from 'it-pushable';
6
- import pDefer from 'p-defer';
7
8
  import { pEvent } from 'p-event';
8
9
  import pRetry from 'p-retry';
9
10
  import pWaitFor from 'p-wait-for';
10
11
  import { raceSignal } from 'race-signal';
12
+ import { Uint8ArrayList } from 'uint8arraylist';
11
13
  import { isValidTick } from '../is-valid-tick.js';
12
14
  import { createPeer, getTransportManager, getUpgrader } from './utils.js';
13
15
  async function getSetup(common) {
14
16
  const setup = await common.setup();
15
- const dialer = await createPeer(setup.dialer);
17
+ const dialer = await createPeer({
18
+ logger: prefixLogger('dialer'),
19
+ ...setup.dialer
20
+ });
16
21
  let listener;
17
22
  if (setup.listener != null) {
18
- listener = await createPeer(setup.listener);
23
+ listener = await createPeer({
24
+ logger: prefixLogger('listener'),
25
+ ...setup.listener
26
+ });
19
27
  }
20
- const dialAddrs = listener?.getMultiaddrs() ?? setup.dialAddrs;
28
+ let dialAddrs = listener?.getMultiaddrs() ?? setup.dialAddrs;
21
29
  if (dialAddrs == null) {
22
30
  throw new Error('Listener config or dial addresses must be specified');
23
31
  }
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
+ }
24
36
  return {
25
37
  dialer,
26
38
  listener,
27
- dialAddrs: dialAddrs.filter(ma => setup.dialMultiaddrMatcher.exactMatch(ma)),
39
+ dialAddrs,
28
40
  dialMultiaddrMatcher: setup.dialMultiaddrMatcher,
29
41
  listenMultiaddrMatcher: setup.listenMultiaddrMatcher
30
42
  };
31
43
  }
32
44
  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
+ });
33
94
  describe('interface-transport', () => {
34
95
  let dialer;
35
96
  let listener;
36
97
  let dialAddrs;
37
98
  let dialMultiaddrMatcher;
99
+ let listenMultiaddrMatcher;
38
100
  afterEach(async () => {
39
101
  await stop(dialer, listener);
40
102
  await common.teardown();
@@ -45,17 +107,20 @@ export default (common) => {
45
107
  const output = await dialer.services.echo.echo(dialAddrs[0], input, {
46
108
  signal: AbortSignal.timeout(5000)
47
109
  });
48
- expect(output).to.equalBytes(input);
110
+ expect(output.subarray()).to.equalBytes(input);
49
111
  });
50
112
  it('should listen on multiple addresses', async () => {
51
113
  ({ dialer, listener, dialAddrs } = await getSetup(common));
52
114
  const input = Uint8Array.from([0, 1, 2, 3, 4]);
53
- await expect(dialer.services.echo.echo(dialAddrs[0], input, {
54
- signal: AbortSignal.timeout(5000)
55
- })).to.eventually.deep.equal(input);
56
- await expect(dialer.services.echo.echo(dialAddrs[1], input, {
115
+ const output1 = await dialer.services.echo.echo(dialAddrs[0], input, {
57
116
  signal: AbortSignal.timeout(5000)
58
- })).to.eventually.deep.equal(input);
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);
59
124
  });
60
125
  it('can close connections', async () => {
61
126
  ({ dialer, listener, dialAddrs } = await getSetup(common));
@@ -75,12 +140,15 @@ export default (common) => {
75
140
  .with.property('name', 'AbortError');
76
141
  });
77
142
  it('should close all streams when the connection closes', async () => {
78
- ({ dialer, listener, dialAddrs } = await getSetup(common));
143
+ ({ dialer, listener, dialAddrs, listenMultiaddrMatcher } = await getSetup(common));
79
144
  let incomingConnectionPromise;
80
145
  if (listener != null) {
81
- incomingConnectionPromise = pDefer();
146
+ incomingConnectionPromise = Promise.withResolvers();
82
147
  listener.addEventListener('connection:open', (event) => {
83
148
  const conn = event.detail;
149
+ if (!listenMultiaddrMatcher.matches(conn.remoteAddr)) {
150
+ return;
151
+ }
84
152
  if (conn.remotePeer.equals(dialer.peerId)) {
85
153
  incomingConnectionPromise?.resolve(conn);
86
154
  }
@@ -96,18 +164,25 @@ export default (common) => {
96
164
  maxOutboundStreams: 5
97
165
  });
98
166
  }
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
- });
167
+ expect(connection).to.have.property('streams').that.has.lengthOf(5);
105
168
  if (remoteConn != null) {
106
- await pWaitFor(() => remoteConn.streams.length === 0, {
107
- timeout: 5000
169
+ await pWaitFor(() => remoteConn.streams.filter(s => s.protocol === '/echo/1.0.0').length === 5, {
170
+ timeout: 5_000,
171
+ interval: 1_000
108
172
  });
109
173
  }
110
- expect(streams.find(stream => stream.status === 'open')).to.be.undefined();
174
+ // 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();
182
+ if (remoteConn != null) {
183
+ expect(remoteConn).to.have.property('status', 'closed');
184
+ expect(remoteConn).to.have.property('streams').that.is.empty();
185
+ }
111
186
  });
112
187
  it('should not handle connection if upgradeInbound rejects', async function () {
113
188
  ({ dialer, listener, dialAddrs, dialMultiaddrMatcher } = await getSetup(common));
@@ -148,262 +223,219 @@ export default (common) => {
148
223
  }
149
224
  }
150
225
  });
151
- it('should handle one big write', async function () {
226
+ it('should handle one small write', async function () {
152
227
  const timeout = 120_000;
153
228
  this.timeout(timeout);
154
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
+ it('should handle one big write', async function () {
239
+ const timeout = 540_000;
240
+ this.timeout(timeout);
241
+ ({ dialer, listener, dialAddrs } = await getSetup(common));
242
+ const connection = await dialer.dial(dialAddrs[0]);
155
243
  const input = new Uint8Array(1024 * 1024 * 10).fill(5);
156
- const output = await dialer.services.echo.echo(dialAddrs[0], input, {
244
+ const output = await dialer.services.echo.echo(connection.remotePeer, input, {
157
245
  signal: AbortSignal.timeout(timeout)
158
246
  });
159
- expect(output).to.equalBytes(input);
247
+ expect(output.subarray()).to.equalBytes(input);
248
+ expect(connection.streams.filter(s => s.protocol === dialer.services.echo.protocol)).to.be.empty();
160
249
  });
161
250
  it('should handle many small writes', async function () {
162
251
  const timeout = 360_000;
163
252
  this.timeout(timeout);
164
253
  ({ dialer, listener, dialAddrs } = await getSetup(common));
165
- for (let i = 0; i < 2000; i++) {
254
+ const connection = await dialer.dial(dialAddrs[0]);
255
+ const echoProtocol = dialer.services.echo.protocol;
256
+ for (let i = 0; i < 2_000; i++) {
166
257
  const input = new Uint8Array(1024).fill(5);
167
- const output = await dialer.services.echo.echo(dialAddrs[0], input, {
258
+ const output = await dialer.services.echo.echo(connection.remotePeer, input, {
168
259
  signal: AbortSignal.timeout(timeout)
169
260
  });
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;
261
+ expect(output.subarray()).to.equalBytes(input);
262
+ expect(connection.streams.filter(s => s.protocol === echoProtocol)).to.be.empty();
240
263
  }
241
- expect(read).to.equal(bytes);
242
264
  });
243
- it('can close local stream for writing and reading while a remote stream is writing', async function () {
265
+ it('can close local stream while a remote stream is writing', async function () {
244
266
  ({ dialer, listener, dialAddrs } = await getSetup(common));
245
267
  if (listener == null) {
246
268
  return this.skip();
247
269
  }
270
+ // 1. remote stream close read
271
+ // 2. local stream close write
272
+ // 3. remote stream close write
248
273
  /**
249
274
  * NodeA NodeB
250
275
  * | <--- STOP_SENDING |
251
276
  * | FIN ---> |
277
+ * | <--- DATA |
252
278
  * | <--- FIN |
253
279
  * | FIN_ACK ---> |
254
280
  * | <--- FIN_ACK |
255
281
  */
256
- const getRemoteStream = pDefer();
282
+ const getRemoteStream = Promise.withResolvers();
257
283
  const protocol = '/close-local-while-remote-writes/1.0.0';
258
- const streamHandler = ({ stream }) => {
259
- void Promise.resolve().then(async () => {
260
- getRemoteStream.resolve(stream);
261
- });
284
+ const streamHandler = (stream) => {
285
+ getRemoteStream.resolve(stream);
262
286
  };
263
- await listener.handle(protocol, (info) => {
264
- streamHandler(info);
265
- }, {
287
+ await listener.handle(protocol, streamHandler, {
266
288
  runOnLimitedConnection: true
267
289
  });
268
290
  const connection = await dialer.dial(dialAddrs[0]);
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
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
277
301
  await remoteStream.closeRead();
278
- // keep the remote write end open, this should delay the FIN_ACK reply to the local stream
302
+ // send data from the remote to the local
279
303
  const remoteInputStream = pushable();
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);
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
+ }
285
314
  }
286
- }, {
287
- minTimeout: 100
315
+ // close the remote writable end
316
+ await remoteStream.close();
288
317
  });
289
- // remote closes write
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
290
325
  remoteInputStream.end();
291
- // wait to receive FIN_ACK
292
- await p;
293
326
  // wait for remote to notice closure
294
327
  await pRetry(() => {
295
328
  if (remoteStream.status !== 'closed') {
296
329
  throw new Error('Remote stream not closed');
297
330
  }
298
331
  });
299
- assertStreamClosed(stream);
332
+ // both ends should be closed
333
+ assertStreamClosed(localStream);
300
334
  assertStreamClosed(remoteStream);
301
335
  });
302
- it('can close local stream for writing and reading while a remote stream is writing using source/sink', async function () {
336
+ it('can close local stream for writing while a remote stream is reading', async function () {
303
337
  ({ dialer, listener, dialAddrs } = await getSetup(common));
304
338
  if (listener == null) {
305
339
  return this.skip();
306
340
  }
307
341
  /**
308
342
  * NodeA NodeB
343
+ * | DATA ---> |
309
344
  * | FIN ---> |
310
345
  * | <--- FIN |
311
346
  * | FIN_ACK ---> |
312
347
  * | <--- FIN_ACK |
313
348
  */
314
- const getRemoteStream = pDefer();
349
+ const getRemoteStream = Promise.withResolvers();
315
350
  const protocol = '/close-local-while-remote-reads/1.0.0';
316
- const streamHandler = ({ stream }) => {
317
- void Promise.resolve().then(async () => {
318
- getRemoteStream.resolve(stream);
319
- });
351
+ const streamHandler = (stream) => {
352
+ getRemoteStream.resolve(stream);
320
353
  };
321
- await listener.handle(protocol, (info) => {
322
- streamHandler(info);
323
- }, {
354
+ await listener.handle(protocol, streamHandler, {
324
355
  runOnLimitedConnection: true
325
356
  });
326
357
  const connection = await dialer.dial(dialAddrs[0]);
327
- // open a stream on the echo protocol
328
- const stream = await connection.newStream(protocol, {
329
- runOnLimitedConnection: true
330
- });
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
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
+ });
345
373
  });
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);
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');
354
400
  // wait for remote to notice closure
355
401
  await pRetry(() => {
356
402
  if (remoteStream.status !== 'closed') {
357
403
  throw new Error('Remote stream not closed');
358
404
  }
359
405
  });
360
- assertStreamClosed(stream);
406
+ // both ends should be closed
407
+ assertStreamClosed(localStream);
361
408
  assertStreamClosed(remoteStream);
362
409
  });
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));
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));
374
414
  if (listener == null) {
375
415
  return this.skip();
376
416
  }
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();
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
+ }
383
430
  }
431
+ await stream.close();
384
432
  });
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
- });
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);
407
439
  });
408
440
  });
409
441
  };
@@ -412,7 +444,5 @@ function assertStreamClosed(stream) {
412
444
  expect(stream.readStatus).to.equal('closed');
413
445
  expect(stream.writeStatus).to.equal('closed');
414
446
  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');
417
447
  }
418
448
  //# sourceMappingURL=index.js.map