arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/server/node_modules/ws/lib/PerMessageDeflate.js
server/node_modules/ws/lib/PerMessageDeflate.jsRaw
  1
  2var zlib = require('zlib');
  3
  4var AVAILABLE_WINDOW_BITS = [8, 9, 10, 11, 12, 13, 14, 15];
  5var DEFAULT_WINDOW_BITS = 15;
  6var DEFAULT_MEM_LEVEL = 8;
  7
  8PerMessageDeflate.extensionName = 'permessage-deflate';
  9
 10/**
 11 * Per-message Compression Extensions implementation
 12 */
 13
 14function PerMessageDeflate(options, isServer,maxPayload) {
 15  if (this instanceof PerMessageDeflate === false) {
 16    throw new TypeError("Classes can't be function-called");
 17  }
 18
 19  this._options = options || {};
 20  this._isServer = !!isServer;
 21  this._inflate = null;
 22  this._deflate = null;
 23  this.params = null;
 24  this._maxPayload = maxPayload || 0;
 25}
 26
 27/**
 28 * Create extension parameters offer
 29 *
 30 * @api public
 31 */
 32
 33PerMessageDeflate.prototype.offer = function() {
 34  var params = {};
 35  if (this._options.serverNoContextTakeover) {
 36    params.server_no_context_takeover = true;
 37  }
 38  if (this._options.clientNoContextTakeover) {
 39    params.client_no_context_takeover = true;
 40  }
 41  if (this._options.serverMaxWindowBits) {
 42    params.server_max_window_bits = this._options.serverMaxWindowBits;
 43  }
 44  if (this._options.clientMaxWindowBits) {
 45    params.client_max_window_bits = this._options.clientMaxWindowBits;
 46  } else if (this._options.clientMaxWindowBits == null) {
 47    params.client_max_window_bits = true;
 48  }
 49  return params;
 50};
 51
 52/**
 53 * Accept extension offer
 54 *
 55 * @api public
 56 */
 57
 58PerMessageDeflate.prototype.accept = function(paramsList) {
 59  paramsList = this.normalizeParams(paramsList);
 60
 61  var params;
 62  if (this._isServer) {
 63    params = this.acceptAsServer(paramsList);
 64  } else {
 65    params = this.acceptAsClient(paramsList);
 66  }
 67
 68  this.params = params;
 69  return params;
 70};
 71
 72/**
 73 * Releases all resources used by the extension
 74 *
 75 * @api public
 76 */
 77
 78PerMessageDeflate.prototype.cleanup = function() {
 79  if (this._inflate) {
 80    if (this._inflate.writeInProgress) {
 81      this._inflate.pendingClose = true;
 82    } else {
 83      if (this._inflate.close) this._inflate.close();
 84      this._inflate = null;
 85    }
 86  }
 87  if (this._deflate) {
 88    if (this._deflate.writeInProgress) {
 89      this._deflate.pendingClose = true;
 90    } else {
 91      if (this._deflate.close) this._deflate.close();
 92      this._deflate = null;
 93    }
 94  }
 95};
 96
 97/**
 98 * Accept extension offer from client
 99 *
100 * @api private
101 */
102
103PerMessageDeflate.prototype.acceptAsServer = function(paramsList) {
104  var accepted = {};
105  var result = paramsList.some(function(params) {
106    accepted = {};
107    if (this._options.serverNoContextTakeover === false && params.server_no_context_takeover) {
108      return;
109    }
110    if (this._options.serverMaxWindowBits === false && params.server_max_window_bits) {
111      return;
112    }
113    if (typeof this._options.serverMaxWindowBits === 'number' &&
114        typeof params.server_max_window_bits === 'number' &&
115        this._options.serverMaxWindowBits > params.server_max_window_bits) {
116      return;
117    }
118    if (typeof this._options.clientMaxWindowBits === 'number' && !params.client_max_window_bits) {
119      return;
120    }
121
122    if (this._options.serverNoContextTakeover || params.server_no_context_takeover) {
123      accepted.server_no_context_takeover = true;
124    }
125    if (this._options.clientNoContextTakeover) {
126      accepted.client_no_context_takeover = true;
127    }
128    if (this._options.clientNoContextTakeover !== false && params.client_no_context_takeover) {
129      accepted.client_no_context_takeover = true;
130    }
131    if (typeof this._options.serverMaxWindowBits === 'number') {
132      accepted.server_max_window_bits = this._options.serverMaxWindowBits;
133    } else if (typeof params.server_max_window_bits === 'number') {
134      accepted.server_max_window_bits = params.server_max_window_bits;
135    }
136    if (typeof this._options.clientMaxWindowBits === 'number') {
137      accepted.client_max_window_bits = this._options.clientMaxWindowBits;
138    } else if (this._options.clientMaxWindowBits !== false && typeof params.client_max_window_bits === 'number') {
139      accepted.client_max_window_bits = params.client_max_window_bits;
140    }
141    return true;
142  }, this);
143
144  if (!result) {
145    throw new Error('Doesn\'t support the offered configuration');
146  }
147
148  return accepted;
149};
150
151/**
152 * Accept extension response from server
153 *
154 * @api privaye
155 */
156
157PerMessageDeflate.prototype.acceptAsClient = function(paramsList) {
158  var params = paramsList[0];
159  if (this._options.clientNoContextTakeover != null) {
160    if (this._options.clientNoContextTakeover === false && params.client_no_context_takeover) {
161      throw new Error('Invalid value for "client_no_context_takeover"');
162    }
163  }
164  if (this._options.clientMaxWindowBits != null) {
165    if (this._options.clientMaxWindowBits === false && params.client_max_window_bits) {
166      throw new Error('Invalid value for "client_max_window_bits"');
167    }
168    if (typeof this._options.clientMaxWindowBits === 'number' &&
169        (!params.client_max_window_bits || params.client_max_window_bits > this._options.clientMaxWindowBits)) {
170      throw new Error('Invalid value for "client_max_window_bits"');
171    }
172  }
173  return params;
174};
175
176/**
177 * Normalize extensions parameters
178 *
179 * @api private
180 */
181
182PerMessageDeflate.prototype.normalizeParams = function(paramsList) {
183  return paramsList.map(function(params) {
184    Object.keys(params).forEach(function(key) {
185      var value = params[key];
186      if (value.length > 1) {
187        throw new Error('Multiple extension parameters for ' + key);
188      }
189
190      value = value[0];
191
192      switch (key) {
193      case 'server_no_context_takeover':
194      case 'client_no_context_takeover':
195        if (value !== true) {
196          throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')');
197        }
198        params[key] = true;
199        break;
200      case 'server_max_window_bits':
201      case 'client_max_window_bits':
202        if (typeof value === 'string') {
203          value = parseInt(value, 10);
204          if (!~AVAILABLE_WINDOW_BITS.indexOf(value)) {
205            throw new Error('invalid extension parameter value for ' + key + ' (' + value + ')');
206          }
207        }
208        if (!this._isServer && value === true) {
209          throw new Error('Missing extension parameter value for ' + key);
210        }
211        params[key] = value;
212        break;
213      default:
214        throw new Error('Not defined extension parameter (' + key + ')');
215      }
216    }, this);
217    return params;
218  }, this);
219};
220
221/**
222 * Decompress message
223 *
224 * @api public
225 */
226
227PerMessageDeflate.prototype.decompress = function (data, fin, callback) {
228  var endpoint = this._isServer ? 'client' : 'server';
229
230  if (!this._inflate) {
231    var maxWindowBits = this.params[endpoint + '_max_window_bits'];
232    this._inflate = zlib.createInflateRaw({
233      windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS
234    });
235  }
236  this._inflate.writeInProgress = true;
237
238  var self = this;
239  var buffers = [];
240  var cumulativeBufferLength=0;
241
242  this._inflate.on('error', onError).on('data', onData);
243  this._inflate.write(data);
244  if (fin) {
245    this._inflate.write(new Buffer([0x00, 0x00, 0xff, 0xff]));
246  }
247  this._inflate.flush(function() {
248    cleanup();
249    callback(null, Buffer.concat(buffers));
250  });
251
252  function onError(err) {
253    cleanup();
254    callback(err);
255  }
256
257  function onData(data) {
258      if(self._maxPayload!==undefined && self._maxPayload!==null && self._maxPayload>0){
259          cumulativeBufferLength+=data.length;
260          if(cumulativeBufferLength>self._maxPayload){
261            buffers=[];
262            cleanup();
263            var err={type:1009};
264            callback(err);
265            return;
266          }
267      }
268      buffers.push(data);
269  }
270
271  function cleanup() {
272    if (!self._inflate) return;
273    self._inflate.removeListener('error', onError);
274    self._inflate.removeListener('data', onData);
275    self._inflate.writeInProgress = false;
276    if ((fin && self.params[endpoint + '_no_context_takeover']) || self._inflate.pendingClose) {
277      if (self._inflate.close) self._inflate.close();
278      self._inflate = null;
279    }
280  }
281};
282
283/**
284 * Compress message
285 *
286 * @api public
287 */
288
289PerMessageDeflate.prototype.compress = function (data, fin, callback) {
290  var endpoint = this._isServer ? 'server' : 'client';
291
292  if (!this._deflate) {
293    var maxWindowBits = this.params[endpoint + '_max_window_bits'];
294    this._deflate = zlib.createDeflateRaw({
295      flush: zlib.Z_SYNC_FLUSH,
296      windowBits: 'number' === typeof maxWindowBits ? maxWindowBits : DEFAULT_WINDOW_BITS,
297      memLevel: this._options.memLevel || DEFAULT_MEM_LEVEL
298    });
299  }
300  this._deflate.writeInProgress = true;
301
302  var self = this;
303  var buffers = [];
304
305  this._deflate.on('error', onError).on('data', onData);
306  this._deflate.write(data);
307  this._deflate.flush(function() {
308    cleanup();
309    var data = Buffer.concat(buffers);
310    if (fin) {
311      data = data.slice(0, data.length - 4);
312    }
313    callback(null, data);
314  });
315
316  function onError(err) {
317    cleanup();
318    callback(err);
319  }
320
321  function onData(data) {
322    buffers.push(data);
323  }
324
325  function cleanup() {
326    if (!self._deflate) return;
327    self._deflate.removeListener('error', onError);
328    self._deflate.removeListener('data', onData);
329    self._deflate.writeInProgress = false;
330    if ((fin && self.params[endpoint + '_no_context_takeover']) || self._deflate.pendingClose) {
331      if (self._deflate.close) self._deflate.close();
332      self._deflate = null;
333    }
334  }
335};
336
337module.exports = PerMessageDeflate;