| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- 'use strict'
- const { runtimeFeatures } = require('../../util/runtime-features')
- const { maxUnsigned16Bit, opcodes } = require('./constants')
- const BUFFER_SIZE = 8 * 1024
- let buffer = null
- let bufIdx = BUFFER_SIZE
- const randomFillSync = runtimeFeatures.has('crypto')
- ? require('node:crypto').randomFillSync
- // not full compatibility, but minimum.
- : function randomFillSync (buffer, _offset, _size) {
- for (let i = 0; i < buffer.length; ++i) {
- buffer[i] = Math.random() * 255 | 0
- }
- return buffer
- }
- function generateMask () {
- if (bufIdx === BUFFER_SIZE) {
- bufIdx = 0
- randomFillSync((buffer ??= Buffer.allocUnsafeSlow(BUFFER_SIZE)), 0, BUFFER_SIZE)
- }
- return [buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++], buffer[bufIdx++]]
- }
- class WebsocketFrameSend {
- /**
- * @param {Buffer|undefined} data
- */
- constructor (data) {
- this.frameData = data
- }
- createFrame (opcode) {
- const frameData = this.frameData
- const maskKey = generateMask()
- const bodyLength = 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] = maskKey[0]
- buffer[offset - 3] = maskKey[1]
- buffer[offset - 2] = maskKey[2]
- buffer[offset - 1] = 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] = frameData[i] ^ maskKey[i & 3]
- }
- return buffer
- }
- /**
- * @param {Uint8Array} buffer
- */
- static createFastTextFrame (buffer) {
- const maskKey = generateMask()
- const bodyLength = buffer.length
- // mask body
- for (let i = 0; i < bodyLength; ++i) {
- buffer[i] ^= maskKey[i & 3]
- }
- let payloadLength = bodyLength
- 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 head = Buffer.allocUnsafeSlow(offset)
- head[0] = 0x80 /* FIN */ | opcodes.TEXT /* opcode TEXT */
- head[1] = payloadLength | 0x80 /* MASK */
- head[offset - 4] = maskKey[0]
- head[offset - 3] = maskKey[1]
- head[offset - 2] = maskKey[2]
- head[offset - 1] = maskKey[3]
- if (payloadLength === 126) {
- head.writeUInt16BE(bodyLength, 2)
- } else if (payloadLength === 127) {
- head[2] = head[3] = 0
- head.writeUIntBE(bodyLength, 4, 6)
- }
- return [head, buffer]
- }
- }
- module.exports = {
- WebsocketFrameSend,
- generateMask // for benchmark
- }
|