@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,341 +1,408 @@
1
1
  /* eslint max-nested-callbacks: ["error", 8] */
2
- import { multiaddrConnectionPair, echo, pbStream } from '@libp2p/utils';
2
+ import { abortableSource } from 'abortable-iterator';
3
3
  import { expect } from 'aegir/chai';
4
4
  import delay from 'delay';
5
5
  import all from 'it-all';
6
- import map from 'it-map';
7
- import { pEvent } from 'p-event';
6
+ import drain from 'it-drain';
7
+ import { duplexPair } from 'it-pair/duplex';
8
+ import { pipe } from 'it-pipe';
9
+ import { pbStream } from 'it-protobuf-stream';
10
+ import toBuffer from 'it-to-buffer';
11
+ import pDefer from 'p-defer';
8
12
  import { Uint8ArrayList } from 'uint8arraylist';
9
13
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
10
14
  import { Message } from './fixtures/pb/message.js';
11
15
  function randomBuffer() {
12
16
  return uint8ArrayFromString(Math.random().toString());
13
17
  }
14
- async function* infiniteRandom() {
15
- while (true) {
16
- await delay(10);
17
- yield new Uint8ArrayList(randomBuffer());
18
- }
18
+ function infiniteRandom() {
19
+ let done = false;
20
+ const generator = {
21
+ [Symbol.asyncIterator]: () => {
22
+ return generator;
23
+ },
24
+ async next() {
25
+ await delay(10);
26
+ if (done instanceof Error) {
27
+ throw done;
28
+ }
29
+ if (done) {
30
+ return {
31
+ done: true,
32
+ value: undefined
33
+ };
34
+ }
35
+ return {
36
+ done: false,
37
+ value: new Uint8ArrayList(randomBuffer())
38
+ };
39
+ },
40
+ async return() {
41
+ done = true;
42
+ return {
43
+ done: true,
44
+ value: undefined
45
+ };
46
+ },
47
+ async throw(err) {
48
+ done = err;
49
+ return {
50
+ done: true,
51
+ value: undefined
52
+ };
53
+ }
54
+ };
55
+ return generator;
19
56
  }
20
57
  export default (common) => {
21
58
  describe('close', () => {
22
- let outboundConnection;
23
- let inboundConnection;
24
- let dialer;
25
- let listener;
26
- beforeEach(async () => {
27
- [outboundConnection, inboundConnection] = multiaddrConnectionPair();
28
- const dialerFactory = await common.setup();
29
- dialer = dialerFactory.createStreamMuxer(outboundConnection);
30
- const listenerFactory = await common.setup();
31
- listener = listenerFactory.createStreamMuxer(inboundConnection);
32
- });
33
- afterEach(async () => {
34
- await dialer?.close();
35
- await listener?.close();
36
- });
37
- it('closing underlying MultiaddrConnection closes streams', async () => {
59
+ it('closing underlying socket closes streams', async () => {
38
60
  let openedStreams = 0;
39
61
  const expectedStreams = 5;
40
- listener.addEventListener('stream', (evt) => {
41
- openedStreams++;
42
- echo(evt.detail);
62
+ const dialerFactory = await common.setup();
63
+ const dialer = dialerFactory.createStreamMuxer({
64
+ direction: 'outbound'
43
65
  });
44
- const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.createStream()));
45
- void Promise.all(streams.map(async (stream) => {
46
- for await (const buf of infiniteRandom()) {
47
- if (stream.status !== 'open') {
48
- return;
49
- }
50
- const sendMore = stream.send(buf);
51
- if (!sendMore) {
52
- await pEvent(stream, 'drain', {
53
- rejectionEvents: [
54
- 'close'
55
- ]
56
- });
57
- }
66
+ // Listener is echo server :)
67
+ const listenerFactory = await common.setup();
68
+ const listener = listenerFactory.createStreamMuxer({
69
+ direction: 'inbound',
70
+ onIncomingStream: (stream) => {
71
+ openedStreams++;
72
+ void pipe(stream, stream);
58
73
  }
74
+ });
75
+ const p = duplexPair();
76
+ void pipe(p[0], dialer, p[0]);
77
+ void pipe(p[1], listener, p[1]);
78
+ const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.newStream()));
79
+ void Promise.all(streams.map(async (stream) => {
80
+ await pipe(infiniteRandom(), stream, drain);
59
81
  }));
60
82
  expect(dialer.streams).to.have.lengthOf(expectedStreams);
61
83
  // Pause, and then close the dialer
62
84
  await delay(50);
63
- await inboundConnection.close();
64
- await outboundConnection.close();
65
- await delay(50);
85
+ await pipe(async function* () { }, dialer, drain);
66
86
  expect(openedStreams).to.have.equal(expectedStreams);
67
87
  expect(dialer.streams).to.have.lengthOf(0);
68
88
  });
69
89
  it('calling close closes streams', async () => {
70
90
  let openedStreams = 0;
71
91
  const expectedStreams = 5;
72
- listener.addEventListener('stream', (evt) => {
73
- openedStreams++;
74
- echo(evt.detail);
92
+ const dialerFactory = await common.setup();
93
+ const dialer = dialerFactory.createStreamMuxer({
94
+ direction: 'outbound'
75
95
  });
76
- const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.createStream()));
77
- void Promise.all(streams.map(async (stream) => {
78
- for await (const buf of infiniteRandom()) {
79
- if (stream.status !== 'open') {
80
- return;
81
- }
82
- const sendMore = stream.send(buf);
83
- if (!sendMore) {
84
- await pEvent(stream, 'drain', {
85
- rejectionEvents: [
86
- 'close'
87
- ]
88
- });
89
- }
96
+ // Listener is echo server :)
97
+ const listenerFactory = await common.setup();
98
+ const listener = listenerFactory.createStreamMuxer({
99
+ direction: 'inbound',
100
+ onIncomingStream: (stream) => {
101
+ openedStreams++;
102
+ void pipe(stream, stream).catch(() => { });
90
103
  }
91
- }))
92
- .catch(() => {
93
- // calling .send on a closed stream will throw so swallow any errors
94
104
  });
105
+ const p = duplexPair();
106
+ void pipe(p[0], dialer, p[0]);
107
+ void pipe(p[1], listener, p[1]);
108
+ const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.newStream()));
109
+ void Promise.all(streams.map(async (stream) => {
110
+ await pipe(infiniteRandom(), stream, drain);
111
+ }));
95
112
  expect(dialer.streams, 'dialer - number of opened streams should match number of calls to newStream').to.have.lengthOf(expectedStreams);
96
113
  // Pause, and then close the dialer
97
114
  await delay(50);
98
115
  await dialer.close();
99
- await delay(50);
100
116
  expect(openedStreams, 'listener - number of opened streams should match number of calls to newStream').to.have.equal(expectedStreams);
101
117
  expect(dialer.streams, 'all tracked streams should be deleted after the muxer has called close').to.have.lengthOf(0);
102
118
  });
103
- it('calling abort aborts streams', async () => {
119
+ it('calling close with an error aborts streams', async () => {
104
120
  let openedStreams = 0;
105
121
  const expectedStreams = 5;
106
- listener.addEventListener('stream', (evt) => {
107
- openedStreams++;
108
- echo(evt.detail);
122
+ const dialerFactory = await common.setup();
123
+ const dialer = dialerFactory.createStreamMuxer({
124
+ direction: 'outbound'
109
125
  });
110
- const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.createStream()));
111
- const streamPipes = streams.map(async (stream) => {
112
- for await (const buf of infiniteRandom()) {
113
- if (stream.writeStatus !== 'writable') {
114
- break;
115
- }
116
- const sendMore = stream.send(buf);
117
- if (!sendMore) {
118
- await pEvent(stream, 'drain', {
119
- rejectionEvents: ['close']
120
- });
121
- }
126
+ // Listener is echo server :)
127
+ const listenerFactory = await common.setup();
128
+ const listener = listenerFactory.createStreamMuxer({
129
+ direction: 'inbound',
130
+ onIncomingStream: (stream) => {
131
+ openedStreams++;
132
+ void pipe(stream, stream).catch(() => { });
122
133
  }
123
134
  });
124
- expect(dialer.streams).to.have.lengthOf(expectedStreams, 'dialer - number of opened streams should match number of calls to createStream');
135
+ const p = duplexPair();
136
+ void pipe(p[0], dialer, p[0]);
137
+ void pipe(p[1], listener, p[1]);
138
+ const streams = await Promise.all(Array(expectedStreams).fill(0).map(async () => dialer.newStream()));
139
+ const streamPipes = streams.map(async (stream) => {
140
+ await pipe(infiniteRandom(), stream, drain);
141
+ });
142
+ expect(dialer.streams, 'dialer - number of opened streams should match number of calls to newStream').to.have.lengthOf(expectedStreams);
143
+ // Pause, and then close the dialer
144
+ await delay(50);
145
+ // close _with an error_
146
+ dialer.abort(new Error('Oh no!'));
125
147
  const timeoutError = new Error('timeout');
126
- await Promise.all([
127
- // Pause, and then close the dialer
128
- delay(50).then(() => {
129
- // close _with an error_
130
- dialer.abort(new Error('Oh no!'));
131
- }),
132
- ...streamPipes.map(async (pipe) => {
133
- try {
134
- await Promise.race([
135
- pipe,
136
- new Promise((resolve, reject) => {
137
- setTimeout(() => {
138
- reject(timeoutError);
139
- }, 70);
140
- })
141
- ]);
142
- expect.fail('stream pipe with infinite source should never return');
143
- }
144
- catch (e) {
145
- if (e === timeoutError) {
146
- expect.fail('expected stream pipe to throw an error after muxer closed with error');
147
- }
148
+ for (const pipe of streamPipes) {
149
+ try {
150
+ await Promise.race([
151
+ pipe,
152
+ new Promise((_resolve, reject) => setTimeout(() => { reject(timeoutError); }, 20))
153
+ ]);
154
+ expect.fail('stream pipe with infinite source should never return');
155
+ }
156
+ catch (e) {
157
+ if (e === timeoutError) {
158
+ expect.fail('expected stream pipe to throw an error after muxer closed with error');
148
159
  }
149
- })
150
- ]);
151
- expect(openedStreams).to.equal(expectedStreams, 'listener - number of opened streams should match number of calls to createStream');
152
- expect(dialer.streams).to.have.lengthOf(0, 'all tracked streams should be deleted after the muxer has called close');
160
+ }
161
+ }
162
+ expect(openedStreams, 'listener - number of opened streams should match number of calls to newStream').to.have.equal(expectedStreams);
163
+ expect(dialer.streams, 'all tracked streams should be deleted after the muxer has called close').to.have.lengthOf(0);
153
164
  });
154
165
  it('calling newStream after close throws an error', async () => {
166
+ const dialerFactory = await common.setup();
167
+ const dialer = dialerFactory.createStreamMuxer({
168
+ direction: 'outbound'
169
+ });
155
170
  await dialer.close();
156
- await expect(dialer.createStream()).to.eventually.rejected.with.property('name', 'MuxerClosedError');
157
- expect(dialer.streams).to.have.lengthOf(0, 'closed muxer should have no streams');
171
+ try {
172
+ await dialer.newStream();
173
+ expect.fail('newStream should throw if called after close');
174
+ }
175
+ catch (e) {
176
+ expect(dialer.streams, 'closed muxer should have no streams').to.have.lengthOf(0);
177
+ }
158
178
  });
159
179
  it('closing one of the muxed streams doesn\'t close others', async () => {
160
- const streamCount = 5;
161
- const allStreamsOpen = Promise.withResolvers();
162
- listener.addEventListener('stream', (evt) => {
163
- echo(evt.detail).catch(() => { });
164
- if (listener.streams.length === streamCount) {
165
- allStreamsOpen.resolve();
180
+ const p = duplexPair();
181
+ const dialerFactory = await common.setup();
182
+ const dialer = dialerFactory.createStreamMuxer({
183
+ direction: 'outbound'
184
+ });
185
+ // Listener is echo server :)
186
+ const listenerFactory = await common.setup();
187
+ const listener = listenerFactory.createStreamMuxer({
188
+ direction: 'inbound',
189
+ onIncomingStream: (stream) => {
190
+ void pipe(stream, stream).catch(() => { });
191
+ }
192
+ });
193
+ void pipe(p[0], dialer, p[0]);
194
+ void pipe(p[1], listener, p[1]);
195
+ const stream = await dialer.newStream();
196
+ const streams = await Promise.all(Array.from(Array(5), async () => dialer.newStream()));
197
+ let closed = false;
198
+ const controllers = [];
199
+ const streamResults = streams.map(async (stream) => {
200
+ const controller = new AbortController();
201
+ controllers.push(controller);
202
+ try {
203
+ const abortableRand = abortableSource(infiniteRandom(), controller.signal, {
204
+ abortName: 'TestAbortError'
205
+ });
206
+ await pipe(abortableRand, stream, drain);
207
+ }
208
+ catch (err) {
209
+ if (err.name !== 'TestAbortError') {
210
+ throw err;
211
+ }
212
+ }
213
+ if (!closed) {
214
+ throw new Error('stream should not have ended yet!');
166
215
  }
167
216
  });
168
- const streams = await Promise.all(Array.from(Array(streamCount), async () => dialer.createStream()));
169
- await allStreamsOpen.promise;
170
- expect(dialer.streams).to.have.lengthOf(streamCount);
171
- expect(listener.streams).to.have.lengthOf(streamCount);
172
- expect(dialer.streams.map(s => s.status)).to.deep.equal(new Array(streamCount).fill('open'));
173
- expect(listener.streams.map(s => s.status)).to.deep.equal(new Array(streamCount).fill('open'));
174
- const localStream = streams[0];
175
- const remoteStream = listener.streams[0];
176
- await Promise.all([
177
- pEvent(remoteStream, 'close'),
178
- pEvent(localStream, 'close'),
179
- localStream.close()
180
- ]);
181
- expect(dialer.streams).to.have.lengthOf(streamCount - 1);
182
- expect(listener.streams).to.have.lengthOf(streamCount - 1);
183
- expect(dialer.streams.map(s => s.status)).to.deep.equal(new Array(streamCount - 1).fill('open'));
184
- expect(listener.streams.map(s => s.status)).to.deep.equal(new Array(streamCount - 1).fill('open'));
217
+ // Pause, and then send some data and close the first stream
218
+ await delay(50);
219
+ await pipe([new Uint8ArrayList(randomBuffer())], stream, drain);
220
+ closed = true;
221
+ // Abort all the other streams later
222
+ await delay(50);
223
+ controllers.forEach(c => { c.abort(); });
224
+ // These should now all resolve without error
225
+ await Promise.all(streamResults);
185
226
  });
186
227
  it('can close a stream for writing', async () => {
187
- const deferred = Promise.withResolvers();
188
- const data = [Uint8Array.from([0, 1, 2, 3, 4]), Uint8Array.from([5, 6, 7, 8, 9])];
189
- listener.addEventListener('stream', (evt) => {
190
- void Promise.resolve().then(async () => {
191
- try {
228
+ const deferred = pDefer();
229
+ const p = duplexPair();
230
+ const dialerFactory = await common.setup();
231
+ const dialer = dialerFactory.createStreamMuxer({
232
+ direction: 'outbound'
233
+ });
234
+ const data = [randomBuffer(), randomBuffer()];
235
+ const listenerFactory = await common.setup();
236
+ const listener = listenerFactory.createStreamMuxer({
237
+ direction: 'inbound',
238
+ onIncomingStream: (stream) => {
239
+ void Promise.resolve().then(async () => {
192
240
  // Immediate close for write
193
- await evt.detail.close({
194
- signal: AbortSignal.timeout(1_000)
241
+ await stream.closeWrite();
242
+ const results = await pipe(stream, async (source) => {
243
+ const data = [];
244
+ for await (const chunk of source) {
245
+ data.push(chunk.slice());
246
+ }
247
+ return data;
195
248
  });
196
- const results = await all(map(evt.detail, (buf) => {
197
- return buf.subarray();
198
- }));
199
- expect(results).to.deep.equal(data);
249
+ expect(results).to.eql(data);
200
250
  try {
201
- evt.detail.send(randomBuffer());
251
+ await stream.sink([new Uint8ArrayList(randomBuffer())]);
202
252
  }
203
253
  catch (err) {
204
254
  deferred.resolve(err);
205
255
  }
206
- throw new Error('should not support writing to closed writer');
207
- }
208
- catch (err) {
209
- deferred.reject(err);
210
- }
211
- });
212
- });
213
- const stream = await dialer.createStream();
214
- for (const buf of data) {
215
- if (!stream.send(buf)) {
216
- await pEvent(stream, 'drain', {
217
- rejectionEvents: [
218
- 'close'
219
- ]
256
+ deferred.reject(new Error('should not support writing to closed writer'));
220
257
  });
221
258
  }
222
- }
223
- await stream.close({
224
- signal: AbortSignal.timeout(1_000)
225
259
  });
260
+ void pipe(p[0], dialer, p[0]);
261
+ void pipe(p[1], listener, p[1]);
262
+ const stream = await dialer.newStream();
263
+ await stream.sink(data);
226
264
  const err = await deferred.promise;
227
265
  expect(err).to.have.property('name', 'StreamStateError');
228
266
  });
229
- it('should emit a close event for closed streams not previously written', async () => {
230
- listener.addEventListener('stream', async (evt) => {
231
- void evt.detail.close();
267
+ it('can close a stream for reading', async () => {
268
+ const deferred = pDefer();
269
+ const p = duplexPair();
270
+ const dialerFactory = await common.setup();
271
+ const dialer = dialerFactory.createStreamMuxer({
272
+ direction: 'outbound'
273
+ });
274
+ const data = [randomBuffer(), randomBuffer()].map(d => new Uint8ArrayList(d));
275
+ const expected = toBuffer(data.map(d => d.subarray()));
276
+ const listenerFactory = await common.setup();
277
+ const listener = listenerFactory.createStreamMuxer({
278
+ direction: 'inbound',
279
+ onIncomingStream: (stream) => {
280
+ void all(stream.source).then(deferred.resolve, deferred.reject);
281
+ }
282
+ });
283
+ void pipe(p[0], dialer, p[0]);
284
+ void pipe(p[1], listener, p[1]);
285
+ const stream = await dialer.newStream();
286
+ await stream.closeRead();
287
+ // Source should be done
288
+ void Promise.resolve().then(async () => {
289
+ expect(await stream.source.next()).to.have.property('done', true);
290
+ await stream.sink(data);
232
291
  });
233
- const deferred = Promise.withResolvers();
234
- const stream = await dialer.createStream();
235
- stream.addEventListener('close', () => {
236
- deferred.resolve();
292
+ const results = await deferred.promise;
293
+ expect(toBuffer(results.map(b => b.subarray()))).to.equalBytes(expected);
294
+ });
295
+ it('calls onStreamEnd for closed streams not previously written', async () => {
296
+ const deferred = pDefer();
297
+ const onStreamEnd = () => { deferred.resolve(); };
298
+ const dialerFactory = await common.setup();
299
+ const dialer = dialerFactory.createStreamMuxer({
300
+ direction: 'outbound',
301
+ onStreamEnd
237
302
  });
303
+ const stream = await dialer.newStream();
238
304
  await stream.close();
239
305
  await deferred.promise;
240
306
  });
241
- it('should emit a close event for aborted streams not previously written', async () => {
242
- const deferred = Promise.withResolvers();
243
- const stream = await dialer.createStream();
244
- stream.addEventListener('close', () => {
245
- deferred.resolve();
307
+ it('calls onStreamEnd for read and write closed streams not previously written', async () => {
308
+ const deferred = pDefer();
309
+ const onStreamEnd = () => { deferred.resolve(); };
310
+ const dialerFactory = await common.setup();
311
+ const dialer = dialerFactory.createStreamMuxer({
312
+ direction: 'outbound',
313
+ onStreamEnd
246
314
  });
247
- stream.abort(new Error('Urk!'));
315
+ const stream = await dialer.newStream();
316
+ await stream.closeWrite();
317
+ await stream.closeRead();
248
318
  await deferred.promise;
249
319
  });
250
320
  it('should wait for all data to be sent when closing streams', async () => {
251
- const deferred = Promise.withResolvers();
252
- listener.addEventListener('stream', (evt) => {
253
- const pb = pbStream(evt.detail);
254
- void pb.read(Message)
255
- .then(async (message) => {
256
- deferred.resolve(message);
257
- await evt.detail.close();
258
- })
259
- .catch(err => {
260
- deferred.reject(err);
261
- });
321
+ const deferred = pDefer();
322
+ const p = duplexPair();
323
+ const dialerFactory = await common.setup();
324
+ const dialer = dialerFactory.createStreamMuxer({
325
+ direction: 'outbound'
326
+ });
327
+ const listenerFactory = await common.setup();
328
+ const listener = listenerFactory.createStreamMuxer({
329
+ direction: 'inbound',
330
+ onIncomingStream: (stream) => {
331
+ const pb = pbStream(stream);
332
+ void pb.read(Message)
333
+ .then(async (message) => {
334
+ deferred.resolve(message);
335
+ await pb.unwrap().close();
336
+ })
337
+ .catch(err => {
338
+ deferred.reject(err);
339
+ });
340
+ }
262
341
  });
342
+ void pipe(p[0], dialer, p[0]);
343
+ void pipe(p[1], listener, p[1]);
263
344
  const message = {
264
345
  message: 'hello world',
265
346
  value: 5,
266
347
  flag: true
267
348
  };
268
- const stream = await dialer.createStream();
349
+ const stream = await dialer.newStream();
269
350
  const pb = pbStream(stream);
270
351
  await pb.write(message, Message);
271
- await stream.close();
352
+ await pb.unwrap().close();
272
353
  await expect(deferred.promise).to.eventually.deep.equal(message);
273
354
  });
274
- it('should remove a stream in the streams list after aborting', async () => {
275
- const [listenerStream, dialerStream] = await Promise.all([
276
- pEvent(listener, 'stream').then(evt => evt.detail),
277
- dialer.createStream()
278
- ]);
279
- expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream');
280
- expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream');
281
- await Promise.all([
282
- pEvent(listenerStream, 'close'),
283
- dialerStream.abort(new Error('Urk!'))
284
- ]);
285
- expect(dialer.streams).to.not.include(dialerStream, 'dialer did not remove outbound stream close');
286
- expect(listener.streams).to.not.include(listenerStream, 'listener did not remove inbound stream after close');
287
- });
288
- it('should remove a stream in the streams list after closing', async () => {
289
- const [listenerStream, dialerStream] = await Promise.all([
290
- pEvent(listener, 'stream').then(evt => evt.detail),
291
- dialer.createStream()
292
- ]);
293
- expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream');
294
- expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream');
295
- await Promise.all([
296
- dialerStream.close(),
297
- listenerStream.close()
298
- ]);
299
- await delay(10);
300
- expect(dialer.streams).to.not.include(dialerStream, 'dialer did not remove outbound stream close');
301
- expect(listener.streams).to.not.include(listenerStream, 'listener did not remove inbound stream after close');
302
- });
303
- it('should not remove a half-closed outbound stream', async () => {
304
- const [listenerStream, dialerStream] = await Promise.all([
305
- pEvent(listener, 'stream').then(evt => evt.detail),
306
- dialer.createStream()
307
- ]);
308
- await dialerStream.close();
309
- expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream');
310
- expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream');
311
- });
312
- it('should not remove a half-closed inbound stream', async () => {
313
- const [listenerStream, dialerStream] = await Promise.all([
314
- pEvent(listener, 'stream').then(evt => evt.detail),
315
- dialer.createStream()
316
- ]);
317
- await listenerStream.close();
318
- expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream');
319
- expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream');
320
- });
321
- it('should remove a stream half closed from both ends', async () => {
322
- const [listenerStream, dialerStream] = await Promise.all([
323
- pEvent(listener, 'stream').then(evt => evt.detail),
324
- dialer.createStream()
325
- ]);
326
- expect(dialer.streams).to.include(dialerStream, 'dialer did not store outbound stream');
327
- expect(listener.streams).to.include(listenerStream, 'listener did not store inbound stream');
328
- await listenerStream.close();
329
- expect(dialer.streams).to.include(dialerStream, 'dialer removed outbound stream before fully closing');
330
- expect(listener.streams).to.include(listenerStream, 'listener removed inbound stream before fully closing');
331
- await Promise.all([
332
- pEvent(listenerStream, 'close'),
333
- dialerStream.close()
334
- ]);
335
- await delay(10);
336
- expect(dialer.streams).to.not.include(dialerStream, 'dialer did not remove outbound stream close');
337
- expect(listener.streams).to.not.include(listenerStream, 'listener did not remove inbound stream after close');
338
- });
355
+ /*
356
+ it('should abort closing a stream with outstanding data to read', async () => {
357
+ const deferred = pDefer<Message>()
358
+
359
+ const p = duplexPair<Uint8Array | Uint8ArrayList>()
360
+ const dialerFactory = await common.setup()
361
+ const dialer = dialerFactory.createStreamMuxer({ direction: 'outbound' })
362
+
363
+ const listenerFactory = await common.setup()
364
+ const listener = listenerFactory.createStreamMuxer({
365
+ direction: 'inbound',
366
+ onIncomingStream: (stream) => {
367
+ const pb = pbStream(stream)
368
+
369
+ void pb.read(Message)
370
+ .then(async message => {
371
+ await pb.write(message, Message)
372
+ await pb.unwrap().close()
373
+ deferred.resolve(message)
374
+ })
375
+ .catch(err => {
376
+ deferred.reject(err)
377
+ })
378
+ }
379
+ })
380
+
381
+ void pipe(p[0], dialer, p[0])
382
+ void pipe(p[1], listener, p[1])
383
+
384
+ const message = {
385
+ message: 'hello world',
386
+ value: 5,
387
+ flag: true
388
+ }
389
+
390
+ const stream = await dialer.newStream()
391
+
392
+ const pb = pbStream(stream)
393
+ await pb.write(message, Message)
394
+
395
+ console.info('await write back')
396
+ await deferred.promise
397
+
398
+ // let message arrive
399
+ await delay(100)
400
+
401
+ // close should time out as message is never read
402
+ await expect(pb.unwrap().close()).to.eventually.be.rejected
403
+ .with.property('name', 'TimeoutError')
404
+ })
405
+ */
339
406
  });
340
407
  };
341
408
  //# sourceMappingURL=close-test.js.map