@libp2p/mplex
Version:
JavaScript implementation of https://github.com/libp2p/mplex
94 lines (78 loc) • 2.83 kB
text/typescript
import { AbstractStream, type AbstractStreamInit } from '@libp2p/utils/abstract-stream'
import { Uint8ArrayList } from 'uint8arraylist'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { MAX_MSG_SIZE } from './decode.js'
import { InitiatorMessageTypes, ReceiverMessageTypes } from './message-types.js'
import type { Message } from './message-types.js'
import type { ComponentLogger } from '@libp2p/interface'
export interface Options {
id: number
send(msg: Message): Promise<void>
name?: string
onEnd?(err?: Error): void
type?: 'initiator' | 'receiver'
maxMsgSize?: number
logger: ComponentLogger
}
interface MplexStreamInit extends AbstractStreamInit {
streamId: number
name: string
send(msg: Message): Promise<void>
/**
* The maximum allowable data size, any data larger than this will be
* chunked and sent in multiple data messages
*/
maxDataSize: number
}
export class MplexStream extends AbstractStream {
private readonly name: string
private readonly streamId: number
private readonly send: (msg: Message) => Promise<void>
private readonly types: Record<string, number>
private readonly maxDataSize: number
constructor (init: MplexStreamInit) {
super(init)
this.types = init.direction === 'outbound' ? InitiatorMessageTypes : ReceiverMessageTypes
this.send = init.send
this.name = init.name
this.streamId = init.streamId
this.maxDataSize = init.maxDataSize
}
async sendNewStream (): Promise<void> {
await this.send({ id: this.streamId, type: InitiatorMessageTypes.NEW_STREAM, data: new Uint8ArrayList(uint8ArrayFromString(this.name)) })
}
async sendData (data: Uint8ArrayList): Promise<void> {
data = data.sublist()
while (data.byteLength > 0) {
const toSend = Math.min(data.byteLength, this.maxDataSize)
await this.send({
id: this.streamId,
type: this.types.MESSAGE,
data: data.sublist(0, toSend)
})
data.consume(toSend)
}
}
async sendReset (): Promise<void> {
await this.send({ id: this.streamId, type: this.types.RESET })
}
async sendCloseWrite (): Promise<void> {
await this.send({ id: this.streamId, type: this.types.CLOSE })
}
async sendCloseRead (): Promise<void> {
// mplex does not support close read, only close write
}
}
export function createStream (options: Options): MplexStream {
const { id, name, send, onEnd, type = 'initiator', maxMsgSize = MAX_MSG_SIZE } = options
return new MplexStream({
id: type === 'initiator' ? (`i${id}`) : `r${id}`,
streamId: id,
name: `${name ?? id}`,
direction: type === 'initiator' ? 'outbound' : 'inbound',
maxDataSize: maxMsgSize,
onEnd,
send,
log: options.logger.forComponent(`libp2p:mplex:stream:${type}:${id}`)
})
}