@libp2p/multistream-select 2.0.2 → 3.1.0
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.
- package/README.md +19 -96
- package/dist/src/handle.d.ts +4 -5
- package/dist/src/handle.d.ts.map +1 -1
- package/dist/src/handle.js +5 -4
- package/dist/src/handle.js.map +1 -1
- package/dist/src/index.d.ts +10 -16
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -31
- package/dist/src/index.js.map +1 -1
- package/dist/src/multistream.d.ts +5 -4
- package/dist/src/multistream.d.ts.map +1 -1
- package/dist/src/multistream.js +21 -8
- package/dist/src/multistream.js.map +1 -1
- package/dist/src/select.d.ts +14 -5
- package/dist/src/select.d.ts.map +1 -1
- package/dist/src/select.js +57 -11
- package/dist/src/select.js.map +1 -1
- package/package.json +4 -3
- package/src/handle.ts +9 -6
- package/src/index.ts +11 -35
- package/src/multistream.ts +23 -10
- package/src/select.ts +68 -11
package/README.md
CHANGED
|
@@ -18,26 +18,14 @@
|
|
|
18
18
|
- [Dialer](#dialer)
|
|
19
19
|
- [Listener](#listener)
|
|
20
20
|
- [API](#api)
|
|
21
|
-
- [`
|
|
21
|
+
- [`mss.select(dulpex, protocols, [options])`](#mssselectdulpex-protocols-options)
|
|
22
22
|
- [Parameters](#parameters)
|
|
23
23
|
- [Returns](#returns)
|
|
24
24
|
- [Examples](#examples)
|
|
25
|
-
- [`
|
|
25
|
+
- [`mss.handle(duplex, protocols, [options])`](#msshandleduplex-protocols-options)
|
|
26
26
|
- [Parameters](#parameters-1)
|
|
27
27
|
- [Returns](#returns-1)
|
|
28
28
|
- [Examples](#examples-1)
|
|
29
|
-
- [`dialer.ls([options])`](#dialerlsoptions)
|
|
30
|
-
- [Parameters](#parameters-2)
|
|
31
|
-
- [Returns](#returns-2)
|
|
32
|
-
- [Examples](#examples-2)
|
|
33
|
-
- [`new Listener(duplex)`](#new-listenerduplex)
|
|
34
|
-
- [Parameters](#parameters-3)
|
|
35
|
-
- [Returns](#returns-3)
|
|
36
|
-
- [Examples](#examples-3)
|
|
37
|
-
- [`listener.handle(protocols, [options])`](#listenerhandleprotocols-options)
|
|
38
|
-
- [Parameters](#parameters-4)
|
|
39
|
-
- [Returns](#returns-4)
|
|
40
|
-
- [Examples](#examples-4)
|
|
41
29
|
- [License](#license)
|
|
42
30
|
- [Contribution](#contribution)
|
|
43
31
|
|
|
@@ -69,33 +57,29 @@ The caller will send "interactive" messages, expecting for some acknowledgement
|
|
|
69
57
|
> <dht-message>
|
|
70
58
|
```
|
|
71
59
|
|
|
72
|
-
This mode also packs a `ls` option, so that the callee can list the protocols it currently supports
|
|
73
|
-
|
|
74
60
|
## Usage
|
|
75
61
|
|
|
76
62
|
```js
|
|
77
|
-
import {
|
|
63
|
+
import { select, handle } from '@libp2p/multistream-select'
|
|
78
64
|
// You can now use
|
|
79
|
-
//
|
|
80
|
-
//
|
|
65
|
+
// select - actively select a protocol with a remote
|
|
66
|
+
// handle - handle a protocol with a remote
|
|
81
67
|
```
|
|
82
68
|
|
|
83
69
|
### Dialer
|
|
84
70
|
|
|
85
71
|
```js
|
|
86
72
|
import { pipe } from 'it-pipe'
|
|
87
|
-
import
|
|
73
|
+
import * as mss from '@libp2p/multistream-select'
|
|
88
74
|
import { Mplex } from '@libp2p/mplex'
|
|
89
75
|
|
|
90
76
|
const muxer = new Mplex()
|
|
91
77
|
const muxedStream = muxer.newStream()
|
|
92
78
|
|
|
93
|
-
const mss = new Dialer(muxedStream)
|
|
94
|
-
|
|
95
79
|
// mss.select(protocol(s))
|
|
96
80
|
// Select from one of the passed protocols (in priority order)
|
|
97
81
|
// Returns selected stream and protocol
|
|
98
|
-
const { stream: dhtStream, protocol } = await mss.select([
|
|
82
|
+
const { stream: dhtStream, protocol } = await mss.select(muxedStream, [
|
|
99
83
|
// This might just be different versions of DHT, but could be different impls
|
|
100
84
|
'/ipfs-dht/2.0.0', // Most of the time this will probably just be one item.
|
|
101
85
|
'/ipfs-dht/1.0.0'
|
|
@@ -122,16 +106,14 @@ const { stream: dhtStream, protocol } = await mss.select([
|
|
|
122
106
|
|
|
123
107
|
```js
|
|
124
108
|
import { pipe } from 'it-pipe'
|
|
125
|
-
import
|
|
109
|
+
import * as mss from '@libp2p/multistream-select'
|
|
126
110
|
import { Mplex } from '@libp2p/mplex'
|
|
127
111
|
|
|
128
112
|
const muxer = new Mplex({
|
|
129
113
|
async onStream (muxedStream) {
|
|
130
|
-
const mss = new Listener(muxedStream)
|
|
131
|
-
|
|
132
114
|
// mss.handle(handledProtocols)
|
|
133
115
|
// Returns selected stream and protocol
|
|
134
|
-
const { stream, protocol } = await mss.handle([
|
|
116
|
+
const { stream, protocol } = await mss.handle(muxedStream, [
|
|
135
117
|
'/ipfs-dht/1.0.0',
|
|
136
118
|
'/ipfs-bitswap/1.0.0'
|
|
137
119
|
])
|
|
@@ -159,36 +141,19 @@ const muxer = new Mplex({
|
|
|
159
141
|
|
|
160
142
|
## API
|
|
161
143
|
|
|
162
|
-
### `
|
|
163
|
-
|
|
164
|
-
Create a new multistream select "dialer" instance which can be used to negotiate a protocol to use, list all available protocols the remote supports, or do both.
|
|
165
|
-
|
|
166
|
-
#### Parameters
|
|
167
|
-
|
|
168
|
-
- `duplex` (`Object`) - A [duplex iterable stream](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#duplex-it) to dial on.
|
|
169
|
-
|
|
170
|
-
#### Returns
|
|
171
|
-
|
|
172
|
-
A new multistream select dialer instance.
|
|
173
|
-
|
|
174
|
-
#### Examples
|
|
175
|
-
|
|
176
|
-
```js
|
|
177
|
-
const dialer = new MSS.Dialer(duplex)
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### `dialer.select(protocols, [options])`
|
|
144
|
+
### `mss.select(dulpex, protocols, [options])`
|
|
181
145
|
|
|
182
146
|
Negotiate a protocol to use from a list of protocols.
|
|
183
147
|
|
|
184
148
|
#### Parameters
|
|
185
149
|
|
|
186
|
-
- `
|
|
187
|
-
- `
|
|
150
|
+
- `duplex` (`Duplex`) - A [duplex iterable stream](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#duplex-it) to dial on.
|
|
151
|
+
- `protocols` (`string[]`/`string`) - A list of protocols (or single protocol) to negotiate with. Protocols are attempted in order until a match is made.
|
|
152
|
+
- `options` (`{ signal: AbortSignal, writeBytes?: boolean }`) - an options object containing an AbortSignal and an optional boolean `writeBytes` - if this is true, `Uint8Array`s will be written into `duplex`, otherwise `Uint8ArrayList`s will
|
|
188
153
|
|
|
189
154
|
#### Returns
|
|
190
155
|
|
|
191
|
-
`Promise<{ stream<
|
|
156
|
+
`Promise<{ stream<Duplex>, protocol<string> }>` - A stream for the selected protocol and the protocol that was selected from the list of protocols provided to `select`.
|
|
192
157
|
|
|
193
158
|
Note that after a protocol is selected `dialer` can no longer be used.
|
|
194
159
|
|
|
@@ -203,68 +168,26 @@ const { stream, protocol } = await dialer.select([
|
|
|
203
168
|
// Now talk `protocol` on `stream`
|
|
204
169
|
```
|
|
205
170
|
|
|
206
|
-
### `
|
|
207
|
-
|
|
208
|
-
List protocols that the remote supports.
|
|
209
|
-
|
|
210
|
-
#### Parameters
|
|
211
|
-
|
|
212
|
-
- `options` (`{ signal: AbortSignal }`) - an options object containing an AbortSignal
|
|
213
|
-
|
|
214
|
-
#### Returns
|
|
215
|
-
|
|
216
|
-
`String[]` - A list of all the protocols the remote supports.
|
|
217
|
-
|
|
218
|
-
#### Examples
|
|
219
|
-
|
|
220
|
-
```js
|
|
221
|
-
const protocols = await dialer.ls()
|
|
222
|
-
const wantedProto = '/ipfs-dht/2.0.0'
|
|
223
|
-
|
|
224
|
-
if (!protocols.includes(wantedProto)) {
|
|
225
|
-
throw new Error('remote does not support ' + wantedProto)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Now use dialer.select to use wantedProto, safe in the knowledge it is supported
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### `new Listener(duplex)`
|
|
232
|
-
|
|
233
|
-
Construct a new multistream select "listener" instance which can be used to handle multistream protocol selections for particular protocols.
|
|
234
|
-
|
|
235
|
-
#### Parameters
|
|
236
|
-
|
|
237
|
-
- `duplex` (`Object`) - A [duplex iterable stream](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#duplex-it) to listen on.
|
|
238
|
-
|
|
239
|
-
#### Returns
|
|
240
|
-
|
|
241
|
-
A new multistream select listener instance.
|
|
242
|
-
|
|
243
|
-
#### Examples
|
|
244
|
-
|
|
245
|
-
```js
|
|
246
|
-
const listener = new MSS.Listener(duplex)
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
### `listener.handle(protocols, [options])`
|
|
171
|
+
### `mss.handle(duplex, protocols, [options])`
|
|
250
172
|
|
|
251
173
|
Handle multistream protocol selections for the given list of protocols.
|
|
252
174
|
|
|
253
175
|
#### Parameters
|
|
254
176
|
|
|
177
|
+
- `duplex` (`Duplex`) - A [duplex iterable stream](https://gist.github.com/alanshaw/591dc7dd54e4f99338a347ef568d6ee9#duplex-it) to listen on.
|
|
255
178
|
- `protocols` (`String[]`/`String`) - A list of protocols (or single protocol) that this listener is able to speak.
|
|
256
|
-
- `options` (`{ signal: AbortSignal }`) - an options object containing an AbortSignal
|
|
179
|
+
- `options` (`{ signal: AbortSignal, writeBytes?: boolean }`) - an options object containing an AbortSignal and an optional boolean `writeBytes` - if this is true, `Uint8Array`s will be written into `duplex`, otherwise `Uint8ArrayList`s will
|
|
257
180
|
|
|
258
181
|
#### Returns
|
|
259
182
|
|
|
260
|
-
`Promise<{ stream<
|
|
183
|
+
`Promise<{ stream<Duplex>, protocol<string> }>` - A stream for the selected protocol and the protocol that was selected from the list of protocols provided to `select`.
|
|
261
184
|
|
|
262
185
|
Note that after a protocol is handled `listener` can no longer be used.
|
|
263
186
|
|
|
264
187
|
#### Examples
|
|
265
188
|
|
|
266
189
|
```js
|
|
267
|
-
const { stream, protocol } = await
|
|
190
|
+
const { stream, protocol } = await mss.handle(duplex, [
|
|
268
191
|
'/ipfs-dht/1.0.0',
|
|
269
192
|
'/ipfs-bitswap/1.0.0'
|
|
270
193
|
])
|
package/dist/src/handle.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Uint8ArrayList } from 'uint8arraylist';
|
|
2
2
|
import type { Duplex } from 'it-stream-types';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}>;
|
|
3
|
+
import type { ByteArrayInit, ByteListInit, ProtocolStream } from './index.js';
|
|
4
|
+
export declare function handle(stream: Duplex<Uint8Array>, protocols: string | string[], options: ByteArrayInit): Promise<ProtocolStream<Uint8Array>>;
|
|
5
|
+
export declare function handle(stream: Duplex<Uint8ArrayList, Uint8ArrayList | Uint8Array>, protocols: string | string[], options?: ByteListInit): Promise<ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>>;
|
|
7
6
|
//# sourceMappingURL=handle.d.ts.map
|
package/dist/src/handle.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../../src/handle.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../../src/handle.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAyB,cAAc,EAAE,MAAM,YAAY,CAAA;AAIpG,wBAAsB,MAAM,CAAE,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAA;AACpJ,wBAAsB,MAAM,CAAE,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,cAAc,GAAG,UAAU,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE,cAAc,GAAG,UAAU,CAAC,CAAC,CAAA"}
|
package/dist/src/handle.js
CHANGED
|
@@ -13,22 +13,23 @@ export async function handle(stream, protocols, options) {
|
|
|
13
13
|
log('read "%s"', protocol);
|
|
14
14
|
if (protocol === PROTOCOL_ID) {
|
|
15
15
|
log('respond with "%s" for "%s"', PROTOCOL_ID, protocol);
|
|
16
|
-
multistream.write(writer, uint8ArrayFromString(PROTOCOL_ID));
|
|
16
|
+
multistream.write(writer, uint8ArrayFromString(PROTOCOL_ID), options);
|
|
17
17
|
continue;
|
|
18
18
|
}
|
|
19
19
|
if (protocols.includes(protocol)) {
|
|
20
|
-
multistream.write(writer, uint8ArrayFromString(protocol));
|
|
20
|
+
multistream.write(writer, uint8ArrayFromString(protocol), options);
|
|
21
21
|
log('respond with "%s" for "%s"', protocol, protocol);
|
|
22
22
|
rest();
|
|
23
23
|
return { stream: shakeStream, protocol };
|
|
24
24
|
}
|
|
25
25
|
if (protocol === 'ls') {
|
|
26
26
|
// <varint-msg-len><varint-proto-name-len><proto-name>\n<varint-proto-name-len><proto-name>\n\n
|
|
27
|
-
multistream.write(writer, new Uint8ArrayList(...protocols.map(p => multistream.encode(uint8ArrayFromString(p)))));
|
|
27
|
+
multistream.write(writer, new Uint8ArrayList(...protocols.map(p => multistream.encode(uint8ArrayFromString(p)))), options);
|
|
28
|
+
// multistream.writeAll(writer, protocols.map(p => uint8ArrayFromString(p)))
|
|
28
29
|
log('respond with "%s" for %s', protocols, protocol);
|
|
29
30
|
continue;
|
|
30
31
|
}
|
|
31
|
-
multistream.write(writer, uint8ArrayFromString('na'));
|
|
32
|
+
multistream.write(writer, uint8ArrayFromString('na'), options);
|
|
32
33
|
log('respond with "na" for "%s"', protocol);
|
|
33
34
|
}
|
|
34
35
|
}
|
package/dist/src/handle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handle.js","sourceRoot":"","sources":["../../src/handle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACvC,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,UAAU,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAI/C,MAAM,GAAG,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"handle.js","sourceRoot":"","sources":["../../src/handle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACvC,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,UAAU,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAC5E,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAI/C,MAAM,GAAG,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAA;AAIvC,MAAM,CAAC,KAAK,UAAU,MAAM,CAAE,MAAmB,EAAE,SAA4B,EAAE,OAA+B;IAC9G,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,CAAA;IAEvE,OAAO,IAAI,EAAE;QACX,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAC9D,GAAG,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;QAE1B,IAAI,QAAQ,KAAK,WAAW,EAAE;YAC5B,GAAG,CAAC,4BAA4B,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAA;YACxD,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,oBAAoB,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,CAAA;YACrE,SAAQ;SACT;QAED,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE;YAChC,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,oBAAoB,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAA;YAClE,GAAG,CAAC,4BAA4B,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAA;YACrD,IAAI,EAAE,CAAA;YACN,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAA;SACzC;QAED,IAAI,QAAQ,KAAK,IAAI,EAAE;YACrB,+FAA+F;YAC/F,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,cAAc,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAA;YAC1H,4EAA4E;YAC5E,GAAG,CAAC,0BAA0B,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAA;YACpD,SAAQ;SACT;QAED,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,oBAAoB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAA;QAC9D,GAAG,CAAC,4BAA4B,EAAE,QAAQ,CAAC,CAAA;KAC5C;AACH,CAAC"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -2,25 +2,19 @@ import { PROTOCOL_ID } from './constants.js';
|
|
|
2
2
|
import type { Duplex } from 'it-stream-types';
|
|
3
3
|
import type { AbortOptions } from '@libp2p/interfaces';
|
|
4
4
|
export { PROTOCOL_ID };
|
|
5
|
-
export interface ProtocolStream {
|
|
6
|
-
stream: Duplex<
|
|
5
|
+
export interface ProtocolStream<TSource, TSink = TSource> {
|
|
6
|
+
stream: Duplex<TSource, TSink>;
|
|
7
7
|
protocol: string;
|
|
8
8
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
protected shaken: boolean;
|
|
12
|
-
constructor(stream: Duplex<Uint8Array>);
|
|
13
|
-
/**
|
|
14
|
-
* Perform the multistream-select handshake
|
|
15
|
-
*
|
|
16
|
-
* @param {AbortOptions} [options]
|
|
17
|
-
*/
|
|
18
|
-
_handshake(options?: AbortOptions): Promise<void>;
|
|
9
|
+
export interface ByteArrayInit extends AbortOptions {
|
|
10
|
+
writeBytes: true;
|
|
19
11
|
}
|
|
20
|
-
export
|
|
21
|
-
|
|
12
|
+
export interface ByteListInit extends AbortOptions {
|
|
13
|
+
writeBytes?: false;
|
|
22
14
|
}
|
|
23
|
-
export
|
|
24
|
-
|
|
15
|
+
export interface MultistreamSelectInit extends AbortOptions {
|
|
16
|
+
writeBytes?: boolean;
|
|
25
17
|
}
|
|
18
|
+
export { select, lazySelect } from './select.js';
|
|
19
|
+
export { handle } from './handle.js';
|
|
26
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD,OAAO,EAAE,WAAW,EAAE,CAAA;AAEtB,MAAM,WAAW,cAAc,CAAC,OAAO,EAAE,KAAK,GAAG,OAAO;IACtD,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;IAC9B,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,aAAc,SAAQ,YAAY;IACjD,UAAU,EAAE,IAAI,CAAA;CACjB;AAED,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD,UAAU,CAAC,EAAE,KAAK,CAAA;CACnB;AAED,MAAM,WAAW,qBAAsB,SAAQ,YAAY;IACzD,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/src/index.js
CHANGED
|
@@ -1,34 +1,5 @@
|
|
|
1
|
-
import { select } from './select.js';
|
|
2
|
-
import { handle } from './handle.js';
|
|
3
1
|
import { PROTOCOL_ID } from './constants.js';
|
|
4
2
|
export { PROTOCOL_ID };
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.stream = stream;
|
|
8
|
-
this.shaken = false;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Perform the multistream-select handshake
|
|
12
|
-
*
|
|
13
|
-
* @param {AbortOptions} [options]
|
|
14
|
-
*/
|
|
15
|
-
async _handshake(options) {
|
|
16
|
-
if (this.shaken) {
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
const { stream } = await select(this.stream, PROTOCOL_ID, undefined, options);
|
|
20
|
-
this.stream = stream;
|
|
21
|
-
this.shaken = true;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
export class Dialer extends MultistreamSelect {
|
|
25
|
-
async select(protocols, options) {
|
|
26
|
-
return await select(this.stream, protocols, this.shaken ? undefined : PROTOCOL_ID, options);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export class Listener extends MultistreamSelect {
|
|
30
|
-
async handle(protocols, options) {
|
|
31
|
-
return await handle(this.stream, protocols, options);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
3
|
+
export { select, lazySelect } from './select.js';
|
|
4
|
+
export { handle } from './handle.js';
|
|
34
5
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAI5C,OAAO,EAAE,WAAW,EAAE,CAAA;AAmBtB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -2,15 +2,16 @@ import { Uint8ArrayList } from 'uint8arraylist';
|
|
|
2
2
|
import type { Pushable } from 'it-pushable';
|
|
3
3
|
import type { AbortOptions } from '@libp2p/interfaces';
|
|
4
4
|
import type { Reader } from 'it-reader';
|
|
5
|
-
|
|
5
|
+
import type { MultistreamSelectInit } from '.';
|
|
6
|
+
export declare function encode(buffer: Uint8Array | Uint8ArrayList): Uint8ArrayList;
|
|
6
7
|
/**
|
|
7
8
|
* `write` encodes and writes a single buffer
|
|
8
9
|
*/
|
|
9
|
-
export declare function write(writer: Pushable<
|
|
10
|
+
export declare function write(writer: Pushable<any>, buffer: Uint8Array | Uint8ArrayList, options?: MultistreamSelectInit): void;
|
|
10
11
|
/**
|
|
11
12
|
* `writeAll` behaves like `write`, except it encodes an array of items as a single write
|
|
12
13
|
*/
|
|
13
|
-
export declare function writeAll(writer: Pushable<
|
|
14
|
-
export declare function read(reader: Reader, options?: AbortOptions): Promise<
|
|
14
|
+
export declare function writeAll(writer: Pushable<any>, buffers: Uint8Array[], options?: MultistreamSelectInit): void;
|
|
15
|
+
export declare function read(reader: Reader, options?: AbortOptions): Promise<Uint8ArrayList>;
|
|
15
16
|
export declare function readString(reader: Reader, options?: AbortOptions): Promise<string>;
|
|
16
17
|
//# sourceMappingURL=multistream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multistream.d.ts","sourceRoot":"","sources":["../../src/multistream.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAQ/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"multistream.d.ts","sourceRoot":"","sources":["../../src/multistream.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAQ/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,GAAG,CAAA;AAI9C,wBAAgB,MAAM,CAAE,MAAM,EAAE,UAAU,GAAG,cAAc,GAAG,cAAc,CAI3E;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,GAAG,cAAc,EAAE,OAAO,GAAE,qBAA0B,QAQrH;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,OAAO,GAAE,qBAA0B,QAY1G;AAED,wBAAsB,IAAI,CAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAmC3F;AAED,wBAAsB,UAAU,CAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,mBAIvE"}
|
package/dist/src/multistream.js
CHANGED
|
@@ -9,23 +9,34 @@ import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
|
|
|
9
9
|
const NewLine = uint8ArrayFromString('\n');
|
|
10
10
|
export function encode(buffer) {
|
|
11
11
|
const list = new Uint8ArrayList(buffer, NewLine);
|
|
12
|
-
return lp.encode.single(list)
|
|
12
|
+
return lp.encode.single(list);
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* `write` encodes and writes a single buffer
|
|
16
16
|
*/
|
|
17
|
-
export function write(writer, buffer) {
|
|
18
|
-
|
|
17
|
+
export function write(writer, buffer, options = {}) {
|
|
18
|
+
const encoded = encode(buffer);
|
|
19
|
+
if (options.writeBytes === true) {
|
|
20
|
+
writer.push(encoded.subarray());
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
writer.push(encoded);
|
|
24
|
+
}
|
|
19
25
|
}
|
|
20
26
|
/**
|
|
21
27
|
* `writeAll` behaves like `write`, except it encodes an array of items as a single write
|
|
22
28
|
*/
|
|
23
|
-
export function writeAll(writer, buffers) {
|
|
29
|
+
export function writeAll(writer, buffers, options = {}) {
|
|
24
30
|
const list = new Uint8ArrayList();
|
|
25
31
|
for (const buf of buffers) {
|
|
26
32
|
list.append(encode(buf));
|
|
27
33
|
}
|
|
28
|
-
|
|
34
|
+
if (options.writeBytes === true) {
|
|
35
|
+
writer.push(list.subarray());
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
writer.push(list);
|
|
39
|
+
}
|
|
29
40
|
}
|
|
30
41
|
export async function read(reader, options) {
|
|
31
42
|
let byteLength = 1; // Read single byte chunks until the length is known
|
|
@@ -40,7 +51,9 @@ export async function read(reader, options) {
|
|
|
40
51
|
input = abortableSource(varByteSource, options.signal);
|
|
41
52
|
}
|
|
42
53
|
// Once the length has been parsed, read chunk for that length
|
|
43
|
-
const onLength = (l) => {
|
|
54
|
+
const onLength = (l) => {
|
|
55
|
+
byteLength = l;
|
|
56
|
+
};
|
|
44
57
|
const buf = await pipe(input, lp.decode({ onLength }), async (source) => await first(source));
|
|
45
58
|
if (buf == null) {
|
|
46
59
|
throw errCode(new Error('no buffer returned'), 'ERR_INVALID_MULTISTREAM_SELECT_MESSAGE');
|
|
@@ -48,10 +61,10 @@ export async function read(reader, options) {
|
|
|
48
61
|
if (buf.get(buf.byteLength - 1) !== NewLine[0]) {
|
|
49
62
|
throw errCode(new Error('missing newline'), 'ERR_INVALID_MULTISTREAM_SELECT_MESSAGE');
|
|
50
63
|
}
|
|
51
|
-
return buf.
|
|
64
|
+
return buf.sublist(0, -1); // Remove newline
|
|
52
65
|
}
|
|
53
66
|
export async function readString(reader, options) {
|
|
54
67
|
const buf = await read(reader, options);
|
|
55
|
-
return uint8ArrayToString(buf);
|
|
68
|
+
return uint8ArrayToString(buf.subarray());
|
|
56
69
|
}
|
|
57
70
|
//# sourceMappingURL=multistream.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multistream.js","sourceRoot":"","sources":["../../src/multistream.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,KAAK,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,SAAS,CAAA;AAC9B,OAAO,OAAO,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,UAAU,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAC5E,OAAO,KAAK,MAAM,UAAU,CAAA;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"multistream.js","sourceRoot":"","sources":["../../src/multistream.ts"],"names":[],"mappings":"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"}
|
package/dist/src/select.d.ts
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import type { AbortOptions } from '@libp2p/interfaces';
|
|
2
1
|
import type { Duplex } from 'it-stream-types';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import { Uint8ArrayList } from 'uint8arraylist';
|
|
3
|
+
import type { ByteArrayInit, ByteListInit, ProtocolStream } from './index.js';
|
|
4
|
+
export declare function select(stream: Duplex<Uint8Array>, protocols: string | string[], options: ByteArrayInit): Promise<ProtocolStream<Uint8Array>>;
|
|
5
|
+
export declare function select(stream: Duplex<Uint8ArrayList, Uint8ArrayList | Uint8Array>, protocols: string | string[], options?: ByteListInit): Promise<ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>>;
|
|
6
|
+
/**
|
|
7
|
+
* Lazily negotiates a protocol.
|
|
8
|
+
*
|
|
9
|
+
* It *does not* block writes waiting for the other end to respond. Instead, it
|
|
10
|
+
* simply assumes the negotiation went successfully and starts writing data.
|
|
11
|
+
*
|
|
12
|
+
* Use when it is known that the receiver supports the desired protocol.
|
|
13
|
+
*/
|
|
14
|
+
export declare function lazySelect(stream: Duplex<Uint8Array>, protocol: string): ProtocolStream<Uint8Array>;
|
|
15
|
+
export declare function lazySelect(stream: Duplex<Uint8ArrayList, Uint8ArrayList | Uint8Array>, protocol: string): ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>;
|
|
7
16
|
//# sourceMappingURL=select.d.ts.map
|
package/dist/src/select.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/select.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/select.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAI/C,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAyB,cAAc,EAAE,MAAM,YAAY,CAAA;AAIpG,wBAAsB,MAAM,CAAE,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAA;AACpJ,wBAAsB,MAAM,CAAE,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,cAAc,GAAG,UAAU,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE,cAAc,GAAG,UAAU,CAAC,CAAC,CAAA;AAgDtN;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAE,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAAC,UAAU,CAAC,CAAA;AACrG,wBAAgB,UAAU,CAAE,MAAM,EAAE,MAAM,CAAC,cAAc,EAAE,cAAc,GAAG,UAAU,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAAC,cAAc,EAAE,cAAc,GAAG,UAAU,CAAC,CAAA"}
|
package/dist/src/select.js
CHANGED
|
@@ -3,26 +3,27 @@ import errCode from 'err-code';
|
|
|
3
3
|
import * as multistream from './multistream.js';
|
|
4
4
|
import { handshake } from 'it-handshake';
|
|
5
5
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
|
|
6
|
+
import { PROTOCOL_ID } from './index.js';
|
|
7
|
+
import { Uint8ArrayList } from 'uint8arraylist';
|
|
8
|
+
import { pushable } from 'it-pushable';
|
|
9
|
+
import merge from 'it-merge';
|
|
10
|
+
import { reader } from 'it-reader';
|
|
6
11
|
const log = logger('libp2p:mss:select');
|
|
7
|
-
export async function select(stream, protocols,
|
|
12
|
+
export async function select(stream, protocols, options = {}) {
|
|
8
13
|
protocols = Array.isArray(protocols) ? [...protocols] : [protocols];
|
|
9
14
|
const { reader, writer, rest, stream: shakeStream } = handshake(stream);
|
|
10
15
|
const protocol = protocols.shift();
|
|
11
16
|
if (protocol == null) {
|
|
12
17
|
throw new Error('At least one protocol must be specified');
|
|
13
18
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
else {
|
|
19
|
-
log('select: write "%s"', protocol);
|
|
20
|
-
multistream.write(writer, uint8ArrayFromString(protocol));
|
|
21
|
-
}
|
|
19
|
+
log('select: write ["%s", "%s"]', PROTOCOL_ID, protocol);
|
|
20
|
+
const p1 = uint8ArrayFromString(PROTOCOL_ID);
|
|
21
|
+
const p2 = uint8ArrayFromString(protocol);
|
|
22
|
+
multistream.writeAll(writer, [p1, p2], options);
|
|
22
23
|
let response = await multistream.readString(reader, options);
|
|
23
24
|
log('select: read "%s"', response);
|
|
24
25
|
// Read the protocol response if we got the protocolId in return
|
|
25
|
-
if (response ===
|
|
26
|
+
if (response === PROTOCOL_ID) {
|
|
26
27
|
response = await multistream.readString(reader, options);
|
|
27
28
|
log('select: read "%s"', response);
|
|
28
29
|
}
|
|
@@ -34,7 +35,7 @@ export async function select(stream, protocols, protocolId, options) {
|
|
|
34
35
|
// We haven't gotten a valid ack, try the other protocols
|
|
35
36
|
for (const protocol of protocols) {
|
|
36
37
|
log('select: write "%s"', protocol);
|
|
37
|
-
multistream.write(writer, uint8ArrayFromString(protocol));
|
|
38
|
+
multistream.write(writer, uint8ArrayFromString(protocol), options);
|
|
38
39
|
const response = await multistream.readString(reader, options);
|
|
39
40
|
log('select: read "%s" for "%s"', response, protocol);
|
|
40
41
|
if (response === protocol) {
|
|
@@ -45,4 +46,49 @@ export async function select(stream, protocols, protocolId, options) {
|
|
|
45
46
|
rest();
|
|
46
47
|
throw errCode(new Error('protocol selection failed'), 'ERR_UNSUPPORTED_PROTOCOL');
|
|
47
48
|
}
|
|
49
|
+
export function lazySelect(stream, protocol) {
|
|
50
|
+
// This is a signal to write the multistream headers if the consumer tries to
|
|
51
|
+
// read from the source
|
|
52
|
+
const negotiateTrigger = pushable();
|
|
53
|
+
let negotiated = false;
|
|
54
|
+
return {
|
|
55
|
+
stream: {
|
|
56
|
+
sink: async (source) => await stream.sink((async function* () {
|
|
57
|
+
let first = true;
|
|
58
|
+
for await (const chunk of merge(source, negotiateTrigger)) {
|
|
59
|
+
if (first) {
|
|
60
|
+
first = false;
|
|
61
|
+
negotiated = true;
|
|
62
|
+
negotiateTrigger.end();
|
|
63
|
+
const p1 = uint8ArrayFromString(PROTOCOL_ID);
|
|
64
|
+
const p2 = uint8ArrayFromString(protocol);
|
|
65
|
+
const list = new Uint8ArrayList(multistream.encode(p1), multistream.encode(p2));
|
|
66
|
+
if (chunk.length > 0)
|
|
67
|
+
list.append(chunk);
|
|
68
|
+
yield* list;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
yield chunk;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
})()),
|
|
75
|
+
source: (async function* () {
|
|
76
|
+
if (!negotiated)
|
|
77
|
+
negotiateTrigger.push(new Uint8Array());
|
|
78
|
+
const byteReader = reader(stream.source);
|
|
79
|
+
let response = await multistream.readString(byteReader);
|
|
80
|
+
if (response === PROTOCOL_ID) {
|
|
81
|
+
response = await multistream.readString(byteReader);
|
|
82
|
+
}
|
|
83
|
+
if (response !== protocol) {
|
|
84
|
+
throw errCode(new Error('protocol selection failed'), 'ERR_UNSUPPORTED_PROTOCOL');
|
|
85
|
+
}
|
|
86
|
+
for await (const chunk of byteReader) {
|
|
87
|
+
yield* chunk;
|
|
88
|
+
}
|
|
89
|
+
})()
|
|
90
|
+
},
|
|
91
|
+
protocol
|
|
92
|
+
};
|
|
93
|
+
}
|
|
48
94
|
//# sourceMappingURL=select.js.map
|
package/dist/src/select.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACvC,OAAO,OAAO,MAAM,UAAU,CAAA;AAC9B,OAAO,KAAK,WAAW,MAAM,kBAAkB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EAAE,UAAU,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/select.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libp2p/multistream-select",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "JavaScript implementation of multistream-select",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"homepage": "https://github.com/libp2p/js-libp2p-multistream-select#readme",
|
|
@@ -149,13 +149,14 @@
|
|
|
149
149
|
"it-first": "^1.0.6",
|
|
150
150
|
"it-handshake": "^4.0.1",
|
|
151
151
|
"it-length-prefixed": "^8.0.2",
|
|
152
|
+
"it-merge": "^1.0.4",
|
|
152
153
|
"it-pipe": "^2.0.3",
|
|
153
154
|
"it-pushable": "^3.0.0",
|
|
154
155
|
"it-reader": "^6.0.1",
|
|
155
156
|
"it-stream-types": "^1.0.4",
|
|
156
157
|
"p-defer": "^4.0.0",
|
|
157
|
-
"uint8arraylist": "^2.
|
|
158
|
-
"uint8arrays": "^
|
|
158
|
+
"uint8arraylist": "^2.3.1",
|
|
159
|
+
"uint8arrays": "^4.0.2"
|
|
159
160
|
},
|
|
160
161
|
"devDependencies": {
|
|
161
162
|
"@types/varint": "^6.0.0",
|
package/src/handle.ts
CHANGED
|
@@ -4,12 +4,14 @@ import { handshake } from 'it-handshake'
|
|
|
4
4
|
import { PROTOCOL_ID } from './constants.js'
|
|
5
5
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
6
6
|
import { Uint8ArrayList } from 'uint8arraylist'
|
|
7
|
-
import type { AbortOptions } from '@libp2p/interfaces'
|
|
8
7
|
import type { Duplex } from 'it-stream-types'
|
|
8
|
+
import type { ByteArrayInit, ByteListInit, MultistreamSelectInit, ProtocolStream } from './index.js'
|
|
9
9
|
|
|
10
10
|
const log = logger('libp2p:mss:handle')
|
|
11
11
|
|
|
12
|
-
export async function handle (stream: Duplex<Uint8Array>, protocols: string | string[], options
|
|
12
|
+
export async function handle (stream: Duplex<Uint8Array>, protocols: string | string[], options: ByteArrayInit): Promise<ProtocolStream<Uint8Array>>
|
|
13
|
+
export async function handle (stream: Duplex<Uint8ArrayList, Uint8ArrayList | Uint8Array>, protocols: string | string[], options?: ByteListInit): Promise<ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>>
|
|
14
|
+
export async function handle (stream: Duplex<any>, protocols: string | string[], options?: MultistreamSelectInit): Promise<ProtocolStream<any>> {
|
|
13
15
|
protocols = Array.isArray(protocols) ? protocols : [protocols]
|
|
14
16
|
const { writer, reader, rest, stream: shakeStream } = handshake(stream)
|
|
15
17
|
|
|
@@ -19,12 +21,12 @@ export async function handle (stream: Duplex<Uint8Array>, protocols: string | st
|
|
|
19
21
|
|
|
20
22
|
if (protocol === PROTOCOL_ID) {
|
|
21
23
|
log('respond with "%s" for "%s"', PROTOCOL_ID, protocol)
|
|
22
|
-
multistream.write(writer, uint8ArrayFromString(PROTOCOL_ID))
|
|
24
|
+
multistream.write(writer, uint8ArrayFromString(PROTOCOL_ID), options)
|
|
23
25
|
continue
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
if (protocols.includes(protocol)) {
|
|
27
|
-
multistream.write(writer, uint8ArrayFromString(protocol))
|
|
29
|
+
multistream.write(writer, uint8ArrayFromString(protocol), options)
|
|
28
30
|
log('respond with "%s" for "%s"', protocol, protocol)
|
|
29
31
|
rest()
|
|
30
32
|
return { stream: shakeStream, protocol }
|
|
@@ -32,12 +34,13 @@ export async function handle (stream: Duplex<Uint8Array>, protocols: string | st
|
|
|
32
34
|
|
|
33
35
|
if (protocol === 'ls') {
|
|
34
36
|
// <varint-msg-len><varint-proto-name-len><proto-name>\n<varint-proto-name-len><proto-name>\n\n
|
|
35
|
-
multistream.write(writer, new Uint8ArrayList(...protocols.map(p => multistream.encode(uint8ArrayFromString(p)))))
|
|
37
|
+
multistream.write(writer, new Uint8ArrayList(...protocols.map(p => multistream.encode(uint8ArrayFromString(p)))), options)
|
|
38
|
+
// multistream.writeAll(writer, protocols.map(p => uint8ArrayFromString(p)))
|
|
36
39
|
log('respond with "%s" for %s', protocols, protocol)
|
|
37
40
|
continue
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
multistream.write(writer, uint8ArrayFromString('na'))
|
|
43
|
+
multistream.write(writer, uint8ArrayFromString('na'), options)
|
|
41
44
|
log('respond with "na" for "%s"', protocol)
|
|
42
45
|
}
|
|
43
46
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,49 +1,25 @@
|
|
|
1
|
-
import { select } from './select.js'
|
|
2
|
-
import { handle } from './handle.js'
|
|
3
1
|
import { PROTOCOL_ID } from './constants.js'
|
|
4
2
|
import type { Duplex } from 'it-stream-types'
|
|
5
3
|
import type { AbortOptions } from '@libp2p/interfaces'
|
|
6
4
|
|
|
7
5
|
export { PROTOCOL_ID }
|
|
8
6
|
|
|
9
|
-
export interface ProtocolStream {
|
|
10
|
-
stream: Duplex<
|
|
7
|
+
export interface ProtocolStream<TSource, TSink = TSource> {
|
|
8
|
+
stream: Duplex<TSource, TSink>
|
|
11
9
|
protocol: string
|
|
12
10
|
}
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
protected shaken: boolean
|
|
17
|
-
|
|
18
|
-
constructor (stream: Duplex<Uint8Array>) {
|
|
19
|
-
this.stream = stream
|
|
20
|
-
this.shaken = false
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Perform the multistream-select handshake
|
|
25
|
-
*
|
|
26
|
-
* @param {AbortOptions} [options]
|
|
27
|
-
*/
|
|
28
|
-
async _handshake (options?: AbortOptions): Promise<void> {
|
|
29
|
-
if (this.shaken) {
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const { stream } = await select(this.stream, PROTOCOL_ID, undefined, options)
|
|
34
|
-
this.stream = stream
|
|
35
|
-
this.shaken = true
|
|
36
|
-
}
|
|
12
|
+
export interface ByteArrayInit extends AbortOptions {
|
|
13
|
+
writeBytes: true
|
|
37
14
|
}
|
|
38
15
|
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
return await select(this.stream, protocols, this.shaken ? undefined : PROTOCOL_ID, options)
|
|
42
|
-
}
|
|
16
|
+
export interface ByteListInit extends AbortOptions {
|
|
17
|
+
writeBytes?: false
|
|
43
18
|
}
|
|
44
19
|
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
return await handle(this.stream, protocols, options)
|
|
48
|
-
}
|
|
20
|
+
export interface MultistreamSelectInit extends AbortOptions {
|
|
21
|
+
writeBytes?: boolean
|
|
49
22
|
}
|
|
23
|
+
|
|
24
|
+
export { select, lazySelect } from './select.js'
|
|
25
|
+
export { handle } from './handle.js'
|
package/src/multistream.ts
CHANGED
|
@@ -11,36 +11,47 @@ import type { Pushable } from 'it-pushable'
|
|
|
11
11
|
import type { AbortOptions } from '@libp2p/interfaces'
|
|
12
12
|
import type { Source } from 'it-stream-types'
|
|
13
13
|
import type { Reader } from 'it-reader'
|
|
14
|
+
import type { MultistreamSelectInit } from '.'
|
|
14
15
|
|
|
15
16
|
const NewLine = uint8ArrayFromString('\n')
|
|
16
17
|
|
|
17
|
-
export function encode (buffer: Uint8Array | Uint8ArrayList):
|
|
18
|
+
export function encode (buffer: Uint8Array | Uint8ArrayList): Uint8ArrayList {
|
|
18
19
|
const list = new Uint8ArrayList(buffer, NewLine)
|
|
19
20
|
|
|
20
|
-
return lp.encode.single(list)
|
|
21
|
+
return lp.encode.single(list)
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* `write` encodes and writes a single buffer
|
|
25
26
|
*/
|
|
26
|
-
export function write (writer: Pushable<
|
|
27
|
-
|
|
27
|
+
export function write (writer: Pushable<any>, buffer: Uint8Array | Uint8ArrayList, options: MultistreamSelectInit = {}) {
|
|
28
|
+
const encoded = encode(buffer)
|
|
29
|
+
|
|
30
|
+
if (options.writeBytes === true) {
|
|
31
|
+
writer.push(encoded.subarray())
|
|
32
|
+
} else {
|
|
33
|
+
writer.push(encoded)
|
|
34
|
+
}
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
/**
|
|
31
38
|
* `writeAll` behaves like `write`, except it encodes an array of items as a single write
|
|
32
39
|
*/
|
|
33
|
-
export function writeAll (writer: Pushable<
|
|
40
|
+
export function writeAll (writer: Pushable<any>, buffers: Uint8Array[], options: MultistreamSelectInit = {}) {
|
|
34
41
|
const list = new Uint8ArrayList()
|
|
35
42
|
|
|
36
43
|
for (const buf of buffers) {
|
|
37
44
|
list.append(encode(buf))
|
|
38
45
|
}
|
|
39
46
|
|
|
40
|
-
|
|
47
|
+
if (options.writeBytes === true) {
|
|
48
|
+
writer.push(list.subarray())
|
|
49
|
+
} else {
|
|
50
|
+
writer.push(list)
|
|
51
|
+
}
|
|
41
52
|
}
|
|
42
53
|
|
|
43
|
-
export async function read (reader: Reader, options?: AbortOptions) {
|
|
54
|
+
export async function read (reader: Reader, options?: AbortOptions): Promise<Uint8ArrayList> {
|
|
44
55
|
let byteLength = 1 // Read single byte chunks until the length is known
|
|
45
56
|
const varByteSource = { // No return impl - we want the reader to remain readable
|
|
46
57
|
[Symbol.asyncIterator]: () => varByteSource,
|
|
@@ -56,7 +67,9 @@ export async function read (reader: Reader, options?: AbortOptions) {
|
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
// Once the length has been parsed, read chunk for that length
|
|
59
|
-
const onLength = (l: number) => {
|
|
70
|
+
const onLength = (l: number) => {
|
|
71
|
+
byteLength = l
|
|
72
|
+
}
|
|
60
73
|
|
|
61
74
|
const buf = await pipe(
|
|
62
75
|
input,
|
|
@@ -72,11 +85,11 @@ export async function read (reader: Reader, options?: AbortOptions) {
|
|
|
72
85
|
throw errCode(new Error('missing newline'), 'ERR_INVALID_MULTISTREAM_SELECT_MESSAGE')
|
|
73
86
|
}
|
|
74
87
|
|
|
75
|
-
return buf.
|
|
88
|
+
return buf.sublist(0, -1) // Remove newline
|
|
76
89
|
}
|
|
77
90
|
|
|
78
91
|
export async function readString (reader: Reader, options?: AbortOptions) {
|
|
79
92
|
const buf = await read(reader, options)
|
|
80
93
|
|
|
81
|
-
return uint8ArrayToString(buf)
|
|
94
|
+
return uint8ArrayToString(buf.subarray())
|
|
82
95
|
}
|
package/src/select.ts
CHANGED
|
@@ -3,12 +3,19 @@ import errCode from 'err-code'
|
|
|
3
3
|
import * as multistream from './multistream.js'
|
|
4
4
|
import { handshake } from 'it-handshake'
|
|
5
5
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
6
|
-
import
|
|
6
|
+
import { PROTOCOL_ID } from './index.js'
|
|
7
7
|
import type { Duplex } from 'it-stream-types'
|
|
8
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
9
|
+
import { pushable } from 'it-pushable'
|
|
10
|
+
import merge from 'it-merge'
|
|
11
|
+
import { reader } from 'it-reader'
|
|
12
|
+
import type { ByteArrayInit, ByteListInit, MultistreamSelectInit, ProtocolStream } from './index.js'
|
|
8
13
|
|
|
9
14
|
const log = logger('libp2p:mss:select')
|
|
10
15
|
|
|
11
|
-
export async function select (stream: Duplex<Uint8Array>, protocols: string | string[],
|
|
16
|
+
export async function select (stream: Duplex<Uint8Array>, protocols: string | string[], options: ByteArrayInit): Promise<ProtocolStream<Uint8Array>>
|
|
17
|
+
export async function select (stream: Duplex<Uint8ArrayList, Uint8ArrayList | Uint8Array>, protocols: string | string[], options?: ByteListInit): Promise<ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>>
|
|
18
|
+
export async function select (stream: Duplex<any>, protocols: string | string[], options: MultistreamSelectInit = {}): Promise<ProtocolStream<any>> {
|
|
12
19
|
protocols = Array.isArray(protocols) ? [...protocols] : [protocols]
|
|
13
20
|
const { reader, writer, rest, stream: shakeStream } = handshake(stream)
|
|
14
21
|
|
|
@@ -18,19 +25,16 @@ export async function select (stream: Duplex<Uint8Array>, protocols: string | st
|
|
|
18
25
|
throw new Error('At least one protocol must be specified')
|
|
19
26
|
}
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
log('select: write "%s"', protocol)
|
|
26
|
-
multistream.write(writer, uint8ArrayFromString(protocol))
|
|
27
|
-
}
|
|
28
|
+
log('select: write ["%s", "%s"]', PROTOCOL_ID, protocol)
|
|
29
|
+
const p1 = uint8ArrayFromString(PROTOCOL_ID)
|
|
30
|
+
const p2 = uint8ArrayFromString(protocol)
|
|
31
|
+
multistream.writeAll(writer, [p1, p2], options)
|
|
28
32
|
|
|
29
33
|
let response = await multistream.readString(reader, options)
|
|
30
34
|
log('select: read "%s"', response)
|
|
31
35
|
|
|
32
36
|
// Read the protocol response if we got the protocolId in return
|
|
33
|
-
if (response ===
|
|
37
|
+
if (response === PROTOCOL_ID) {
|
|
34
38
|
response = await multistream.readString(reader, options)
|
|
35
39
|
log('select: read "%s"', response)
|
|
36
40
|
}
|
|
@@ -44,7 +48,7 @@ export async function select (stream: Duplex<Uint8Array>, protocols: string | st
|
|
|
44
48
|
// We haven't gotten a valid ack, try the other protocols
|
|
45
49
|
for (const protocol of protocols) {
|
|
46
50
|
log('select: write "%s"', protocol)
|
|
47
|
-
multistream.write(writer, uint8ArrayFromString(protocol))
|
|
51
|
+
multistream.write(writer, uint8ArrayFromString(protocol), options)
|
|
48
52
|
const response = await multistream.readString(reader, options)
|
|
49
53
|
log('select: read "%s" for "%s"', response, protocol)
|
|
50
54
|
|
|
@@ -57,3 +61,56 @@ export async function select (stream: Duplex<Uint8Array>, protocols: string | st
|
|
|
57
61
|
rest()
|
|
58
62
|
throw errCode(new Error('protocol selection failed'), 'ERR_UNSUPPORTED_PROTOCOL')
|
|
59
63
|
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Lazily negotiates a protocol.
|
|
67
|
+
*
|
|
68
|
+
* It *does not* block writes waiting for the other end to respond. Instead, it
|
|
69
|
+
* simply assumes the negotiation went successfully and starts writing data.
|
|
70
|
+
*
|
|
71
|
+
* Use when it is known that the receiver supports the desired protocol.
|
|
72
|
+
*/
|
|
73
|
+
export function lazySelect (stream: Duplex<Uint8Array>, protocol: string): ProtocolStream<Uint8Array>
|
|
74
|
+
export function lazySelect (stream: Duplex<Uint8ArrayList, Uint8ArrayList | Uint8Array>, protocol: string): ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>
|
|
75
|
+
export function lazySelect (stream: Duplex<any>, protocol: string): ProtocolStream<any> {
|
|
76
|
+
// This is a signal to write the multistream headers if the consumer tries to
|
|
77
|
+
// read from the source
|
|
78
|
+
const negotiateTrigger = pushable()
|
|
79
|
+
let negotiated = false
|
|
80
|
+
return {
|
|
81
|
+
stream: {
|
|
82
|
+
sink: async source => await stream.sink((async function * () {
|
|
83
|
+
let first = true
|
|
84
|
+
for await (const chunk of merge(source, negotiateTrigger)) {
|
|
85
|
+
if (first) {
|
|
86
|
+
first = false
|
|
87
|
+
negotiated = true
|
|
88
|
+
negotiateTrigger.end()
|
|
89
|
+
const p1 = uint8ArrayFromString(PROTOCOL_ID)
|
|
90
|
+
const p2 = uint8ArrayFromString(protocol)
|
|
91
|
+
const list = new Uint8ArrayList(multistream.encode(p1), multistream.encode(p2))
|
|
92
|
+
if (chunk.length > 0) list.append(chunk)
|
|
93
|
+
yield * list
|
|
94
|
+
} else {
|
|
95
|
+
yield chunk
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
})()),
|
|
99
|
+
source: (async function * () {
|
|
100
|
+
if (!negotiated) negotiateTrigger.push(new Uint8Array())
|
|
101
|
+
const byteReader = reader(stream.source)
|
|
102
|
+
let response = await multistream.readString(byteReader)
|
|
103
|
+
if (response === PROTOCOL_ID) {
|
|
104
|
+
response = await multistream.readString(byteReader)
|
|
105
|
+
}
|
|
106
|
+
if (response !== protocol) {
|
|
107
|
+
throw errCode(new Error('protocol selection failed'), 'ERR_UNSUPPORTED_PROTOCOL')
|
|
108
|
+
}
|
|
109
|
+
for await (const chunk of byteReader) {
|
|
110
|
+
yield * chunk
|
|
111
|
+
}
|
|
112
|
+
})()
|
|
113
|
+
},
|
|
114
|
+
protocol
|
|
115
|
+
}
|
|
116
|
+
}
|