codekingpro/portable-devtools
114k
1'use strict'
2
3const { WebsocketFrameSend } = require('./frame')
4const { opcodes, sendHints } = require('./constants')
5const FixedQueue = require('../../dispatcher/fixed-queue')
6
7/** @type {typeof Uint8Array} */
8const FastBuffer = Buffer[Symbol.species]
9
10/**
11 * @typedef {object} SendQueueNode
12 * @property {Promise<void> | null} promise
13 * @property {((...args: any[]) => any)} callback
14 * @property {Buffer | null} frame
15 */
16
17class SendQueue {
18 /**
19 * @type {FixedQueue}
20 */
21 #queue = new FixedQueue()
22
23 /**
24 * @type {boolean}
25 */
26 #running = false
27
28 /** @type {import('node:net').Socket} */
29 #socket
30
31 constructor (socket) {
32 this.#socket = socket
33 }
34
35 add (item, cb, hint) {
36 if (hint !== sendHints.blob) {
37 const frame = createFrame(item, hint)
38 if (!this.#running) {
39 // fast-path
40 this.#socket.write(frame, cb)
41 } else {
42 /** @type {SendQueueNode} */
43 const node = {
44 promise: null,
45 callback: cb,
46 frame
47 }
48 this.#queue.push(node)
49 }
50 return
51 }
52
53 /** @type {SendQueueNode} */
54 const node = {
55 promise: item.arrayBuffer().then((ab) => {
56 node.promise = null
57 node.frame = createFrame(ab, hint)
58 }),
59 callback: cb,
60 frame: null
61 }
62
63 this.#queue.push(node)
64
65 if (!this.#running) {
66 this.#run()
67 }
68 }
69
70 async #run () {
71 this.#running = true
72 const queue = this.#queue
73 while (!queue.isEmpty()) {
74 const node = queue.shift()
75 // wait pending promise
76 if (node.promise !== null) {
77 await node.promise
78 }
79 // write
80 this.#socket.write(node.frame, node.callback)
81 // cleanup
82 node.callback = node.frame = null
83 }
84 this.#running = false
85 }
86}
87
88function createFrame (data, hint) {
89 return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY)
90}
91
92function toBuffer (data, hint) {
93 switch (hint) {
94 case sendHints.string:
95 return Buffer.from(data)
96 case sendHints.arrayBuffer:
97 case sendHints.blob:
98 return new FastBuffer(data)
99 case sendHints.typedArray:
100 return new FastBuffer(data.buffer, data.byteOffset, data.byteLength)
101 }
102}
103
104module.exports = { SendQueue }
105 