frame.js 1.6 KB

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