1'use strict' 2 3const { maxUnsigned16Bit } = require('./constants') 4 5/** @type {import('crypto')} */ 6let crypto 7try { 8 crypto = require('crypto') 9} catch { 10 11} 12 13class WebsocketFrameSend { 14 /** 15 * @param {Buffer|undefined} data 16 */ 17 constructor (data) { 18 this.frameData = data 19 this.maskKey = crypto.randomBytes(4) 20 } 21 22 createFrame (opcode) { 23 const bodyLength = this.frameData?.byteLength ?? 0 24 25 /** @type {number} */ 26 let payloadLength = bodyLength // 0-125 27 let offset = 6 28 29 if (bodyLength > maxUnsigned16Bit) { 30 offset += 8 // payload length is next 8 bytes 31 payloadLength = 127 32 } else if (bodyLength > 125) { 33 offset += 2 // payload length is next 2 bytes 34 payloadLength = 126 35 } 36 37 const buffer = Buffer.allocUnsafe(bodyLength + offset) 38 39 // Clear first 2 bytes, everything else is overwritten 40 buffer[0] = buffer[1] = 0 41 buffer[0] |= 0x80 // FIN 42 buffer[0] = (buffer[0] & 0xF0) + opcode // opcode 43 44 /*! ws. MIT License. Einar Otto Stangvik <einaros@gmail.com> */ 45 buffer[offset - 4] = this.maskKey[0] 46 buffer[offset - 3] = this.maskKey[1] 47 buffer[offset - 2] = this.maskKey[2] 48 buffer[offset - 1] = this.maskKey[3] 49 50 buffer[1] = payloadLength 51 52 if (payloadLength === 126) { 53 buffer.writeUInt16BE(bodyLength, 2) 54 } else if (payloadLength === 127) { 55 // Clear extended payload length 56 buffer[2] = buffer[3] = 0 57 buffer.writeUIntBE(bodyLength, 4, 6) 58 } 59 60 buffer[1] |= 0x80 // MASK 61 62 // mask body 63 for (let i = 0; i < bodyLength; i++) { 64 buffer[offset + i] = this.frameData[i] ^ this.maskKey[i % 4] 65 } 66 67 return buffer 68 } 69} 70 71module.exports = { 72 WebsocketFrameSend 73} 74