@libp2p/mplex 11.0.46-cf9aab5c8 → 11.0.47-0f07e3df5
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/dist/index.min.js +1 -1
- package/dist/index.min.js.map +4 -4
- package/dist/src/encode.d.ts +1 -2
- package/dist/src/encode.d.ts.map +1 -1
- package/dist/src/encode.js +4 -6
- package/dist/src/encode.js.map +1 -1
- package/dist/src/index.d.ts +5 -31
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/mplex.d.ts +11 -53
- package/dist/src/mplex.d.ts.map +1 -1
- package/dist/src/mplex.js +56 -222
- package/dist/src/mplex.js.map +1 -1
- package/dist/src/stream.d.ts +17 -23
- package/dist/src/stream.d.ts.map +1 -1
- package/dist/src/stream.js +56 -28
- package/dist/src/stream.js.map +1 -1
- package/package.json +12 -15
- package/src/encode.ts +5 -7
- package/src/index.ts +10 -43
- package/src/mplex.ts +58 -273
- package/src/stream.ts +78 -46
package/src/mplex.ts
CHANGED
@@ -1,27 +1,16 @@
|
|
1
|
-
import {
|
2
|
-
import {
|
3
|
-
import { RateLimiter } from '@libp2p/utils/rate-limiter'
|
4
|
-
import { pipe } from 'it-pipe'
|
5
|
-
import { pushable } from 'it-pushable'
|
1
|
+
import { MuxerClosedError } from '@libp2p/interface'
|
2
|
+
import { RateLimiter, AbstractStreamMuxer } from '@libp2p/utils'
|
6
3
|
import { toString as uint8ArrayToString } from 'uint8arrays'
|
7
|
-
import { Decoder } from './decode.js'
|
8
|
-
import { encode } from './encode.js'
|
9
|
-
import { StreamInputBufferError } from './errors.js'
|
4
|
+
import { Decoder, MAX_MSG_QUEUE_SIZE, MAX_MSG_SIZE } from './decode.js'
|
10
5
|
import { MessageTypes, MessageTypeNames } from './message-types.js'
|
11
6
|
import { createStream } from './stream.js'
|
12
7
|
import type { MplexInit } from './index.js'
|
13
8
|
import type { Message } from './message-types.js'
|
14
9
|
import type { MplexStream } from './stream.js'
|
15
|
-
import type {
|
16
|
-
import type { Pushable } from 'it-pushable'
|
17
|
-
import type { Sink, Source } from 'it-stream-types'
|
10
|
+
import type { CreateStreamOptions, MultiaddrConnection, MessageStreamDirection } from '@libp2p/interface'
|
18
11
|
import type { Uint8ArrayList } from 'uint8arraylist'
|
19
12
|
|
20
|
-
const MAX_STREAMS_INBOUND_STREAMS_PER_CONNECTION = 1024
|
21
|
-
const MAX_STREAMS_OUTBOUND_STREAMS_PER_CONNECTION = 1024
|
22
|
-
const MAX_STREAM_BUFFER_SIZE = 1024 * 1024 * 4 // 4MB
|
23
13
|
const DISCONNECT_THRESHOLD = 5
|
24
|
-
const CLOSE_TIMEOUT = 500
|
25
14
|
|
26
15
|
function printMessage (msg: Message): any {
|
27
16
|
const output: any = {
|
@@ -30,93 +19,34 @@ function printMessage (msg: Message): any {
|
|
30
19
|
}
|
31
20
|
|
32
21
|
if (msg.type === MessageTypes.NEW_STREAM) {
|
33
|
-
output.data = uint8ArrayToString(msg.data
|
22
|
+
output.data = uint8ArrayToString(msg.data.subarray())
|
34
23
|
}
|
35
24
|
|
36
25
|
if (msg.type === MessageTypes.MESSAGE_INITIATOR || msg.type === MessageTypes.MESSAGE_RECEIVER) {
|
37
|
-
output.data = uint8ArrayToString(msg.data
|
26
|
+
output.data = uint8ArrayToString(msg.data.subarray(), 'base16')
|
38
27
|
}
|
39
28
|
|
40
29
|
return output
|
41
30
|
}
|
42
31
|
|
43
|
-
export
|
44
|
-
logger: ComponentLogger
|
45
|
-
}
|
46
|
-
|
47
|
-
interface MplexStreamMuxerInit extends MplexInit, StreamMuxerInit {
|
48
|
-
/**
|
49
|
-
* The default timeout to use in ms when shutting down the muxer.
|
50
|
-
*/
|
51
|
-
closeTimeout?: number
|
52
|
-
}
|
53
|
-
|
54
|
-
export class MplexStreamMuxer implements StreamMuxer {
|
55
|
-
public protocol = '/mplex/6.7.0'
|
56
|
-
|
57
|
-
public sink: Sink<Source<Uint8ArrayList | Uint8Array>, Promise<void>>
|
58
|
-
public source: AsyncGenerator<Uint8ArrayList | Uint8Array>
|
59
|
-
|
60
|
-
private readonly log: Logger
|
32
|
+
export class MplexStreamMuxer extends AbstractStreamMuxer<MplexStream> {
|
61
33
|
private _streamId: number
|
62
|
-
private readonly _streams: { initiators: Map<number, MplexStream>, receivers: Map<number, MplexStream> }
|
63
|
-
private readonly _init: MplexStreamMuxerInit
|
64
|
-
private readonly _source: Pushable<Message>
|
65
|
-
private readonly closeController: AbortController
|
66
34
|
private readonly rateLimiter: RateLimiter
|
67
|
-
private readonly
|
68
|
-
private readonly
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
this._streams = {
|
77
|
-
/**
|
78
|
-
* Stream to ids map
|
79
|
-
*/
|
80
|
-
initiators: new Map<number, MplexStream>(),
|
81
|
-
/**
|
82
|
-
* Stream to ids map
|
83
|
-
*/
|
84
|
-
receivers: new Map<number, MplexStream>()
|
85
|
-
}
|
86
|
-
this._init = init
|
87
|
-
this.closeTimeout = init.closeTimeout ?? CLOSE_TIMEOUT
|
88
|
-
|
89
|
-
/**
|
90
|
-
* An iterable sink
|
91
|
-
*/
|
92
|
-
this.sink = this._createSink()
|
93
|
-
|
94
|
-
/**
|
95
|
-
* An iterable source
|
96
|
-
*/
|
97
|
-
this._source = pushable<Message>({
|
98
|
-
objectMode: true,
|
99
|
-
onEnd: (): void => {
|
100
|
-
// the source has ended, we can't write any more messages to gracefully
|
101
|
-
// close streams so all we can do is destroy them
|
102
|
-
for (const stream of this._streams.initiators.values()) {
|
103
|
-
stream.destroy()
|
104
|
-
}
|
105
|
-
|
106
|
-
for (const stream of this._streams.receivers.values()) {
|
107
|
-
stream.destroy()
|
108
|
-
}
|
109
|
-
}
|
35
|
+
private readonly maxMessageSize: number
|
36
|
+
private readonly maxUnprocessedMessageQueueSize: number
|
37
|
+
private readonly decoder: Decoder
|
38
|
+
|
39
|
+
constructor (maConn: MultiaddrConnection, init: MplexInit) {
|
40
|
+
super(maConn, {
|
41
|
+
...init,
|
42
|
+
protocol: '/mplex/6.7.0',
|
43
|
+
name: 'mplex'
|
110
44
|
})
|
111
|
-
this.source = pipe(
|
112
|
-
this._source,
|
113
|
-
source => encode(source)
|
114
|
-
)
|
115
45
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
this.
|
46
|
+
this._streamId = 0
|
47
|
+
this.maxMessageSize = init.maxMessageSize ?? MAX_MSG_SIZE
|
48
|
+
this.maxUnprocessedMessageQueueSize = init.maxUnprocessedMessageQueueSize ?? MAX_MSG_QUEUE_SIZE
|
49
|
+
this.decoder = new Decoder(this.maxMessageSize, this.maxUnprocessedMessageQueueSize)
|
120
50
|
|
121
51
|
this.rateLimiter = new RateLimiter({
|
122
52
|
points: init.disconnectThreshold ?? DISCONNECT_THRESHOLD,
|
@@ -124,207 +54,75 @@ export class MplexStreamMuxer implements StreamMuxer {
|
|
124
54
|
})
|
125
55
|
}
|
126
56
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
get streams (): Stream[] {
|
131
|
-
// Inbound and Outbound streams may have the same ids, so we need to make those unique
|
132
|
-
const streams: Stream[] = []
|
133
|
-
for (const stream of this._streams.initiators.values()) {
|
134
|
-
streams.push(stream)
|
135
|
-
}
|
136
|
-
|
137
|
-
for (const stream of this._streams.receivers.values()) {
|
138
|
-
streams.push(stream)
|
57
|
+
onData (data: Uint8Array | Uint8ArrayList): void {
|
58
|
+
for (const msg of this.decoder.write(data)) {
|
59
|
+
this.handleMessage(msg)
|
139
60
|
}
|
140
|
-
return streams
|
141
61
|
}
|
142
62
|
|
143
63
|
/**
|
144
64
|
* Initiate a new stream with the given name. If no name is
|
145
65
|
* provided, the id of the stream will be used.
|
146
66
|
*/
|
147
|
-
|
148
|
-
if (this.
|
67
|
+
onCreateStream (options: CreateStreamOptions): MplexStream {
|
68
|
+
if (this.status !== 'open') {
|
149
69
|
throw new MuxerClosedError('Muxer already closed')
|
150
70
|
}
|
151
|
-
const id = this._streamId++
|
152
|
-
name = name == null ? id.toString() : name.toString()
|
153
|
-
const registry = this._streams.initiators
|
154
|
-
return this._newStream({ id, name, type: 'initiator', registry })
|
155
|
-
}
|
156
|
-
|
157
|
-
/**
|
158
|
-
* Close or abort all tracked streams and stop the muxer
|
159
|
-
*/
|
160
|
-
async close (options?: AbortOptions): Promise<void> {
|
161
|
-
if (this.closeController.signal.aborted) {
|
162
|
-
return
|
163
|
-
}
|
164
|
-
|
165
|
-
const signal = options?.signal ?? AbortSignal.timeout(this.closeTimeout)
|
166
|
-
|
167
|
-
try {
|
168
|
-
// try to gracefully close all streams
|
169
|
-
await Promise.all(
|
170
|
-
this.streams.map(async s => s.close({
|
171
|
-
signal
|
172
|
-
}))
|
173
|
-
)
|
174
71
|
|
175
|
-
|
176
|
-
|
177
|
-
// try to gracefully close the muxer
|
178
|
-
await this._source.onEmpty({
|
179
|
-
signal
|
180
|
-
})
|
181
|
-
|
182
|
-
this.closeController.abort()
|
183
|
-
} catch (err: any) {
|
184
|
-
this.abort(err)
|
185
|
-
}
|
186
|
-
}
|
187
|
-
|
188
|
-
abort (err: Error): void {
|
189
|
-
if (this.closeController.signal.aborted) {
|
190
|
-
return
|
191
|
-
}
|
192
|
-
|
193
|
-
this.streams.forEach(s => { s.abort(err) })
|
194
|
-
this.closeController.abort(err)
|
195
|
-
}
|
72
|
+
const id = this._streamId++
|
196
73
|
|
197
|
-
|
198
|
-
* Called whenever an inbound stream is created
|
199
|
-
*/
|
200
|
-
_newReceiverStream (options: { id: number, name: string }): MplexStream {
|
201
|
-
const { id, name } = options
|
202
|
-
const registry = this._streams.receivers
|
203
|
-
return this._newStream({ id, name, type: 'receiver', registry })
|
74
|
+
return this._newStream(id, 'outbound', options)
|
204
75
|
}
|
205
76
|
|
206
|
-
_newStream (
|
207
|
-
|
208
|
-
|
209
|
-
this.log('new %s stream %s', type, id)
|
210
|
-
|
211
|
-
if (type === 'initiator' && this._streams.initiators.size === (this._init.maxOutboundStreams ?? MAX_STREAMS_OUTBOUND_STREAMS_PER_CONNECTION)) {
|
212
|
-
throw new TooManyOutboundProtocolStreamsError('Too many outbound streams open')
|
213
|
-
}
|
77
|
+
_newStream (id: number, direction: MessageStreamDirection, options?: CreateStreamOptions): MplexStream {
|
78
|
+
this.log('new %s stream %s', direction, id)
|
214
79
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
this._source.push(msg)
|
225
|
-
}
|
226
|
-
|
227
|
-
const onEnd = (): void => {
|
228
|
-
this.log('%s stream with id %s and protocol %s ended', type, id, stream.protocol)
|
229
|
-
registry.delete(id)
|
230
|
-
|
231
|
-
if (this._init.onStreamEnd != null) {
|
232
|
-
this._init.onStreamEnd(stream)
|
233
|
-
}
|
234
|
-
}
|
80
|
+
const stream = createStream({
|
81
|
+
...options,
|
82
|
+
id,
|
83
|
+
direction,
|
84
|
+
maxMsgSize: this.maxMessageSize,
|
85
|
+
log: this.log,
|
86
|
+
muxer: this
|
87
|
+
})
|
235
88
|
|
236
|
-
const stream = createStream({ id, name, send, type, onEnd, maxMsgSize: this._init.maxMsgSize, log: this.log })
|
237
|
-
registry.set(id, stream)
|
238
89
|
return stream
|
239
90
|
}
|
240
91
|
|
241
|
-
|
242
|
-
* Creates a sink with an abortable source. Incoming messages will
|
243
|
-
* also have their size restricted. All messages will be varint decoded.
|
244
|
-
*/
|
245
|
-
_createSink (): Sink<Source<Uint8ArrayList | Uint8Array>, Promise<void>> {
|
246
|
-
const sink: Sink<Source<Uint8ArrayList | Uint8Array>, Promise<void>> = async source => {
|
247
|
-
const abortListener = (): void => {
|
248
|
-
closeSource(source, this.log)
|
249
|
-
}
|
250
|
-
|
251
|
-
this.closeController.signal.addEventListener('abort', abortListener)
|
252
|
-
|
253
|
-
try {
|
254
|
-
const decoder = new Decoder(this._init.maxMsgSize, this._init.maxUnprocessedMessageQueueSize)
|
255
|
-
|
256
|
-
for await (const chunk of source) {
|
257
|
-
for (const msg of decoder.write(chunk)) {
|
258
|
-
await this._handleIncoming(msg)
|
259
|
-
}
|
260
|
-
}
|
261
|
-
|
262
|
-
this._source.end()
|
263
|
-
} catch (err: any) {
|
264
|
-
this.log('error in sink', err)
|
265
|
-
this._source.end(err) // End the source with an error
|
266
|
-
} finally {
|
267
|
-
this.closeController.signal.removeEventListener('abort', abortListener)
|
268
|
-
}
|
269
|
-
}
|
270
|
-
|
271
|
-
return sink
|
272
|
-
}
|
273
|
-
|
274
|
-
async _handleIncoming (message: Message): Promise<void> {
|
275
|
-
const { id, type } = message
|
276
|
-
|
92
|
+
handleMessage (message: Message): void {
|
277
93
|
if (this.log.enabled) {
|
278
94
|
this.log.trace('incoming message', printMessage(message))
|
279
95
|
}
|
280
96
|
|
281
97
|
// Create a new stream?
|
282
98
|
if (message.type === MessageTypes.NEW_STREAM) {
|
283
|
-
if
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
type: MessageTypes.RESET_RECEIVER
|
291
|
-
})
|
292
|
-
|
293
|
-
// if we've hit our stream limit, and the remote keeps trying to open
|
294
|
-
// more new streams, if they are doing this very quickly maybe they
|
295
|
-
// are attacking us and we should close the connection
|
296
|
-
try {
|
297
|
-
await this.rateLimiter.consume('new-stream', 1)
|
298
|
-
} catch {
|
299
|
-
this.log('rate limit hit when opening too many new streams over the inbound stream limit - closing remote connection')
|
300
|
-
// since there's no backpressure in mplex, the only thing we can really do to protect ourselves is close the connection
|
301
|
-
this.abort(new Error('Too many open streams'))
|
302
|
-
return
|
303
|
-
}
|
304
|
-
|
99
|
+
// close the connection if the remote opens too many streams too quickly
|
100
|
+
try {
|
101
|
+
this.rateLimiter.consume('new-stream', 1)
|
102
|
+
} catch {
|
103
|
+
this.log('rate limit hit when opening too many new streams over the inbound stream limit - closing remote connection')
|
104
|
+
// since there's no backpressure in mplex, the only thing we can really do to protect ourselves is close the connection
|
105
|
+
this.abort(new Error('Too many open streams'))
|
305
106
|
return
|
306
107
|
}
|
307
108
|
|
308
|
-
const stream = this.
|
309
|
-
|
310
|
-
if (this._init.onIncomingStream != null) {
|
311
|
-
this._init.onIncomingStream(stream)
|
312
|
-
}
|
109
|
+
const stream = this._newStream(message.id, 'inbound', this.streamOptions)
|
110
|
+
this.onRemoteStream(stream)
|
313
111
|
|
314
112
|
return
|
315
113
|
}
|
316
114
|
|
317
|
-
const
|
318
|
-
const stream =
|
115
|
+
const id = `${(message.type & 1) === 1 ? 'i' : 'r'}${message.id}`
|
116
|
+
const stream = this.streams.find(s => s.id === id)
|
319
117
|
|
320
118
|
if (stream == null) {
|
321
|
-
this.log('missing stream %s for message type %s', id, MessageTypeNames[type])
|
119
|
+
this.log('missing stream %s for message type %s', id, MessageTypeNames[message.type])
|
322
120
|
|
323
121
|
// if the remote keeps sending us messages for streams that have been
|
324
122
|
// closed or were never opened they may be attacking us so if they do
|
325
123
|
// this very quickly all we can do is close the connection
|
326
124
|
try {
|
327
|
-
|
125
|
+
this.rateLimiter.consume('missing-stream', 1)
|
328
126
|
} catch {
|
329
127
|
this.log('rate limit hit when receiving messages for streams that do not exist - closing remote connection')
|
330
128
|
// since there's no backpressure in mplex, the only thing we can really do to protect ourselves is close the connection
|
@@ -335,41 +133,28 @@ export class MplexStreamMuxer implements StreamMuxer {
|
|
335
133
|
return
|
336
134
|
}
|
337
135
|
|
338
|
-
const maxBufferSize = this._init.maxStreamBufferSize ?? MAX_STREAM_BUFFER_SIZE
|
339
|
-
|
340
136
|
try {
|
341
|
-
switch (type) {
|
137
|
+
switch (message.type) {
|
342
138
|
case MessageTypes.MESSAGE_INITIATOR:
|
343
139
|
case MessageTypes.MESSAGE_RECEIVER:
|
344
|
-
if (stream.sourceReadableLength() > maxBufferSize) {
|
345
|
-
// Stream buffer has got too large, reset the stream
|
346
|
-
this._source.push({
|
347
|
-
id: message.id,
|
348
|
-
type: type === MessageTypes.MESSAGE_INITIATOR ? MessageTypes.RESET_RECEIVER : MessageTypes.RESET_INITIATOR
|
349
|
-
})
|
350
|
-
|
351
|
-
// Inform the stream consumer they are not fast enough
|
352
|
-
throw new StreamInputBufferError('Input buffer full - increase Mplex maxBufferSize to accommodate slow consumers')
|
353
|
-
}
|
354
|
-
|
355
140
|
// We got data from the remote, push it into our local stream
|
356
|
-
stream.
|
141
|
+
stream.onData(message.data)
|
357
142
|
break
|
358
143
|
case MessageTypes.CLOSE_INITIATOR:
|
359
144
|
case MessageTypes.CLOSE_RECEIVER:
|
360
|
-
// The remote has stopped writing
|
361
|
-
stream.
|
145
|
+
// The remote has stopped writing
|
146
|
+
stream.onRemoteCloseWrite()
|
362
147
|
break
|
363
148
|
case MessageTypes.RESET_INITIATOR:
|
364
149
|
case MessageTypes.RESET_RECEIVER:
|
365
150
|
// The remote has errored, stop reading and writing to the stream immediately
|
366
|
-
stream.
|
151
|
+
stream.onRemoteReset()
|
367
152
|
break
|
368
153
|
default:
|
369
|
-
this.log('unknown message type
|
154
|
+
this.log('unknown message type')
|
370
155
|
}
|
371
156
|
} catch (err: any) {
|
372
|
-
this.log.error('error while processing message', err)
|
157
|
+
this.log.error('error while processing message - %e', err)
|
373
158
|
stream.abort(err)
|
374
159
|
}
|
375
160
|
}
|
package/src/stream.ts
CHANGED
@@ -1,95 +1,127 @@
|
|
1
|
-
import { AbstractStream } from '@libp2p/utils
|
1
|
+
import { AbstractStream } from '@libp2p/utils'
|
2
2
|
import { Uint8ArrayList } from 'uint8arraylist'
|
3
3
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
4
4
|
import { MAX_MSG_SIZE } from './decode.js'
|
5
|
+
import { encode } from './encode.ts'
|
5
6
|
import { InitiatorMessageTypes, ReceiverMessageTypes } from './message-types.js'
|
6
|
-
import type {
|
7
|
-
import type { Logger } from '@libp2p/interface'
|
8
|
-
import type { AbstractStreamInit } from '@libp2p/utils
|
7
|
+
import type { MplexStreamMuxer } from './mplex.ts'
|
8
|
+
import type { Logger, MessageStreamDirection } from '@libp2p/interface'
|
9
|
+
import type { AbstractStreamInit, SendResult } from '@libp2p/utils'
|
10
|
+
import type { AbortOptions } from 'it-pushable'
|
9
11
|
|
10
12
|
export interface Options {
|
11
13
|
id: number
|
12
|
-
send(msg: Message): Promise<void>
|
13
14
|
log: Logger
|
14
|
-
|
15
|
-
onEnd?(err?: Error): void
|
16
|
-
type?: 'initiator' | 'receiver'
|
15
|
+
direction: MessageStreamDirection
|
17
16
|
maxMsgSize?: number
|
17
|
+
muxer: MplexStreamMuxer
|
18
18
|
}
|
19
19
|
|
20
20
|
interface MplexStreamInit extends AbstractStreamInit {
|
21
|
-
streamId: number
|
22
|
-
name: string
|
23
|
-
send(msg: Message): Promise<void>
|
24
|
-
|
25
|
-
/**
|
26
|
-
* The maximum allowable data size, any data larger than this will be
|
27
|
-
* chunked and sent in multiple data messages
|
28
|
-
*/
|
29
21
|
maxDataSize: number
|
22
|
+
muxer: MplexStreamMuxer
|
23
|
+
direction: MessageStreamDirection
|
30
24
|
}
|
31
25
|
|
32
26
|
export class MplexStream extends AbstractStream {
|
33
|
-
|
34
|
-
private readonly streamId: number
|
35
|
-
private readonly send: (msg: Message) => Promise<void>
|
27
|
+
public readonly streamId: number
|
36
28
|
private readonly types: Record<string, number>
|
37
29
|
private readonly maxDataSize: number
|
30
|
+
private readonly muxer: MplexStreamMuxer
|
38
31
|
|
39
32
|
constructor (init: MplexStreamInit) {
|
40
33
|
super(init)
|
41
34
|
|
42
35
|
this.types = init.direction === 'outbound' ? InitiatorMessageTypes : ReceiverMessageTypes
|
43
|
-
this.send = init.send
|
44
|
-
this.name = init.name
|
45
|
-
this.streamId = init.streamId
|
46
36
|
this.maxDataSize = init.maxDataSize
|
47
|
-
|
37
|
+
this.muxer = init.muxer
|
38
|
+
this.streamId = parseInt(this.id.substring(1))
|
48
39
|
|
49
|
-
|
50
|
-
|
40
|
+
if (init.direction === 'outbound') {
|
41
|
+
// open the stream on the receiver end. do this in a microtask so the
|
42
|
+
// stream gets added to the streams array by the muxer superclass before
|
43
|
+
// we send the NEW_STREAM message, otherwise we create a race condition
|
44
|
+
// whereby we can receive the stream messages before the stream is added
|
45
|
+
// to the streams list
|
46
|
+
queueMicrotask(() => {
|
47
|
+
this.muxer.send(
|
48
|
+
encode({
|
49
|
+
id: this.streamId,
|
50
|
+
type: InitiatorMessageTypes.NEW_STREAM,
|
51
|
+
data: new Uint8ArrayList(uint8ArrayFromString(this.id))
|
52
|
+
})
|
53
|
+
)
|
54
|
+
})
|
55
|
+
}
|
51
56
|
}
|
52
57
|
|
53
|
-
|
54
|
-
|
58
|
+
sendData (data: Uint8ArrayList): SendResult {
|
59
|
+
const list = new Uint8ArrayList()
|
60
|
+
const sentBytes = data.byteLength
|
55
61
|
|
56
62
|
while (data.byteLength > 0) {
|
57
63
|
const toSend = Math.min(data.byteLength, this.maxDataSize)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
64
|
+
const slice = data.sublist(0, toSend)
|
65
|
+
data = data.sublist(toSend)
|
66
|
+
|
67
|
+
list.append(
|
68
|
+
encode({
|
69
|
+
id: this.streamId,
|
70
|
+
type: this.types.MESSAGE,
|
71
|
+
data: slice
|
72
|
+
})
|
73
|
+
)
|
74
|
+
}
|
63
75
|
|
64
|
-
|
76
|
+
return {
|
77
|
+
sentBytes,
|
78
|
+
canSendMore: this.muxer.send(list)
|
65
79
|
}
|
66
80
|
}
|
67
81
|
|
68
|
-
|
69
|
-
|
82
|
+
sendReset (): boolean {
|
83
|
+
return this.muxer.send(
|
84
|
+
encode({
|
85
|
+
id: this.streamId,
|
86
|
+
type: this.types.RESET
|
87
|
+
})
|
88
|
+
)
|
70
89
|
}
|
71
90
|
|
72
|
-
async sendCloseWrite (): Promise<void> {
|
73
|
-
|
91
|
+
async sendCloseWrite (options?: AbortOptions): Promise<void> {
|
92
|
+
this.muxer.send(
|
93
|
+
encode({
|
94
|
+
id: this.streamId,
|
95
|
+
type: this.types.CLOSE
|
96
|
+
})
|
97
|
+
)
|
98
|
+
options?.signal?.throwIfAborted()
|
74
99
|
}
|
75
100
|
|
76
|
-
async sendCloseRead (): Promise<void> {
|
101
|
+
async sendCloseRead (options?: AbortOptions): Promise<void> {
|
102
|
+
options?.signal?.throwIfAborted()
|
77
103
|
// mplex does not support close read, only close write
|
78
104
|
}
|
105
|
+
|
106
|
+
sendPause (): void {
|
107
|
+
// mplex does not support backpressure
|
108
|
+
}
|
109
|
+
|
110
|
+
sendResume (): void {
|
111
|
+
// mplex does not support backpressure
|
112
|
+
}
|
79
113
|
}
|
80
114
|
|
81
115
|
export function createStream (options: Options): MplexStream {
|
82
|
-
const { id,
|
83
|
-
const direction = type === 'initiator' ? 'outbound' : 'inbound'
|
116
|
+
const { id, muxer, direction, maxMsgSize = MAX_MSG_SIZE } = options
|
84
117
|
|
85
118
|
return new MplexStream({
|
86
|
-
|
87
|
-
|
88
|
-
name: `${name ?? id}`,
|
119
|
+
...options,
|
120
|
+
id: direction === 'outbound' ? (`i${id}`) : `r${id}`,
|
89
121
|
direction,
|
90
122
|
maxDataSize: maxMsgSize,
|
91
|
-
|
92
|
-
|
93
|
-
|
123
|
+
muxer,
|
124
|
+
log: options.log.newScope(`${direction}:${id}`),
|
125
|
+
protocol: ''
|
94
126
|
})
|
95
127
|
}
|