permessage-deflate.js 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. 'use strict'
  2. const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = require('node:zlib')
  3. const { isValidClientWindowBits } = require('./util')
  4. const tail = Buffer.from([0x00, 0x00, 0xff, 0xff])
  5. const kBuffer = Symbol('kBuffer')
  6. const kLength = Symbol('kLength')
  7. class PerMessageDeflate {
  8. /** @type {import('node:zlib').InflateRaw} */
  9. #inflate
  10. #options = {}
  11. constructor (extensions) {
  12. this.#options.serverNoContextTakeover = extensions.has('server_no_context_takeover')
  13. this.#options.serverMaxWindowBits = extensions.get('server_max_window_bits')
  14. }
  15. decompress (chunk, fin, callback) {
  16. // An endpoint uses the following algorithm to decompress a message.
  17. // 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
  18. // payload of the message.
  19. // 2. Decompress the resulting data using DEFLATE.
  20. if (!this.#inflate) {
  21. let windowBits = Z_DEFAULT_WINDOWBITS
  22. if (this.#options.serverMaxWindowBits) { // empty values default to Z_DEFAULT_WINDOWBITS
  23. if (!isValidClientWindowBits(this.#options.serverMaxWindowBits)) {
  24. callback(new Error('Invalid server_max_window_bits'))
  25. return
  26. }
  27. windowBits = Number.parseInt(this.#options.serverMaxWindowBits)
  28. }
  29. this.#inflate = createInflateRaw({ windowBits })
  30. this.#inflate[kBuffer] = []
  31. this.#inflate[kLength] = 0
  32. this.#inflate.on('data', (data) => {
  33. this.#inflate[kBuffer].push(data)
  34. this.#inflate[kLength] += data.length
  35. })
  36. this.#inflate.on('error', (err) => {
  37. this.#inflate = null
  38. callback(err)
  39. })
  40. }
  41. this.#inflate.write(chunk)
  42. if (fin) {
  43. this.#inflate.write(tail)
  44. }
  45. this.#inflate.flush(() => {
  46. const full = Buffer.concat(this.#inflate[kBuffer], this.#inflate[kLength])
  47. this.#inflate[kBuffer].length = 0
  48. this.#inflate[kLength] = 0
  49. callback(null, full)
  50. })
  51. }
  52. }
  53. module.exports = { PerMessageDeflate }