79 lines
2.0 KiB
JavaScript
79 lines
2.0 KiB
JavaScript
'use strict';
|
|
|
|
const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = require('node:zlib');
|
|
const { isValidClientWindowBits } = require('./util');
|
|
|
|
const tail = Buffer.from([0x00, 0x00, 0xff, 0xff]);
|
|
const kBuffer = Symbol('kBuffer');
|
|
const kLength = Symbol('kLength');
|
|
|
|
class PerMessageDeflate {
|
|
/** @type {import('node:zlib').InflateRaw} */
|
|
#inflate;
|
|
|
|
#options = {};
|
|
|
|
constructor(extensions) {
|
|
this.#options.serverNoContextTakeover = extensions.has(
|
|
'server_no_context_takeover'
|
|
);
|
|
this.#options.serverMaxWindowBits = extensions.get(
|
|
'server_max_window_bits'
|
|
);
|
|
}
|
|
|
|
decompress(chunk, fin, callback) {
|
|
// An endpoint uses the following algorithm to decompress a message.
|
|
// 1. Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
|
|
// payload of the message.
|
|
// 2. Decompress the resulting data using DEFLATE.
|
|
|
|
if (!this.#inflate) {
|
|
let windowBits = Z_DEFAULT_WINDOWBITS;
|
|
|
|
if (this.#options.serverMaxWindowBits) {
|
|
// empty values default to Z_DEFAULT_WINDOWBITS
|
|
if (!isValidClientWindowBits(this.#options.serverMaxWindowBits)) {
|
|
callback(new Error('Invalid server_max_window_bits'));
|
|
return;
|
|
}
|
|
|
|
windowBits = Number.parseInt(this.#options.serverMaxWindowBits);
|
|
}
|
|
|
|
this.#inflate = createInflateRaw({ windowBits });
|
|
this.#inflate[kBuffer] = [];
|
|
this.#inflate[kLength] = 0;
|
|
|
|
this.#inflate.on('data', (data) => {
|
|
this.#inflate[kBuffer].push(data);
|
|
this.#inflate[kLength] += data.length;
|
|
});
|
|
|
|
this.#inflate.on('error', (err) => {
|
|
this.#inflate = null;
|
|
callback(err);
|
|
});
|
|
}
|
|
|
|
this.#inflate.write(chunk);
|
|
if (fin) {
|
|
this.#inflate.write(tail);
|
|
}
|
|
|
|
this.#inflate.flush(() => {
|
|
const full = Buffer.concat(
|
|
this.#inflate[kBuffer],
|
|
this.#inflate[kLength]
|
|
);
|
|
|
|
this.#inflate[kBuffer].length = 0;
|
|
this.#inflate[kLength] = 0;
|
|
|
|
callback(null, full);
|
|
});
|
|
}
|
|
}
|
|
|
|
module.exports = { PerMessageDeflate };
|