@libp2p/multistream-select 4.0.9-f537b3731 → 4.0.10
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 +1 -1
- package/dist/index.min.js +2 -14
- package/dist/src/handle.d.ts +5 -3
- package/dist/src/handle.d.ts.map +1 -1
- package/dist/src/handle.js +18 -72
- package/dist/src/handle.js.map +1 -1
- package/dist/src/index.d.ts +13 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/multistream.d.ts +9 -14
- package/dist/src/multistream.d.ts.map +1 -1
- package/dist/src/multistream.js +53 -15
- package/dist/src/multistream.js.map +1 -1
- package/dist/src/select.d.ts +15 -10
- package/dist/src/select.d.ts.map +1 -1
- package/dist/src/select.js +66 -249
- package/dist/src/select.js.map +1 -1
- package/package.json +14 -14
- package/src/handle.ts +24 -35
- package/src/index.ts +14 -6
- package/src/multistream.ts +72 -20
- package/src/select.ts +71 -251
package/src/multistream.ts
CHANGED
|
@@ -1,46 +1,98 @@
|
|
|
1
|
-
import { CodeError } from '@libp2p/interface'
|
|
2
|
-
import {
|
|
1
|
+
import { CodeError } from '@libp2p/interface/errors'
|
|
2
|
+
import { logger } from '@libp2p/logger'
|
|
3
|
+
import { abortableSource } from 'abortable-iterator'
|
|
4
|
+
import first from 'it-first'
|
|
5
|
+
import * as lp from 'it-length-prefixed'
|
|
6
|
+
import { pipe } from 'it-pipe'
|
|
7
|
+
import { Uint8ArrayList } from 'uint8arraylist'
|
|
3
8
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
4
9
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
10
|
+
import { MAX_PROTOCOL_LENGTH } from './constants.js'
|
|
5
11
|
import type { MultistreamSelectInit } from '.'
|
|
6
|
-
import type { AbortOptions
|
|
7
|
-
import type {
|
|
8
|
-
import type {
|
|
12
|
+
import type { AbortOptions } from '@libp2p/interface'
|
|
13
|
+
import type { Pushable } from 'it-pushable'
|
|
14
|
+
import type { Reader } from 'it-reader'
|
|
15
|
+
import type { Source } from 'it-stream-types'
|
|
16
|
+
|
|
17
|
+
const log = logger('libp2p:mss')
|
|
9
18
|
|
|
10
19
|
const NewLine = uint8ArrayFromString('\n')
|
|
11
20
|
|
|
21
|
+
export function encode (buffer: Uint8Array | Uint8ArrayList): Uint8ArrayList {
|
|
22
|
+
const list = new Uint8ArrayList(buffer, NewLine)
|
|
23
|
+
|
|
24
|
+
return lp.encode.single(list)
|
|
25
|
+
}
|
|
26
|
+
|
|
12
27
|
/**
|
|
13
28
|
* `write` encodes and writes a single buffer
|
|
14
29
|
*/
|
|
15
|
-
export
|
|
16
|
-
|
|
30
|
+
export function write (writer: Pushable<any>, buffer: Uint8Array | Uint8ArrayList, options: MultistreamSelectInit = {}): void {
|
|
31
|
+
const encoded = encode(buffer)
|
|
32
|
+
|
|
33
|
+
if (options.writeBytes === true) {
|
|
34
|
+
writer.push(encoded.subarray())
|
|
35
|
+
} else {
|
|
36
|
+
writer.push(encoded)
|
|
37
|
+
}
|
|
17
38
|
}
|
|
18
39
|
|
|
19
40
|
/**
|
|
20
41
|
* `writeAll` behaves like `write`, except it encodes an array of items as a single write
|
|
21
42
|
*/
|
|
22
|
-
export
|
|
23
|
-
|
|
43
|
+
export function writeAll (writer: Pushable<any>, buffers: Uint8Array[], options: MultistreamSelectInit = {}): void {
|
|
44
|
+
const list = new Uint8ArrayList()
|
|
45
|
+
|
|
46
|
+
for (const buf of buffers) {
|
|
47
|
+
list.append(encode(buf))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (options.writeBytes === true) {
|
|
51
|
+
writer.push(list.subarray())
|
|
52
|
+
} else {
|
|
53
|
+
writer.push(list)
|
|
54
|
+
}
|
|
24
55
|
}
|
|
25
56
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
57
|
+
export async function read (reader: Reader, options?: AbortOptions): Promise<Uint8ArrayList> {
|
|
58
|
+
let byteLength = 1 // Read single byte chunks until the length is known
|
|
59
|
+
const varByteSource = { // No return impl - we want the reader to remain readable
|
|
60
|
+
[Symbol.asyncIterator]: () => varByteSource,
|
|
61
|
+
next: async () => reader.next(byteLength)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let input: Source<Uint8ArrayList> = varByteSource
|
|
65
|
+
|
|
66
|
+
// If we have been passed an abort signal, wrap the input source in an abortable
|
|
67
|
+
// iterator that will throw if the operation is aborted
|
|
68
|
+
if (options?.signal != null) {
|
|
69
|
+
input = abortableSource(varByteSource, options.signal)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Once the length has been parsed, read chunk for that length
|
|
73
|
+
const onLength = (l: number): void => {
|
|
74
|
+
byteLength = l
|
|
75
|
+
}
|
|
31
76
|
|
|
32
|
-
|
|
33
|
-
|
|
77
|
+
const buf = await pipe(
|
|
78
|
+
input,
|
|
79
|
+
(source) => lp.decode(source, { onLength, maxDataLength: MAX_PROTOCOL_LENGTH }),
|
|
80
|
+
async (source) => first(source)
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if (buf == null || buf.length === 0) {
|
|
84
|
+
throw new CodeError('no buffer returned', 'ERR_INVALID_MULTISTREAM_SELECT_MESSAGE')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (buf.get(buf.byteLength - 1) !== NewLine[0]) {
|
|
88
|
+
log.error('Invalid mss message - missing newline - %s', buf.subarray())
|
|
34
89
|
throw new CodeError('missing newline', 'ERR_INVALID_MULTISTREAM_SELECT_MESSAGE')
|
|
35
90
|
}
|
|
36
91
|
|
|
37
92
|
return buf.sublist(0, -1) // Remove newline
|
|
38
93
|
}
|
|
39
94
|
|
|
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> {
|
|
95
|
+
export async function readString (reader: Reader, options?: AbortOptions): Promise<string> {
|
|
44
96
|
const buf = await read(reader, options)
|
|
45
97
|
|
|
46
98
|
return uint8ArrayToString(buf.subarray())
|
package/src/select.ts
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
import { CodeError } from '@libp2p/interface'
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
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'
|
|
5
7
|
import { Uint8ArrayList } from 'uint8arraylist'
|
|
6
8
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
7
|
-
import { MAX_PROTOCOL_LENGTH } from './constants.js'
|
|
8
9
|
import * as multistream from './multistream.js'
|
|
9
10
|
import { PROTOCOL_ID } from './index.js'
|
|
10
|
-
import type { MultistreamSelectInit, ProtocolStream } from './index.js'
|
|
11
|
-
import type {
|
|
12
|
-
import type { Duplex } from 'it-stream-types'
|
|
11
|
+
import type { ByteArrayInit, ByteListInit, MultistreamSelectInit, ProtocolStream } from './index.js'
|
|
12
|
+
import type { Duplex, Source } from 'it-stream-types'
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
readStatus?: string
|
|
16
|
-
closeWrite?(options?: AbortOptions): Promise<void>
|
|
17
|
-
closeRead?(options?: AbortOptions): Promise<void>
|
|
18
|
-
close?(options?: AbortOptions): Promise<void>
|
|
19
|
-
}
|
|
14
|
+
const log = logger('libp2p:mss:select')
|
|
20
15
|
|
|
21
16
|
/**
|
|
22
17
|
* Negotiate a protocol to use from a list of protocols.
|
|
@@ -61,281 +56,106 @@ export interface SelectStream extends Duplex<any, any, any> {
|
|
|
61
56
|
* // }
|
|
62
57
|
* ```
|
|
63
58
|
*/
|
|
64
|
-
export async function select
|
|
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>> {
|
|
65
62
|
protocols = Array.isArray(protocols) ? [...protocols] : [protocols]
|
|
63
|
+
const { reader, writer, rest, stream: shakeStream } = handshake(stream)
|
|
66
64
|
|
|
67
|
-
if (protocols.length === 1) {
|
|
68
|
-
return optimisticSelect(stream, protocols[0], options)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const lp = lpStream(stream, {
|
|
72
|
-
...options,
|
|
73
|
-
maxDataLength: MAX_PROTOCOL_LENGTH
|
|
74
|
-
})
|
|
75
65
|
const protocol = protocols.shift()
|
|
76
66
|
|
|
77
67
|
if (protocol == null) {
|
|
78
68
|
throw new Error('At least one protocol must be specified')
|
|
79
69
|
}
|
|
80
70
|
|
|
81
|
-
|
|
82
|
-
const p1 = uint8ArrayFromString(
|
|
83
|
-
const p2 = uint8ArrayFromString(
|
|
84
|
-
|
|
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)
|
|
85
75
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
options?.log?.trace('select: read "%s"', response)
|
|
76
|
+
let response = await multistream.readString(reader, options)
|
|
77
|
+
log.trace('select: read "%s"', response)
|
|
89
78
|
|
|
90
79
|
// Read the protocol response if we got the protocolId in return
|
|
91
80
|
if (response === PROTOCOL_ID) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
options?.log?.trace('select: read "%s"', response)
|
|
81
|
+
response = await multistream.readString(reader, options)
|
|
82
|
+
log.trace('select: read "%s"', response)
|
|
95
83
|
}
|
|
96
84
|
|
|
97
85
|
// We're done
|
|
98
86
|
if (response === protocol) {
|
|
99
|
-
|
|
87
|
+
rest()
|
|
88
|
+
return { stream: shakeStream, protocol }
|
|
100
89
|
}
|
|
101
90
|
|
|
102
91
|
// We haven't gotten a valid ack, try the other protocols
|
|
103
92
|
for (const protocol of protocols) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
options?.log?.trace('select: read "%s" for "%s"', response, protocol)
|
|
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)
|
|
109
97
|
|
|
110
98
|
if (response === protocol) {
|
|
111
|
-
|
|
99
|
+
rest() // End our writer so others can start writing to stream
|
|
100
|
+
return { stream: shakeStream, protocol }
|
|
112
101
|
}
|
|
113
102
|
}
|
|
114
103
|
|
|
104
|
+
rest()
|
|
115
105
|
throw new CodeError('protocol selection failed', 'ERR_UNSUPPORTED_PROTOCOL')
|
|
116
106
|
}
|
|
117
107
|
|
|
118
108
|
/**
|
|
119
|
-
*
|
|
109
|
+
* Lazily negotiates a protocol.
|
|
120
110
|
*
|
|
121
111
|
* It *does not* block writes waiting for the other end to respond. Instead, it
|
|
122
112
|
* simply assumes the negotiation went successfully and starts writing data.
|
|
123
113
|
*
|
|
124
114
|
* Use when it is known that the receiver supports the desired protocol.
|
|
125
115
|
*/
|
|
126
|
-
function
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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()
|
|
130
122
|
let negotiated = false
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (sendingProtocol) {
|
|
159
|
-
await doneSendingProtocol.promise
|
|
123
|
+
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)
|
|
160
150
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (!sentProtocol) {
|
|
164
|
-
sendingProtocol = true
|
|
165
|
-
|
|
166
|
-
options?.log?.trace('optimistic: write ["%s", "%s", data(%d)] in sink', PROTOCOL_ID, protocol, buf.byteLength)
|
|
167
|
-
|
|
168
|
-
const protocolString = `${protocol}\n`
|
|
169
|
-
|
|
170
|
-
// send protocols in first chunk of data written to transport
|
|
171
|
-
yield new Uint8ArrayList(
|
|
172
|
-
Uint8Array.from([19]), // length of PROTOCOL_ID plus newline
|
|
173
|
-
uint8ArrayFromString(`${PROTOCOL_ID}\n`),
|
|
174
|
-
varint.encode(protocolString.length),
|
|
175
|
-
uint8ArrayFromString(protocolString),
|
|
176
|
-
buf
|
|
177
|
-
).subarray()
|
|
178
|
-
|
|
179
|
-
options?.log?.trace('optimistic: wrote ["%s", "%s", data(%d)] in sink', PROTOCOL_ID, protocol, buf.byteLength)
|
|
180
|
-
|
|
181
|
-
sentProtocol = true
|
|
182
|
-
sendingProtocol = false
|
|
183
|
-
doneSendingProtocol.resolve()
|
|
184
|
-
} else {
|
|
185
|
-
yield buf
|
|
151
|
+
if (response !== protocol) {
|
|
152
|
+
throw new CodeError('protocol selection failed', 'ERR_UNSUPPORTED_PROTOCOL')
|
|
186
153
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
// negotiated the protocol yet so do it now
|
|
193
|
-
if (!sentData) {
|
|
194
|
-
await negotiate()
|
|
195
|
-
}
|
|
196
|
-
}())
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
async function negotiate (): Promise<void> {
|
|
200
|
-
if (negotiating) {
|
|
201
|
-
options?.log?.trace('optimistic: already negotiating %s stream', protocol)
|
|
202
|
-
await doneNegotiating.promise
|
|
203
|
-
return
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
negotiating = true
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
// we haven't sent the protocol yet, send it now
|
|
210
|
-
if (!sentProtocol) {
|
|
211
|
-
options?.log?.trace('optimistic: doing send protocol for %s stream', protocol)
|
|
212
|
-
await doSendProtocol()
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// if we haven't read the protocol response yet, do it now
|
|
216
|
-
if (!readProtocol) {
|
|
217
|
-
options?.log?.trace('optimistic: doing read protocol for %s stream', protocol)
|
|
218
|
-
await doReadProtocol()
|
|
219
|
-
}
|
|
220
|
-
} finally {
|
|
221
|
-
negotiating = false
|
|
222
|
-
negotiated = true
|
|
223
|
-
doneNegotiating.resolve()
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async function doSendProtocol (): Promise<void> {
|
|
228
|
-
if (sendingProtocol) {
|
|
229
|
-
await doneSendingProtocol.promise
|
|
230
|
-
return
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
sendingProtocol = true
|
|
234
|
-
|
|
235
|
-
try {
|
|
236
|
-
options?.log?.trace('optimistic: write ["%s", "%s", data] in source', PROTOCOL_ID, protocol)
|
|
237
|
-
await lp.writeV([
|
|
238
|
-
uint8ArrayFromString(`${PROTOCOL_ID}\n`),
|
|
239
|
-
uint8ArrayFromString(`${protocol}\n`)
|
|
240
|
-
])
|
|
241
|
-
options?.log?.trace('optimistic: wrote ["%s", "%s", data] in source', PROTOCOL_ID, protocol)
|
|
242
|
-
} finally {
|
|
243
|
-
sentProtocol = true
|
|
244
|
-
sendingProtocol = false
|
|
245
|
-
doneSendingProtocol.resolve()
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
async function doReadProtocol (): Promise<void> {
|
|
250
|
-
if (readingProtocol) {
|
|
251
|
-
await doneReadingProtocol.promise
|
|
252
|
-
return
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
readingProtocol = true
|
|
256
|
-
|
|
257
|
-
try {
|
|
258
|
-
options?.log?.trace('optimistic: reading multistream select header')
|
|
259
|
-
let response = await multistream.readString(lp, options)
|
|
260
|
-
options?.log?.trace('optimistic: read multistream select header "%s"', response)
|
|
261
|
-
|
|
262
|
-
if (response === PROTOCOL_ID) {
|
|
263
|
-
response = await multistream.readString(lp, options)
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
options?.log?.trace('optimistic: read protocol "%s", expecting "%s"', response, protocol)
|
|
267
|
-
|
|
268
|
-
if (response !== protocol) {
|
|
269
|
-
throw new CodeError('protocol selection failed', 'ERR_UNSUPPORTED_PROTOCOL')
|
|
270
|
-
}
|
|
271
|
-
} finally {
|
|
272
|
-
readProtocol = true
|
|
273
|
-
readingProtocol = false
|
|
274
|
-
doneReadingProtocol.resolve()
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
stream.source = (async function * () {
|
|
279
|
-
// make sure we've done protocol negotiation before we read stream data
|
|
280
|
-
await negotiate()
|
|
281
|
-
|
|
282
|
-
options?.log?.trace('optimistic: reading data from "%s" stream', protocol)
|
|
283
|
-
yield * lp.unwrap().source
|
|
284
|
-
})()
|
|
285
|
-
|
|
286
|
-
if (stream.closeRead != null) {
|
|
287
|
-
const originalCloseRead = stream.closeRead.bind(stream)
|
|
288
|
-
|
|
289
|
-
stream.closeRead = async (opts) => {
|
|
290
|
-
// we need to read & write to negotiate the protocol so ensure we've done
|
|
291
|
-
// this before closing the readable end of the stream
|
|
292
|
-
if (!negotiated) {
|
|
293
|
-
await negotiate().catch(err => {
|
|
294
|
-
options?.log?.error('could not negotiate protocol before close read', err)
|
|
295
|
-
})
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// protocol has been negotiated, ok to close the readable end
|
|
299
|
-
await originalCloseRead(opts)
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (stream.closeWrite != null) {
|
|
304
|
-
const originalCloseWrite = stream.closeWrite.bind(stream)
|
|
305
|
-
|
|
306
|
-
stream.closeWrite = async (opts) => {
|
|
307
|
-
// we need to read & write to negotiate the protocol so ensure we've done
|
|
308
|
-
// this before closing the writable end of the stream
|
|
309
|
-
if (!negotiated) {
|
|
310
|
-
await negotiate().catch(err => {
|
|
311
|
-
options?.log?.error('could not negotiate protocol before close write', err)
|
|
312
|
-
})
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// protocol has been negotiated, ok to close the writable end
|
|
316
|
-
await originalCloseWrite(opts)
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (stream.close != null) {
|
|
321
|
-
const originalClose = stream.close.bind(stream)
|
|
322
|
-
|
|
323
|
-
stream.close = async (opts) => {
|
|
324
|
-
// the stream is being closed, don't try to negotiate a protocol if we
|
|
325
|
-
// haven't already
|
|
326
|
-
if (!negotiated) {
|
|
327
|
-
negotiated = true
|
|
328
|
-
negotiating = false
|
|
329
|
-
doneNegotiating.resolve()
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// protocol has been negotiated, ok to close the writable end
|
|
333
|
-
await originalClose(opts)
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return {
|
|
338
|
-
stream,
|
|
154
|
+
for await (const chunk of byteReader) {
|
|
155
|
+
yield * chunk
|
|
156
|
+
}
|
|
157
|
+
})()
|
|
158
|
+
},
|
|
339
159
|
protocol
|
|
340
160
|
}
|
|
341
161
|
}
|