@libp2p/multistream-select 4.0.6-05b52d69c → 4.0.6-3dee5df4d

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.
@@ -1,93 +1,133 @@
1
1
  import { CodeError } from '@libp2p/interface/errors';
2
- import { handshake } from 'it-handshake';
3
- import merge from 'it-merge';
4
- import { pushable } from 'it-pushable';
5
- import { reader } from 'it-reader';
6
- import { Uint8ArrayList } from 'uint8arraylist';
2
+ import { lpStream } from 'it-length-prefixed-stream';
7
3
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
4
+ import { MAX_PROTOCOL_LENGTH } from './constants.js';
8
5
  import * as multistream from './multistream.js';
9
6
  import { PROTOCOL_ID } from './index.js';
7
+ /**
8
+ * Negotiate a protocol to use from a list of protocols.
9
+ *
10
+ * @param stream - A duplex iterable stream to dial on
11
+ * @param protocols - A list of protocols (or single protocol) to negotiate with. Protocols are attempted in order until a match is made.
12
+ * @param options - 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
13
+ * @returns A stream for the selected protocol and the protocol that was selected from the list of protocols provided to `select`.
14
+ * @example
15
+ *
16
+ * ```js
17
+ * import { pipe } from 'it-pipe'
18
+ * import * as mss from '@libp2p/multistream-select'
19
+ * import { Mplex } from '@libp2p/mplex'
20
+ *
21
+ * const muxer = new Mplex()
22
+ * const muxedStream = muxer.newStream()
23
+ *
24
+ * // mss.select(protocol(s))
25
+ * // Select from one of the passed protocols (in priority order)
26
+ * // Returns selected stream and protocol
27
+ * const { stream: dhtStream, protocol } = await mss.select(muxedStream, [
28
+ * // This might just be different versions of DHT, but could be different impls
29
+ * '/ipfs-dht/2.0.0', // Most of the time this will probably just be one item.
30
+ * '/ipfs-dht/1.0.0'
31
+ * ])
32
+ *
33
+ * // Typically this stream will be passed back to the caller of libp2p.dialProtocol
34
+ * //
35
+ * // ...it might then do something like this:
36
+ * // try {
37
+ * // await pipe(
38
+ * // [uint8ArrayFromString('Some DHT data')]
39
+ * // dhtStream,
40
+ * // async source => {
41
+ * // for await (const chunk of source)
42
+ * // // DHT response data
43
+ * // }
44
+ * // )
45
+ * // } catch (err) {
46
+ * // // Error in stream
47
+ * // }
48
+ * ```
49
+ */
10
50
  export async function select(stream, protocols, options) {
11
51
  protocols = Array.isArray(protocols) ? [...protocols] : [protocols];
12
- const { reader, writer, rest, stream: shakeStream } = handshake(stream);
52
+ const lp = lpStream(stream, {
53
+ maxDataLength: MAX_PROTOCOL_LENGTH
54
+ });
13
55
  const protocol = protocols.shift();
14
56
  if (protocol == null) {
15
57
  throw new Error('At least one protocol must be specified');
16
58
  }
17
59
  options?.log.trace('select: write ["%s", "%s"]', PROTOCOL_ID, protocol);
18
- const p1 = uint8ArrayFromString(PROTOCOL_ID);
19
- const p2 = uint8ArrayFromString(protocol);
20
- multistream.writeAll(writer, [p1, p2], options);
21
- let response = await multistream.readString(reader, options);
60
+ const p1 = uint8ArrayFromString(`${PROTOCOL_ID}\n`);
61
+ const p2 = uint8ArrayFromString(`${protocol}\n`);
62
+ await multistream.writeAll(lp, [p1, p2], options);
63
+ options?.log.trace('select: reading multistream-select header');
64
+ let response = await multistream.readString(lp, options);
22
65
  options?.log.trace('select: read "%s"', response);
23
66
  // Read the protocol response if we got the protocolId in return
24
67
  if (response === PROTOCOL_ID) {
25
- response = await multistream.readString(reader, options);
68
+ options?.log.trace('select: reading protocol response');
69
+ response = await multistream.readString(lp, options);
26
70
  options?.log.trace('select: read "%s"', response);
27
71
  }
28
72
  // We're done
29
73
  if (response === protocol) {
30
- rest();
31
- return { stream: shakeStream, protocol };
74
+ return { stream: lp.unwrap(), protocol };
32
75
  }
33
76
  // We haven't gotten a valid ack, try the other protocols
34
77
  for (const protocol of protocols) {
35
78
  options?.log.trace('select: write "%s"', protocol);
36
- multistream.write(writer, uint8ArrayFromString(protocol), options);
37
- const response = await multistream.readString(reader, options);
79
+ await multistream.write(lp, uint8ArrayFromString(`${protocol}\n`), options);
80
+ options?.log.trace('select: reading protocol response');
81
+ const response = await multistream.readString(lp, options);
38
82
  options?.log.trace('select: read "%s" for "%s"', response, protocol);
39
83
  if (response === protocol) {
40
- rest(); // End our writer so others can start writing to stream
41
- return { stream: shakeStream, protocol };
84
+ return { stream: lp.unwrap(), protocol };
42
85
  }
43
86
  }
44
- rest();
45
87
  throw new CodeError('protocol selection failed', 'ERR_UNSUPPORTED_PROTOCOL');
46
88
  }
