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;