frame.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /**
  2. * @file Utility functions for creating frames
  3. */
  4. 'use strict'
  5. /**
  6. * Creates a text frame
  7. * @param {string} data
  8. * @param {boolean} [masked=false] if the frame should be masked
  9. * @returns {Buffer}
  10. * @private
  11. */
  12. exports.createTextFrame = function (data, masked) {
  13. var payload, meta
  14. payload = Buffer.from(data)
  15. meta = generateMetaData(true, 1, masked === undefined ? false : masked, payload)
  16. return Buffer.concat([meta, payload], meta.length + payload.length)
  17. }
  18. /**
  19. * Create a binary frame
  20. * @param {Buffer} data
  21. * @param {boolean} [masked=false] if the frame should be masked
  22. * @param {boolean} [first=true] if this is the first frame in a sequence
  23. * @param {boolean} [fin=true] if this is the final frame in a sequence
  24. * @returns {Buffer}
  25. * @private
  26. */
  27. exports.createBinaryFrame = function (data, masked, first, fin) {
  28. var payload, meta
  29. first = first === undefined ? true : first
  30. masked = masked === undefined ? false : masked
  31. if (masked) {
  32. payload = Buffer.alloc(data.length)
  33. data.copy(payload)
  34. } else {
  35. payload = data
  36. }
  37. meta = generateMetaData(fin === undefined ? true : fin, first ? 2 : 0, masked, payload)
  38. return Buffer.concat([meta, payload], meta.length + payload.length)
  39. }
  40. /**
  41. * Create a close frame
  42. * @param {number} code
  43. * @param {string} [reason='']
  44. * @param {boolean} [masked=false] if the frame should be masked
  45. * @returns {Buffer}
  46. * @private
  47. */
  48. exports.createCloseFrame = function (code, reason, masked) {
  49. var payload, meta
  50. if (code !== undefined && code !== 1005) {
  51. payload = Buffer.from(reason === undefined ? '--' : '--' + reason)
  52. payload.writeUInt16BE(code, 0)
  53. } else {
  54. payload = Buffer.alloc(0)
  55. }
  56. meta = generateMetaData(true, 8, masked === undefined ? false : masked, payload)
  57. return Buffer.concat([meta, payload], meta.length + payload.length)
  58. }
  59. /**
  60. * Create a ping frame
  61. * @param {string} data
  62. * @param {boolean} [masked=false] if the frame should be masked
  63. * @returns {Buffer}
  64. * @private
  65. */
  66. exports.createPingFrame = function (data, masked) {
  67. var payload, meta
  68. payload = Buffer.from(data)
  69. meta = generateMetaData(true, 9, masked === undefined ? false : masked, payload)
  70. return Buffer.concat([meta, payload], meta.length + payload.length)
  71. }
  72. /**
  73. * Create a pong frame
  74. * @param {string} data
  75. * @param {boolean} [masked=false] if the frame should be masked
  76. * @returns {Buffer}
  77. * @private
  78. */
  79. exports.createPongFrame = function (data, masked) {
  80. var payload, meta
  81. payload = Buffer.from(data)
  82. meta = generateMetaData(true, 10, masked === undefined ? false : masked, payload)
  83. return Buffer.concat([meta, payload], meta.length + payload.length)
  84. }
  85. /**
  86. * Creates the meta-data portion of the frame
  87. * If the frame is masked, the payload is altered accordingly
  88. * @param {boolean} fin
  89. * @param {number} opcode
  90. * @param {boolean} masked
  91. * @param {Buffer} payload
  92. * @returns {Buffer}
  93. * @private
  94. */
  95. function generateMetaData(fin, opcode, masked, payload) {
  96. var len, meta, start, mask, i
  97. len = payload.length
  98. // Creates the buffer for meta-data
  99. meta = Buffer.alloc(2 + (len < 126 ? 0 : (len < 65536 ? 2 : 8)) + (masked ? 4 : 0))
  100. // Sets fin and opcode
  101. meta[0] = (fin ? 128 : 0) + opcode
  102. // Sets the mask and length
  103. meta[1] = masked ? 128 : 0
  104. start = 2
  105. if (len < 126) {
  106. meta[1] += len
  107. } else if (len < 65536) {
  108. meta[1] += 126
  109. meta.writeUInt16BE(len, 2)
  110. start += 2
  111. } else {
  112. // Warning: JS doesn't support integers greater than 2^53
  113. meta[1] += 127
  114. meta.writeUInt32BE(Math.floor(len / Math.pow(2, 32)), 2)
  115. meta.writeUInt32BE(len % Math.pow(2, 32), 6)
  116. start += 8
  117. }
  118. // Set the mask-key
  119. if (masked) {
  120. mask = Buffer.alloc(4)
  121. for (i = 0; i < 4; i++) {
  122. meta[start + i] = mask[i] = Math.floor(Math.random() * 256)
  123. }
  124. for (i = 0; i < payload.length; i++) {
  125. payload[i] ^= mask[i % 4]
  126. }
  127. start += 4
  128. }
  129. return meta
  130. }