47
- export function lazySelect(stream, protocol) {
48
- // This is a signal to write the multistream headers if the consumer tries to
49
- // read from the source
50
- const negotiateTrigger = pushable();
51
- let negotiated = false;
89
+ /**
90
+ * Lazily negotiates a protocol.
91
+ *
92
+ * It *does not* block writes waiting for the other end to respond. Instead, it
93
+ * simply assumes the negotiation went successfully and starts writing data.
94
+ *
95
+ * Use when it is known that the receiver supports the desired protocol.
96
+ */
97
+ export function lazySelect(stream, protocol, options) {
98
+ const originalSink = stream.sink.bind(stream);
99
+ const originalSource = stream.source;
100
+ const lp = lpStream({
101
+ sink: originalSink,
102
+ source: originalSource
103
+ }, {
104
+ maxDataLength: MAX_PROTOCOL_LENGTH
105
+ });
106
+ stream.sink = async (source) => {
107
+ options?.log.trace('lazy: write ["%s", "%s"]', PROTOCOL_ID, protocol);
108
+ await lp.writeV([
109
+ uint8ArrayFromString(`${PROTOCOL_ID}\n`),
110
+ uint8ArrayFromString(`${protocol}\n`)
111
+ ]);
112
+ options?.log.trace('lazy: writing rest of "%s" stream', protocol);
113
+ await lp.unwrap().sink(source);
114
+ };
115
+ stream.source = (async function* () {
116
+ options?.log.trace('lazy: reading multistream select header');
117
+ let response = await multistream.readString(lp, options);
118
+ options?.log.trace('lazy: read multistream select header "%s"', response);
119
+ if (response === PROTOCOL_ID) {
120
+ response = await multistream.readString(lp, options);
121
+ }
122
+ options?.log.trace('lazy: read protocol "%s", expecting "%s"', response, protocol);
123
+ if (response !== protocol) {
124
+ throw new CodeError('protocol selection failed', 'ERR_UNSUPPORTED_PROTOCOL');
125
+ }
126
+ options?.log.trace('lazy: reading rest of "%s" stream', protocol);
127
+ yield* lp.unwrap().source;
128
+ })();
52
129
  return {
53
- stream: {
54
- sink: async (source) => {
55
- await stream.sink((async function* () {
56
- let first = true;
57
- for await (const chunk of merge(source, negotiateTrigger)) {
58
- if (first) {
59
- first = false;
60
- negotiated = true;
61
- negotiateTrigger.end();
62
- const p1 = uint8ArrayFromString(PROTOCOL_ID);
63
- const p2 = uint8ArrayFromString(protocol);
64
- const list = new Uint8ArrayList(multistream.encode(p1), multistream.encode(p2));
65
- if (chunk.length > 0)
66
- list.append(chunk);
67
- yield* list;
68
- }
69
- else {
70
- yield chunk;
71
- }
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 new CodeError('protocol selection failed', 'ERR_UNSUPPORTED_PROTOCOL');
85
- }
86
- for await (const chunk of byteReader) {
87
- yield* chunk;
88
- }
89
- })()
90
- },
130
+ stream,
91
131
  protocol
92
132
  };
93
133
  }
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/select.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/select.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@libp2p/multistream-select",
3
- "version": "4.0.6-05b52d69c",
3
+ "version": "4.0.6-3dee5df4d",
4
4
  "description": "JavaScript implementation of multistream-select",
5
5
  "license": "Apache-2.0 OR MIT",
6
- "homepage": "https://github.com/libp2p/js-libp2p/tree/master/packages/multistream-select#readme",
6
+ "homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/multistream-select#readme",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/libp2p/js-libp2p.git"
@@ -53,26 +53,20 @@
53
53
  "test:electron-main": "aegir test -t electron-main"
54
54
  },
55
55
  "dependencies": {
56
- "@libp2p/interface": "0.1.6-05b52d69c",
57
- "abortable-iterator": "^5.0.1",
58
- "it-first": "^3.0.3",
59
- "it-handshake": "^4.1.3",
56
+ "@libp2p/interface": "0.1.6-3dee5df4d",
60
57
  "it-length-prefixed": "^9.0.3",
61
- "it-merge": "^3.0.0",
58
+ "it-length-prefixed-stream": "^1.1.1",
62
59
  "it-pipe": "^3.0.1",
63
- "it-pushable": "^3.2.1",
64
- "it-reader": "^6.0.1",
65
60
  "it-stream-types": "^2.0.1",
66
- "uint8-varint": "^2.0.0",
67
61
  "uint8arraylist": "^2.4.3",
68
62
  "uint8arrays": "^4.0.6"
69
63
  },
70
64
  "devDependencies": {
71
- "@libp2p/logger": "3.1.0-05b52d69c",
65
+ "@libp2p/logger": "3.1.0-3dee5df4d",
72
66
  "aegir": "^41.0.2",
73
67
  "iso-random-stream": "^2.0.2",
74
68
  "it-all": "^3.0.3",
75
- "it-map": "^3.0.3",
69
+ "it-drain": "^3.0.5",
76
70
  "it-pair": "^2.0.6",
77
71
  "p-timeout": "^6.0.0"
78
72
  }
package/src/handle.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { handshake } from 'it-handshake'
1
+ import { encode } from 'it-length-prefixed'
2
+ import { lpStream } from 'it-length-prefixed-stream'
2
3
  import { Uint8ArrayList } from 'uint8arraylist'
3
4
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
4
- import { PROTOCOL_ID } from './constants.js'
5
+ import { MAX_PROTOCOL_LENGTH, PROTOCOL_ID } from './constants.js'
5
6
  import * as multistream from './multistream.js'
6
- import type { ByteArrayInit, ByteListInit, MultistreamSelectInit, ProtocolStream } from './index.js'
7
- import type { Duplex, Source } from 'it-stream-types'
7
+ import type { MultistreamSelectInit, ProtocolStream } from './index.js'
8
+ import type { Duplex } from 'it-stream-types'
8
9
 
9
10
  /**
10
11
  * Handle multistream protocol selections for the given list of protocols.
@@ -52,38 +53,43 @@ import type { Duplex, Source } from 'it-stream-types'
52
53
  * })
53
54
  * ```
54
55
  */
55
- export async function handle (stream: Duplex<Source<Uint8Array>, Source<Uint8Array>>, protocols: string | string[], options: ByteArrayInit): Promise<ProtocolStream<Uint8Array>>
56
- export async function handle (stream: Duplex<Source<Uint8ArrayList | Uint8Array>, Source<Uint8ArrayList | Uint8Array>>, protocols: string | string[], options?: ByteListInit): Promise<ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>>
57
- export async function handle (stream: any, protocols: string | string[], options?: MultistreamSelectInit): Promise<ProtocolStream<any>> {
56
+ export async function handle <Stream extends Duplex<any, any, any>> (stream: Stream, protocols: string | string[], options: MultistreamSelectInit): Promise<ProtocolStream<Stream>> {
58
57
  protocols = Array.isArray(protocols) ? protocols : [protocols]
59
- const { writer, reader, rest, stream: shakeStream } = handshake(stream)
58
+ const lp = lpStream(stream, {
59
+ maxDataLength: MAX_PROTOCOL_LENGTH
60
+ })
60
61
 
61
62
  while (true) {
62
- const protocol = await multistream.readString(reader, options)
63
- options?.log.trace('read "%s"', protocol)
63
+ options.log.trace('handle - available protocols %s', protocols)
64
+ const protocol = await multistream.readString(lp, options)
65
+ options.log.trace('read "%s"', protocol)
64
66
 
65
67
  if (protocol === PROTOCOL_ID) {
66
- options?.log.trace('respond with "%s" for "%s"', PROTOCOL_ID, protocol)
67
- multistream.write(writer, uint8ArrayFromString(PROTOCOL_ID), options)
68
+ options.log.trace('respond with "%s" for "%s"', PROTOCOL_ID, protocol)
69
+ await multistream.write(lp, uint8ArrayFromString(`${PROTOCOL_ID}\n`), options)
68
70
  continue
69
71
  }
70
72
 
71
73
  if (protocols.includes(protocol)) {
72
- multistream.write(writer, uint8ArrayFromString(protocol), options)
73
- options?.log.trace('respond with "%s" for "%s"', protocol, protocol)
74
- rest()
75
- return { stream: shakeStream, protocol }
74
+ options.log.trace('respond with "%s" for "%s"', protocol, protocol)
75
+ await multistream.write(lp, uint8ArrayFromString(`${protocol}\n`), options)
76
+
77
+ return { stream: lp.unwrap(), protocol }
76
78
  }
77
79
 
78
80
  if (protocol === 'ls') {
79
81
  // <varint-msg-len><varint-proto-name-len><proto-name>\n<varint-proto-name-len><proto-name>\n\n
80
- multistream.write(writer, new Uint8ArrayList(...protocols.map(p => multistream.encode(uint8ArrayFromString(p)))), options)
81
- // multistream.writeAll(writer, protocols.map(p => uint8ArrayFromString(p)))
82
- options?.log.trace('respond with "%s" for %s', protocols, protocol)
82
+ const protos = new Uint8ArrayList(
83
+ ...protocols.map(p => encode.single(uint8ArrayFromString(`${p}\n`))),
84
+ uint8ArrayFromString('\n')
85
+ )
86
+
87
+ await multistream.write(lp, protos, options)
88
+ options.log.trace('respond with "%s" for %s', protocols, protocol)
83
89
  continue
84
90
  }
85
91
 
86
- multistream.write(writer, uint8ArrayFromString('na'), options)
87
- options?.log('respond with "na" for "%s"', protocol)
92
+ options.log('respond with "na" for "%s"', protocol)
93
+ await multistream.write(lp, uint8ArrayFromString('na\n'), options)
88
94
  }
89
95
  }
package/src/index.ts CHANGED
@@ -22,25 +22,16 @@
22
22
 
23
23
  import { PROTOCOL_ID } from './constants.js'
24
24
  import type { AbortOptions, LoggerOptions } from '@libp2p/interface'
25
- import type { Duplex, Source } from 'it-stream-types'
26
25
 
27
26
  export { PROTOCOL_ID }
28
27
 
29
- export interface ProtocolStream<TSource, TSink = TSource, RSink = Promise<void>> {
30
- stream: Duplex<AsyncGenerator<TSource>, Source<TSink>, RSink>
28
+ export interface ProtocolStream<Stream> {
29
+ stream: Stream
31
30
  protocol: string
32
31
  }
33
32
 
34
- export interface ByteArrayInit extends AbortOptions, LoggerOptions {
35
- writeBytes: true
36
- }
37
-
38
- export interface ByteListInit extends AbortOptions, LoggerOptions {
39
- writeBytes?: false
40
- }
41
-
42
33
  export interface MultistreamSelectInit extends AbortOptions, LoggerOptions {
43
- writeBytes?: boolean
34
+
44
35
  }
45
36
 
46
37
  export { select, lazySelect } from './select.js'
@@ -1,95 +1,46 @@
1
1
  import { CodeError } from '@libp2p/interface/errors'
2
- import { abortableSource } from 'abortable-iterator'
3
- import first from 'it-first'
4
- import * as lp from 'it-length-prefixed'
5
- import { pipe } from 'it-pipe'
6
- import { Uint8ArrayList } from 'uint8arraylist'
2
+ import { type Uint8ArrayList } from 'uint8arraylist'
7
3
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
8
4
  import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
9
- import { MAX_PROTOCOL_LENGTH } from './constants.js'
10
5
  import type { MultistreamSelectInit } from '.'
11
6
  import type { AbortOptions, LoggerOptions } from '@libp2p/interface'
12
- import type { Pushable } from 'it-pushable'
13
- import type { Reader } from 'it-reader'
14
- import type { Source } from 'it-stream-types'
7
+ import type { LengthPrefixedStream } from 'it-length-prefixed-stream'
8
+ import type { Duplex, Source } from 'it-stream-types'
15
9
 
16
10
  const NewLine = uint8ArrayFromString('\n')
17
11
 
18
- export function encode (buffer: Uint8Array | Uint8ArrayList): Uint8ArrayList {
19
- const list = new Uint8ArrayList(buffer, NewLine)
20
-
21
- return lp.encode.single(list)
22
- }
23
-
24
12
  /**
25
13
  * `write` encodes and writes a single buffer
26
14
  */
27
- export function write (writer: Pushable<any>, buffer: Uint8Array | Uint8ArrayList, options?: MultistreamSelectInit): void {
28
- const encoded = encode(buffer)
29
-
30
- if (options?.writeBytes === true) {
31
- writer.push(encoded.subarray())
32
- } else {
33
- writer.push(encoded)
34
- }
15
+ export async function write (writer: LengthPrefixedStream<Duplex<AsyncGenerator<Uint8Array>, Source<Uint8Array>>>, buffer: Uint8Array | Uint8ArrayList, options?: MultistreamSelectInit): Promise<void> {
16
+ await writer.write(buffer, options)
35
17
  }
36
18
 
37
19
  /**
38
20
  * `writeAll` behaves like `write`, except it encodes an array of items as a single write
39
21
  */
40
- export function writeAll (writer: Pushable<any>, buffers: Uint8Array[], options?: MultistreamSelectInit): void {
41
- const list = new Uint8ArrayList()
42
-
43
- for (const buf of buffers) {
44
- list.append(encode(buf))
45
- }
46
-
47
- if (options?.writeBytes === true) {
48
- writer.push(list.subarray())
49
- } else {
50
- writer.push(list)
51
- }
22
+ export async function writeAll (writer: LengthPrefixedStream<Duplex<AsyncGenerator<Uint8Array>, Source<Uint8Array>>>, buffers: Uint8Array[], options?: MultistreamSelectInit): Promise<void> {
23
+ await writer.writeV(buffers, options)
52
24
  }
53
25
 
54
- export async function read (reader: Reader, options?: AbortOptions & LoggerOptions): Promise<Uint8ArrayList> {
55
- let byteLength = 1 // Read single byte chunks until the length is known
56
- const varByteSource = { // No return impl - we want the reader to remain readable
57
- [Symbol.asyncIterator]: () => varByteSource,
58
- next: async () => reader.next(byteLength)
59
- }
60
-
61
- let input: Source<Uint8ArrayList> = varByteSource
62
-
63
- // If we have been passed an abort signal, wrap the input source in an abortable
64
- // iterator that will throw if the operation is aborted
65
- if (options?.signal != null) {
66
- input = abortableSource(varByteSource, options.signal)
67
- }
68
-
69
- // Once the length has been parsed, read chunk for that length
70
- const onLength = (l: number): void => {
71
- byteLength = l
72
- }
73
-
74
- const buf = await pipe(
75
- input,
76
- (source) => lp.decode(source, { onLength, maxDataLength: MAX_PROTOCOL_LENGTH }),
77
- async (source) => first(source)
78
- )
79
-
80
- if (buf == null || buf.length === 0) {
81
- throw new CodeError('no buffer returned', 'ERR_INVALID_MULTISTREAM_SELECT_MESSAGE')
82
- }
26
+ /**
27
+ * Read a length-prefixed buffer from the passed stream, stripping the final newline character
28
+ */
29
+ export async function read (reader: LengthPrefixedStream<Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>, Source<Uint8Array>>>, options?: AbortOptions & LoggerOptions): Promise<Uint8ArrayList> {
30
+ const buf = await reader.read(options)
83
31
 
84
- if (buf.get(buf.byteLength - 1) !== NewLine[0]) {
85
- options?.log.error('Invalid mss message - missing newline - %s', buf.subarray())
32
+ if (buf.byteLength === 0 || buf.get(buf.byteLength - 1) !== NewLine[0]) {
33
+ options?.log.error('Invalid mss message - missing newline', buf)
86
34
  throw new CodeError('missing newline', 'ERR_INVALID_MULTISTREAM_SELECT_MESSAGE')
87
35
  }
88
36
 
89
37
  return buf.sublist(0, -1) // Remove newline
90
38
  }
91
39
 
92
- export async function readString (reader: Reader, options?: AbortOptions & LoggerOptions): Promise<string> {
40
+ /**
41
+ * Read a length-prefixed string from the passed stream, stripping the final newline character
42
+ */
43
+ export async function readString (reader: LengthPrefixedStream<Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>, Source<Uint8Array>>>, options?: AbortOptions & LoggerOptions): Promise<string> {
93
44
  const buf = await read(reader, options)
94
45
 
95
46
  return uint8ArrayToString(buf.subarray())