@libp2p/interface-compliance-tests 6.4.16 → 6.5.0-8484de8a2

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