summaryrefslogtreecommitdiffstats
path: root/lib/websocket/frame.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/websocket/frame.js73
1 files changed, 73 insertions, 0 deletions
diff --git a/lib/websocket/frame.js b/lib/websocket/frame.js
new file mode 100644
index 0000000..d867ad1
--- /dev/null
+++ b/lib/websocket/frame.js
@@ -0,0 +1,73 @@
+'use strict'
+
+const { maxUnsigned16Bit } = require('./constants')
+
+/** @type {import('crypto')} */
+let crypto
+try {
+ crypto = require('crypto')
+} catch {
+
+}
+
+class WebsocketFrameSend {
+ /**
+ * @param {Buffer|undefined} data
+ */
+ constructor (data) {
+ this.frameData = data
+ this.maskKey = crypto.randomBytes(4)
+ }
+
+ createFrame (opcode) {
+ const bodyLength = this.frameData?.byteLength ?? 0
+
+ /** @type {number} */
+ let payloadLength = bodyLength // 0-125
+ let offset = 6
+
+ if (bodyLength > maxUnsigned16Bit) {
+ offset += 8 // payload length is next 8 bytes
+ payloadLength = 127
+ } else if (bodyLength > 125) {
+ offset += 2 // payload length is next 2 bytes
+ payloadLength = 126
+ }
+
+ const buffer = Buffer.allocUnsafe(bodyLength + offset)
+
+ // Clear first 2 bytes, everything else is overwritten
+ buffer[0] = buffer[1] = 0
+ buffer[0] |= 0x80 // FIN
+ buffer[0] = (buffer[0] & 0xF0) + opcode // opcode
+
+ /*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> */
+ buffer[offset - 4] = this.maskKey[0]
+ buffer[offset - 3] = this.maskKey[1]
+ buffer[offset - 2] = this.maskKey[2]
+ buffer[offset - 1] = this.maskKey[3]
+
+ buffer[1] = payloadLength
+
+ if (payloadLength === 126) {
+ buffer.writeUInt16BE(bodyLength, 2)
+ } else if (payloadLength === 127) {
+ // Clear extended payload length
+ buffer[2] = buffer[3] = 0
+ buffer.writeUIntBE(bodyLength, 4, 6)
+ }
+
+ buffer[1] |= 0x80 // MASK
+
+ // mask body
+ for (let i = 0; i < bodyLength; i++) {
+ buffer[offset + i] = this.frameData[i] ^ this.maskKey[i % 4]
+ }
+
+ return buffer
+ }
+}
+
+module.exports = {
+ WebsocketFrameSend
+}