@libp2p/multistream-select 4.0.6-0f5c305af → 4.0.6-6625a27fc

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/src/select.ts CHANGED
@@ -1,17 +1,11 @@
1
1
  import { CodeError } from '@libp2p/interface/errors'
2
- import { logger } from '@libp2p/logger'
3
- import { handshake } from 'it-handshake'
4
- import merge from 'it-merge'
5
- import { pushable } from 'it-pushable'
6
- import { reader } from 'it-reader'
7
- import { Uint8ArrayList } from 'uint8arraylist'
2
+ import { lpStream } from 'it-length-prefixed-stream'
8
3
  import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
4
+ import { MAX_PROTOCOL_LENGTH } from './constants.js'
9
5
  import * as multistream from './multistream.js'
10
6
  import { PROTOCOL_ID } from './index.js'
11
- import type { ByteArrayInit, ByteListInit, MultistreamSelectInit, ProtocolStream } from './index.js'
12
- import type { Duplex, Source } from 'it-stream-types'
13
-
14
- const log = logger('libp2p:mss:select')
7
+ import type { MultistreamSelectInit, ProtocolStream } from './index.js'
8
+ import type { Duplex } from 'it-stream-types'
15
9
 
16
10
  /**
17
11
  * Negotiate a protocol to use from a list of protocols.
@@ -56,52 +50,48 @@ const log = logger('libp2p:mss:select')
56
50
  * // }
57
51
  * ```
58
52
  */
