frame.js 2.3 KB

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