aboutsummaryrefslogtreecommitdiffhomepage
path: root/node_modules/ws/lib/PerMessageDeflate.js
diff options
context:
space:
mode:
authorYaroslav De La Peña Smirnov <yaros.rus_89@live.com.mx>2017-11-29 11:44:34 +0300
committerYaroslav De La Peña Smirnov <yaros.rus_89@live.com.mx>2017-11-29 11:44:34 +0300
commit67fdec20726e48ba3a934cb25bb30d47ec4a4f29 (patch)
tree37fd9f4f0b0c20103e1646fc83021e4765de3680 /node_modules/ws/lib/PerMessageDeflate.js
downloadspanish-checkers-67fdec20726e48ba3a934cb25bb30d47ec4a4f29.tar.gz
spanish-checkers-67fdec20726e48ba3a934cb25bb30d47ec4a4f29.zip
Initial commit, version 0.5.3
Diffstat (limited to 'node_modules/ws/lib/PerMessageDeflate.js')
-rw-r--r--node_modules/ws/lib/PerMessageDeflate.js384
1 files changed, 384 insertions, 0 deletions
diff --git a/node_modules/ws/lib/PerMessageDeflate.js b/node_modules/ws/lib/PerMessageDeflate.js
new file mode 100644
index 0000000..c1a1d3c
--- /dev/null
+++ b/node_modules/ws/lib/PerMessageDeflate.js
@@ -0,0 +1,384 @@
+'use strict';
+
+const safeBuffer = require('safe-buffer');
+const zlib = require('zlib');
+
+const bufferUtil = require('./BufferUtil');
+
+const Buffer = safeBuffer.Buffer;
+
+const AVAILABLE_WINDOW_BITS = [8, 9, 10, 11, 12, 13, 14, 15];
+const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
+const EMPTY_BLOCK = Buffer.from([0x00]);
+const DEFAULT_WINDOW_BITS = 15;
+const DEFAULT_MEM_LEVEL = 8;
+
+/**
+ * Per-message Deflate implementation.
+ */
+class PerMessageDeflate {
+ constructor (options, isServer, maxPayload) {
+ this._options = options || {};
+ this._isServer = !!isServer;
+ this._inflate = null;
+ this._deflate = null;
+ this.params = null;
+ this._maxPayload = maxPayload || 0;
+ this.threshold = this._options.threshold === undefined ? 1024 : this._options.threshold;
+ }
+
+ static get extensionName () {
+ return 'permessage-deflate';
+ }
+
+ /**
+ * Create extension parameters offer.
+ *
+ * @return {Object} Extension parameters
+ * @public
+ */
+ offer () {
+ const params = {};
+
+ if (this._options.serverNoContextTakeover) {
+ params.server_no_context_takeover = true;
+ }
+ if (this._options.clientNoContextTakeover) {
+ params.client_no_context_takeover = true;
+ }
+ if (this._options.serverMaxWindowBits) {
+ params.server_max_window_bits = this._options.serverMaxWindowBits;
+ }
+ if (this._options.clientMaxWindowBits) {
+ params.client_max_window_bits = this._options.clientMaxWindowBits;
+ } else if (this._options.clientMaxWindowBits == null) {
+ params.client_max_window_bits = true;
+ }
+
+ return params;
+ }
+
+ /**
+ * Accept extension offer.
+ *
+ * @param {Array} paramsList Extension parameters
+ * @return {Object} Accepted configuration
+ * @public
+ */
+ accept (paramsList) {
+ paramsList = this.normalizeParams(paramsList);
+
+ var params;
+ if (this._isServer) {
+ params = this.acceptAsServer(paramsList);
+ } else {
+ params = this.acceptAsClient(paramsList);
+ }
+
+ this.params = params;
+ return params;
+ }
+
+ /**
+ * Releases all resources used by the extension.
+ *
+ * @public
+ */
+ cleanup () {
+ if (this._inflate) {
+ if (this._inflate.writeInProgress) {
+ this._inflate.pendingClose = true;
+ } else {
+ this._inflate.close();
+ this._inflate = null;
+ }
+ }
+ if (this._deflate) {
+ if (this._deflate.writeInProgress) {
+ this._deflate.pendingClose = true;
+ } else {
+ this._deflate.close();
+ this._deflate = null;
+ }
+ }
+ }
+
+ /**
+ * Accept extension offer from client.
+ *
+ * @param {Array} paramsList Extension parameters
+ * @return {Object} Accepted configuration
+ * @private
+ */
+ acceptAsServer (paramsList) {
+ const accepted = {};
+ const result = paramsList.some((params) => {
+ if ((
+ this._options.serverNoContextTakeover === false &&
+ params.server_no_context_takeover
+ ) || (
+ this._options.serverMaxWindowBits === false &&
+ params.server_max_window_bits
+ ) || (
+ typeof this._options.serverMaxWindowBits === 'number' &&
+ typeof params.server_max_window_bits === 'number' &&
+ this._options.serverMaxWindowBits > params.server_max_window_bits
+ ) || (
+ typeof this._options.clientMaxWindowBits === 'number' &&
+ !params.client_max_window_bits
+ )) {
+ return;
+ }
+
+ if (
+ this._options.serverNoContextTakeover ||
+ params.server_no_context_takeover
+ ) {
+ accepted.server_no_context_takeover = true;
+ }
+ if (this._options.clientNoContextTakeover) {
+ accepted.client_no_context_takeover = true;
+ }
+ if (
+ this._options.clientNoContextTakeover !== false &&
+ params.client_no_context_takeover
+ ) {
+ accepted.client_no_context_takeover = true;
+ }
+ if (typeof this._options.serverMaxWindowBits === 'number') {
+ accepted.server_max_window_bits = this._options.serverMaxWindowBits;
+ } else if (typeof params.server_max_window_bits === 'number') {
+ accepted.server_max_window_bits = params.server_max_window_bits;
+ }
+ if (typeof this._options.clientMaxWindowBits === 'number') {
+ accepted.client_max_window_bits = this._options.clientMaxWindowBits;
+ } else if (
+ this._options.clientMaxWindowBits !== false &&
+ typeof params.client_max_window_bits === 'number'
+ ) {
+ accepted.client_max_window_bits = params.client_max_window_bits;
+ }
+ return true;
+ });
+
+ if (!result) throw new Error(`Doesn't support the offered configuration`);
+
+ return accepted;
+ }
+
+ /**
+ * Accept extension response from server.
+ *
+ * @param {Array} paramsList Extension parameters
+ * @return {Object} Accepted configuration
+ * @private
+ */
+ acceptAsClient (paramsList) {
+ const params = paramsList[0];
+
+ if (this._options.clientNoContextTakeover != null) {
+ if (
+ this._options.clientNoContextTakeover === false &&
+ params.client_no_context_takeover
+ ) {
+ throw new Error('Invalid value for "client_no_context_takeover"');
+ }
+ }
+ if (this._options.clientMaxWindowBits != null) {
+ if (
+ this._options.clientMaxWindowBits === false &&
+ params.client_max_window_bits
+ ) {
+ throw new Error('Invalid value for "client_max_window_bits"');
+ }
+ if (
+ typeof this._options.clientMaxWindowBits === 'number' && (
+ !params.client_max_window_bits ||
+ params.client_max_window_bits > this._options.clientMaxWindowBits
+ )) {
+ throw new Error('Invalid value for "client_max_window_bits"');
+ }
+ }
+
+ return params;
+ }
+
+ /**
+ * Normalize extensions parameters.
+ *
+ * @param {Array} paramsList Extension parameters
+ * @return {Array} Normalized extensions parameters
+ * @private
+ */
+ normalizeParams (paramsList) {
+ return paramsList.map((params) => {
+ Object.keys(params).forEach((key) => {
+ var value = params[key];
+ if (value.length > 1) {
+ throw new Error(`Multiple extension parameters for ${key}`);
+ }
+
+ value = value[0];
+
+ switch (key) {
+ case 'server_no_context_takeover':
+ case 'client_no_context_takeover':
+ if (value !== true) {
+ throw new Error(`invalid extension parameter value for ${key} (${value})`);
+ }
+ params[key] = true;
+ break;
+ case 'server_max_window_bits':
+ case 'client_max_window_bits':
+ if (typeof value === 'string') {
+ value = parseInt(value, 10);
+ if (!~AVAILABLE_WINDOW_BITS.indexOf(value)) {
+ throw new Error(`invalid extension parameter value for ${key} (${value})`);
+ }
+ }
+ if (!this._isServer && value === true) {
+ throw new Error(`Missing extension parameter value for ${key}`);
+ }
+ params[key] = value;
+ break;
+ default:
+ throw new Error(`Not defined extension parameter (${key})`);
+ }
+ });
+ return params;
+ });
+ }
+
+ /**
+ * Decompress data.
+ *
+ * @param {Buffer} data Compressed data
+ * @param {Boolean} fin Specifies whether or not this is the last fragment
+ * @param {Function} callback Callback
+ * @public
+ */
+ decompress (data, fin, callback) {
+ const endpoint = this._isServer ? 'client' : 'server';
+
+ if (!this._inflate) {
+ const maxWindowBits = this.params[`${endpoint}_max_window_bits`];
+ this._inflate = zlib.createInflateRaw({
+ windowBits: typeof maxWindowBits === 'number' ? maxWindowBits : DEFAULT_WINDOW_BITS
+ });
+ }
+ this._inflate.writeInProgress = true;
+
+ var totalLength = 0;
+ const buffers = [];
+ var err;
+
+ const onData = (data) => {
+ totalLength += data.length;
+ if (this._maxPayload < 1 || totalLength <= this._maxPayload) {
+ return buffers.push(data);
+ }
+
+ err = new Error('max payload size exceeded');
+ err.closeCode = 1009;
+ this._inflate.reset();
+ };
+
+ const onError = (err) => {
+ cleanup();
+ callback(err);
+ };
+
+ const cleanup = () => {
+ if (!this._inflate) return;
+
+ this._inflate.removeListener('error', onError);
+ this._inflate.removeListener('data', onData);
+ this._inflate.writeInProgress = false;
+
+ if (
+ (fin && this.params[`${endpoint}_no_context_takeover`]) ||
+ this._inflate.pendingClose
+ ) {
+ this._inflate.close();
+ this._inflate = null;
+ }
+ };
+
+ this._inflate.on('error', onError).on('data', onData);
+ this._inflate.write(data);
+ if (fin) this._inflate.write(TRAILER);
+
+ this._inflate.flush(() => {
+ cleanup();
+ if (err) callback(err);
+ else callback(null, bufferUtil.concat(buffers, totalLength));
+ });
+ }
+
+ /**
+ * Compress data.
+ *
+ * @param {Buffer} data Data to compress
+ * @param {Boolean} fin Specifies whether or not this is the last fragment
+ * @param {Function} callback Callback
+ * @public
+ */
+ compress (data, fin, callback) {
+ if (!data || data.length === 0) {
+ process.nextTick(callback, null, EMPTY_BLOCK);
+ return;
+ }
+
+ const endpoint = this._isServer ? 'server' : 'client';
+
+ if (!this._deflate) {
+ const maxWindowBits = this.params[`${endpoint}_max_window_bits`];
+ this._deflate = zlib.createDeflateRaw({
+ flush: zlib.Z_SYNC_FLUSH,
+ windowBits: typeof maxWindowBits === 'number' ? maxWindowBits : DEFAULT_WINDOW_BITS,
+ memLevel: this._options.memLevel || DEFAULT_MEM_LEVEL
+ });
+ }
+ this._deflate.writeInProgress = true;
+
+ var totalLength = 0;
+ const buffers = [];
+
+ const onData = (data) => {
+ totalLength += data.length;
+ buffers.push(data);
+ };
+
+ const onError = (err) => {
+ cleanup();
+ callback(err);
+ };
+
+ const cleanup = () => {
+ if (!this._deflate) return;
+
+ this._deflate.removeListener('error', onError);
+ this._deflate.removeListener('data', onData);
+ this._deflate.writeInProgress = false;
+
+ if (
+ (fin && this.params[`${endpoint}_no_context_takeover`]) ||
+ this._deflate.pendingClose
+ ) {
+ this._deflate.close();
+ this._deflate = null;
+ }
+ };
+
+ this._deflate.on('error', onError).on('data', onData);
+ this._deflate.write(data);
+ this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
+ cleanup();
+ var data = bufferUtil.concat(buffers, totalLength);
+ if (fin) data = data.slice(0, data.length - 4);
+ callback(null, data);
+ });
+ }
+}
+
+module.exports = PerMessageDeflate;