'use strict'; const { WebsocketFrameSend } = require('./frame'); const { opcodes, sendHints } = require('./constants'); const FixedQueue = require('../../dispatcher/fixed-queue'); /** * @typedef {object} SendQueueNode * @property {Promise | null} promise * @property {((...args: any[]) => any)} callback * @property {Buffer | null} frame */ class SendQueue { /** * @type {FixedQueue} */ #queue = new FixedQueue(); /** * @type {boolean} */ #running = false; /** @type {import('node:net').Socket} */ #socket; constructor(socket) { this.#socket = socket; } add(item, cb, hint) { if (hint !== sendHints.blob) { if (!this.#running) { // TODO(@tsctx): support fast-path for string on running if (hint === sendHints.text) { // special fast-path for string const { 0: head, 1: body } = WebsocketFrameSend.createFastTextFrame(item); this.#socket.cork(); this.#socket.write(head); this.#socket.write(body, cb); this.#socket.uncork(); } else { // direct writing this.#socket.write(createFrame(item, hint), cb); } } else { /** @type {SendQueueNode} */ const node = { promise: null, callback: cb, frame: createFrame(item, hint), }; this.#queue.push(node); } return; } /** @type {SendQueueNode} */ const node = { promise: item.arrayBuffer().then((ab) => { node.promise = null; node.frame = createFrame(ab, hint); }), callback: cb, frame: null, }; this.#queue.push(node); if (!this.#running) { this.#run(); } } async #run() { this.#running = true; const queue = this.#queue; while (!queue.isEmpty()) { const node = queue.shift(); // wait pending promise if (node.promise !== null) { await node.promise; } // write this.#socket.write(node.frame, node.callback); // cleanup node.callback = node.frame = null; } this.#running = false; } } function createFrame(data, hint) { return new WebsocketFrameSend(toBuffer(data, hint)).createFrame( hint === sendHints.text ? opcodes.TEXT : opcodes.BINARY ); } function toBuffer(data, hint) { switch (hint) { case sendHints.text: case sendHints.typedArray: return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); case sendHints.arrayBuffer: case sendHints.blob: return new Uint8Array(data); } } module.exports = { SendQueue };