59
- export async function select (stream: Duplex<AsyncGenerator<Uint8Array>, Source<Uint8Array>>, protocols: string | string[], options: ByteArrayInit): Promise<ProtocolStream<Uint8Array>>
60
- export async function select (stream: Duplex<AsyncGenerator<Uint8ArrayList | Uint8Array>, Source<Uint8ArrayList | Uint8Array>>, protocols: string | string[], options?: ByteListInit): Promise<ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>>
61
- export async function select (stream: any, protocols: string | string[], options: MultistreamSelectInit = {}): Promise<ProtocolStream<any>> {
53
+ export async function select <Stream extends Duplex<any, any, any>> (stream: Stream, protocols: string | string[], options: MultistreamSelectInit): Promise<ProtocolStream<Stream>> {
62
54
  protocols = Array.isArray(protocols) ? [...protocols] : [protocols]
63
- const { reader, writer, rest, stream: shakeStream } = handshake(stream)
64
-
55
+ const lp = lpStream(stream, {
56
+ maxDataLength: MAX_PROTOCOL_LENGTH
57
+ })
65
58
  const protocol = protocols.shift()
66
59
 
67
60
  if (protocol == null) {
68
61
  throw new Error('At least one protocol must be specified')
69
62
  }
70
63
 
71
- log.trace('select: write ["%s", "%s"]', PROTOCOL_ID, protocol)
72
- const p1 = uint8ArrayFromString(PROTOCOL_ID)
73
- const p2 = uint8ArrayFromString(protocol)
74
- multistream.writeAll(writer, [p1, p2], options)
64
+ options?.log.trace('select: write ["%s", "%s"]', PROTOCOL_ID, protocol)
65
+ const p1 = uint8ArrayFromString(`${PROTOCOL_ID}\n`)
66
+ const p2 = uint8ArrayFromString(`${protocol}\n`)
67
+ await multistream.writeAll(lp, [p1, p2], options)
75
68
 
76
- let response = await multistream.readString(reader, options)
77
- log.trace('select: read "%s"', response)
69
+ let response = await multistream.readString(lp, options)
70
+ options?.log.trace('select: read "%s"', response)
78
71
 
79
72
  // Read the protocol response if we got the protocolId in return
80
73
  if (response === PROTOCOL_ID) {
81
- response = await multistream.readString(reader, options)
82
- log.trace('select: read "%s"', response)
74
+ response = await multistream.readString(lp, options)
75
+ options?.log.trace('select: read "%s"', response)
83
76
  }
84
77
 
85
78
  // We're done
86
79
  if (response === protocol) {
87
- rest()
88
- return { stream: shakeStream, protocol }
80
+ return { stream: lp.unwrap(), protocol }
89
81
  }
90
82
 
91
83
  // We haven't gotten a valid ack, try the other protocols
92
84
  for (const protocol of protocols) {
93
- log.trace('select: write "%s"', protocol)
94
- multistream.write(writer, uint8ArrayFromString(protocol), options)
95
- const response = await multistream.readString(reader, options)
96
- log.trace('select: read "%s" for "%s"', response, protocol)
85
+ options?.log.trace('select: write "%s"', protocol)
86
+ await multistream.write(lp, uint8ArrayFromString(`${protocol}\n`), options)
87
+ const response = await multistream.readString(lp, options)
88
+ options?.log.trace('select: read "%s" for "%s"', response, protocol)
97
89
 
98
90
  if (response === protocol) {
99
- rest() // End our writer so others can start writing to stream
100
- return { stream: shakeStream, protocol }
91
+ return { stream: lp.unwrap(), protocol }
101
92
  }
102
93
  }
103
94
 
104
- rest()
105
95
  throw new CodeError('protocol selection failed', 'ERR_UNSUPPORTED_PROTOCOL')
106
96
  }
107
97
 
@@ -113,49 +103,51 @@ export async function select (stream: any, protocols: string | string[], options
113
103
  *
114
104
  * Use when it is known that the receiver supports the desired protocol.
115
105
  */
116
- export function lazySelect (stream: Duplex<Source<Uint8Array>, Source<Uint8Array>>, protocol: string): ProtocolStream<Uint8Array>
117
- export function lazySelect (stream: Duplex<Source<Uint8ArrayList | Uint8Array>, Source<Uint8ArrayList | Uint8Array>>, protocol: string): ProtocolStream<Uint8ArrayList, Uint8ArrayList | Uint8Array>
118
- export function lazySelect (stream: Duplex<any>, protocol: string): ProtocolStream<any> {
119
- // This is a signal to write the multistream headers if the consumer tries to
120
- // read from the source
121
- const negotiateTrigger = pushable()
122
- let negotiated = false
106
+ export function lazySelect <Stream extends Duplex<any, any, any>> (stream: Stream, protocol: string, options: MultistreamSelectInit): ProtocolStream<Stream> {
107
+ const originalSink = stream.sink.bind(stream)
108
+ const originalSource = stream.source
109
+
110
+ const lp = lpStream({
111
+ sink: originalSink,
112
+ source: originalSource
113
+ }, {
114
+ maxDataLength: MAX_PROTOCOL_LENGTH
115
+ })
116
+
117
+ stream.sink = async source => {
118
+ options?.log.trace('lazy: write ["%s", "%s"]', PROTOCOL_ID, protocol)
119
+
120
+ await lp.writeV([
121
+ uint8ArrayFromString(`${PROTOCOL_ID}\n`),
122
+ uint8ArrayFromString(`${protocol}\n`)
123
+ ])
124
+
125
+ options?.log.trace('lazy: writing rest of "%s" stream', protocol)
126
+ await lp.unwrap().sink(source)
127
+ }
128
+
129
+ stream.source = (async function * () {
130
+ options?.log.trace('lazy: reading multistream select header')
131
+
132
+ let response = await multistream.readString(lp, options)
133
+ options?.log.trace('lazy: read multistream select header "%s"', response)
134
+
135
+ if (response === PROTOCOL_ID) {
136
+ response = await multistream.readString(lp, options)
137
+ }
138
+
139
+ options?.log.trace('lazy: read protocol "%s", expecting "%s"', response, protocol)
140
+
141
+ if (response !== protocol) {
142
+ throw new CodeError('protocol selection failed', 'ERR_UNSUPPORTED_PROTOCOL')
143
+ }
144
+
145
+ options?.log.trace('lazy: reading rest of "%s" stream', protocol)
146
+ yield * lp.unwrap().source
147
+ })()
148
+
123
149
  return {
124
- stream: {
125
- sink: async source => {
126
- await stream.sink((async function * () {
127
- let first = true
128
- for await (const chunk of merge(source, negotiateTrigger)) {
129
- if (first) {
130
- first = false
131
- negotiated = true
132
- negotiateTrigger.end()
133
- const p1 = uint8ArrayFromString(PROTOCOL_ID)
134
- const p2 = uint8ArrayFromString(protocol)
135
- const list = new Uint8ArrayList(multistream.encode(p1), multistream.encode(p2))
136
- if (chunk.length > 0) list.append(chunk)
137
- yield * list
138
- } else {
139
- yield chunk
140
- }
141
- }
142
- })())
143
- },
144
- source: (async function * () {
145
- if (!negotiated) negotiateTrigger.push(new Uint8Array())
146
- const byteReader = reader(stream.source)
147
- let response = await multistream.readString(byteReader)
148
- if (response === PROTOCOL_ID) {
149
- response = await multistream.readString(byteReader)
150
- }
151
- if (response !== protocol) {
152
- throw new CodeError('protocol selection failed', 'ERR_UNSUPPORTED_PROTOCOL')
153
- }
154
- for await (const chunk of byteReader) {
155
- yield * chunk
156
- }
157
- })()
158
- },
150
+ stream,
159
151
  protocol
160
152
  }
161
153
  }