UNPKG

@libp2p/webrtc-peer

Version:
75 lines 3.32 kB
import errCode from 'err-code'; import defer from 'p-defer'; const MAX_BUFFERED_AMOUNT = 64 * 1024; const CHANNEL_CLOSING_TIMEOUT = 5 * 1000; export class WebRTCDataChannel { constructor(channel, opts) { this.label = channel.label; this.open = defer(); this.channel = channel; this.channel.binaryType = 'arraybuffer'; this.log = opts.log; if (typeof this.channel.bufferedAmountLowThreshold === 'number') { this.channel.bufferedAmountLowThreshold = MAX_BUFFERED_AMOUNT; } channel.addEventListener('message', event => { opts.onMessage(event); }); channel.addEventListener('bufferedamountlow', () => { this.log('stop backpressure: bufferedAmount %d', this.channel.bufferedAmount); this.open.resolve(); }); channel.addEventListener('open', () => { this.open.resolve(); opts.onOpen(); }); channel.addEventListener('close', () => { opts.onClose(); }); channel.addEventListener('error', event => { // @ts-expect-error ChannelErrorEvent is just an Event in the types? if (event.error?.message === 'Transport channel closed') { return this.close(); } // @ts-expect-error ChannelErrorEvent is just an Event in the types? opts.log.error('channel encounter an error in state "%s" message: "%s" detail: "%s', channel.readyState, event.error?.message, event.error?.errorDetail); // eslint-disable-line @typescript-eslint/restrict-template-expressions // @ts-expect-error ChannelErrorEvent is just an Event in the types? const err = event.error instanceof Error // @ts-expect-error ChannelErrorEvent is just an Event in the types? ? event.error // @ts-expect-error ChannelErrorEvent is just an Event in the types? : new Error(`datachannel error: ${event.error?.message} ${event.error?.errorDetail}`); // eslint-disable-line @typescript-eslint/restrict-template-expressions opts.onError(errCode(err, 'ERR_DATA_CHANNEL')); }); // HACK: Chrome will sometimes get stuck in readyState "closing", let's check for this condition // https://bugs.chromium.org/p/chromium/issues/detail?id=882743 let isClosing = false; this.closingInterval = setInterval(() => { if (channel.readyState === 'closing') { if (isClosing) { opts.onClose(); // closing timed out: equivalent to onclose firing } isClosing = true; } else { isClosing = false; } }, CHANNEL_CLOSING_TIMEOUT); } async send(data) { await this.open.promise; this.channel.send(data); if (this.channel.bufferedAmount > MAX_BUFFERED_AMOUNT) { this.log('start backpressure: bufferedAmount %d', this.channel.bufferedAmount); this.open = defer(); } } close() { clearInterval(this.closingInterval); this.channel.close(); } get bufferedAmount() { return this.channel.bufferedAmount; } } //# sourceMappingURL=channel.js.map