source: frontend/node_modules/ws/lib/sender.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 10.6 KB
RevLine 
[9af201e]1/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^net|tls$" }] */
2
3'use strict';
4
5const net = require('net');
6const tls = require('tls');
7const { randomFillSync } = require('crypto');
8
9const PerMessageDeflate = require('./permessage-deflate');
10const { EMPTY_BUFFER } = require('./constants');
11const { isValidStatusCode } = require('./validation');
12const { mask: applyMask, toBuffer } = require('./buffer-util');
13
14const mask = Buffer.alloc(4);
15
16/**
17 * HyBi Sender implementation.
18 */
19class Sender {
20 /**
21 * Creates a Sender instance.
22 *
23 * @param {(net.Socket|tls.Socket)} socket The connection socket
24 * @param {Object} [extensions] An object containing the negotiated extensions
25 */
26 constructor(socket, extensions) {
27 this._extensions = extensions || {};
28 this._socket = socket;
29
30 this._firstFragment = true;
31 this._compress = false;
32
33 this._bufferedBytes = 0;
34 this._deflating = false;
35 this._queue = [];
36 }
37
38 /**
39 * Frames a piece of data according to the HyBi WebSocket protocol.
40 *
41 * @param {Buffer} data The data to frame
42 * @param {Object} options Options object
43 * @param {Number} options.opcode The opcode
44 * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
45 * modified
46 * @param {Boolean} [options.fin=false] Specifies whether or not to set the
47 * FIN bit
48 * @param {Boolean} [options.mask=false] Specifies whether or not to mask
49 * `data`
50 * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
51 * RSV1 bit
52 * @return {Buffer[]} The framed data as a list of `Buffer` instances
53 * @public
54 */
55 static frame(data, options) {
56 const merge = options.mask && options.readOnly;
57 let offset = options.mask ? 6 : 2;
58 let payloadLength = data.length;
59
60 if (data.length >= 65536) {
61 offset += 8;
62 payloadLength = 127;
63 } else if (data.length > 125) {
64 offset += 2;
65 payloadLength = 126;
66 }
67
68 const target = Buffer.allocUnsafe(merge ? data.length + offset : offset);
69
70 target[0] = options.fin ? options.opcode | 0x80 : options.opcode;
71 if (options.rsv1) target[0] |= 0x40;
72
73 target[1] = payloadLength;
74
75 if (payloadLength === 126) {
76 target.writeUInt16BE(data.length, 2);
77 } else if (payloadLength === 127) {
78 target.writeUInt32BE(0, 2);
79 target.writeUInt32BE(data.length, 6);
80 }
81
82 if (!options.mask) return [target, data];
83
84 randomFillSync(mask, 0, 4);
85
86 target[1] |= 0x80;
87 target[offset - 4] = mask[0];
88 target[offset - 3] = mask[1];
89 target[offset - 2] = mask[2];
90 target[offset - 1] = mask[3];
91
92 if (merge) {
93 applyMask(data, mask, target, offset, data.length);
94 return [target];
95 }
96
97 applyMask(data, mask, data, 0, data.length);
98 return [target, data];
99 }
100
101 /**
102 * Sends a close message to the other peer.
103 *
104 * @param {Number} [code] The status code component of the body
105 * @param {String} [data] The message component of the body
106 * @param {Boolean} [mask=false] Specifies whether or not to mask the message
107 * @param {Function} [cb] Callback
108 * @public
109 */
110 close(code, data, mask, cb) {
111 let buf;
112
113 if (code === undefined) {
114 buf = EMPTY_BUFFER;
115 } else if (typeof code !== 'number' || !isValidStatusCode(code)) {
116 throw new TypeError('First argument must be a valid error code number');
117 } else if (data === undefined || data === '') {
118 buf = Buffer.allocUnsafe(2);
119 buf.writeUInt16BE(code, 0);
120 } else {
121 const length = Buffer.byteLength(data);
122
123 if (length > 123) {
124 throw new RangeError('The message must not be greater than 123 bytes');
125 }
126
127 buf = Buffer.allocUnsafe(2 + length);
128 buf.writeUInt16BE(code, 0);
129 buf.write(data, 2);
130 }
131
132 if (this._deflating) {
133 this.enqueue([this.doClose, buf, mask, cb]);
134 } else {
135 this.doClose(buf, mask, cb);
136 }
137 }
138
139 /**
140 * Frames and sends a close message.
141 *
142 * @param {Buffer} data The message to send
143 * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
144 * @param {Function} [cb] Callback
145 * @private
146 */
147 doClose(data, mask, cb) {
148 this.sendFrame(
149 Sender.frame(data, {
150 fin: true,
151 rsv1: false,
152 opcode: 0x08,
153 mask,
154 readOnly: false
155 }),
156 cb
157 );
158 }
159
160 /**
161 * Sends a ping message to the other peer.
162 *
163 * @param {*} data The message to send
164 * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
165 * @param {Function} [cb] Callback
166 * @public
167 */
168 ping(data, mask, cb) {
169 const buf = toBuffer(data);
170
171 if (buf.length > 125) {
172 throw new RangeError('The data size must not be greater than 125 bytes');
173 }
174
175 if (this._deflating) {
176 this.enqueue([this.doPing, buf, mask, toBuffer.readOnly, cb]);
177 } else {
178 this.doPing(buf, mask, toBuffer.readOnly, cb);
179 }
180 }
181
182 /**
183 * Frames and sends a ping message.
184 *
185 * @param {Buffer} data The message to send
186 * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
187 * @param {Boolean} [readOnly=false] Specifies whether `data` can be modified
188 * @param {Function} [cb] Callback
189 * @private
190 */
191 doPing(data, mask, readOnly, cb) {
192 this.sendFrame(
193 Sender.frame(data, {
194 fin: true,
195 rsv1: false,
196 opcode: 0x09,
197 mask,
198 readOnly
199 }),
200 cb
201 );
202 }
203
204 /**
205 * Sends a pong message to the other peer.
206 *
207 * @param {*} data The message to send
208 * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
209 * @param {Function} [cb] Callback
210 * @public
211 */
212 pong(data, mask, cb) {
213 const buf = toBuffer(data);
214
215 if (buf.length > 125) {
216 throw new RangeError('The data size must not be greater than 125 bytes');
217 }
218
219 if (this._deflating) {
220 this.enqueue([this.doPong, buf, mask, toBuffer.readOnly, cb]);
221 } else {
222 this.doPong(buf, mask, toBuffer.readOnly, cb);
223 }
224 }
225
226 /**
227 * Frames and sends a pong message.
228 *
229 * @param {Buffer} data The message to send
230 * @param {Boolean} [mask=false] Specifies whether or not to mask `data`
231 * @param {Boolean} [readOnly=false] Specifies whether `data` can be modified
232 * @param {Function} [cb] Callback
233 * @private
234 */
235 doPong(data, mask, readOnly, cb) {
236 this.sendFrame(
237 Sender.frame(data, {
238 fin: true,
239 rsv1: false,
240 opcode: 0x0a,
241 mask,
242 readOnly
243 }),
244 cb
245 );
246 }
247
248 /**
249 * Sends a data message to the other peer.
250 *
251 * @param {*} data The message to send
252 * @param {Object} options Options object
253 * @param {Boolean} [options.compress=false] Specifies whether or not to
254 * compress `data`
255 * @param {Boolean} [options.binary=false] Specifies whether `data` is binary
256 * or text
257 * @param {Boolean} [options.fin=false] Specifies whether the fragment is the
258 * last one
259 * @param {Boolean} [options.mask=false] Specifies whether or not to mask
260 * `data`
261 * @param {Function} [cb] Callback
262 * @public
263 */
264 send(data, options, cb) {
265 const buf = toBuffer(data);
266 const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
267 let opcode = options.binary ? 2 : 1;
268 let rsv1 = options.compress;
269
270 if (this._firstFragment) {
271 this._firstFragment = false;
272 if (rsv1 && perMessageDeflate) {
273 rsv1 = buf.length >= perMessageDeflate._threshold;
274 }
275 this._compress = rsv1;
276 } else {
277 rsv1 = false;
278 opcode = 0;
279 }
280
281 if (options.fin) this._firstFragment = true;
282
283 if (perMessageDeflate) {
284 const opts = {
285 fin: options.fin,
286 rsv1,
287 opcode,
288 mask: options.mask,
289 readOnly: toBuffer.readOnly
290 };
291
292 if (this._deflating) {
293 this.enqueue([this.dispatch, buf, this._compress, opts, cb]);
294 } else {
295 this.dispatch(buf, this._compress, opts, cb);
296 }
297 } else {
298 this.sendFrame(
299 Sender.frame(buf, {
300 fin: options.fin,
301 rsv1: false,
302 opcode,
303 mask: options.mask,
304 readOnly: toBuffer.readOnly
305 }),
306 cb
307 );
308 }
309 }
310
311 /**
312 * Dispatches a data message.
313 *
314 * @param {Buffer} data The message to send
315 * @param {Boolean} [compress=false] Specifies whether or not to compress
316 * `data`
317 * @param {Object} options Options object
318 * @param {Number} options.opcode The opcode
319 * @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
320 * modified
321 * @param {Boolean} [options.fin=false] Specifies whether or not to set the
322 * FIN bit
323 * @param {Boolean} [options.mask=false] Specifies whether or not to mask
324 * `data`
325 * @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
326 * RSV1 bit
327 * @param {Function} [cb] Callback
328 * @private
329 */
330 dispatch(data, compress, options, cb) {
331 if (!compress) {
332 this.sendFrame(Sender.frame(data, options), cb);
333 return;
334 }
335
336 const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
337
338 this._bufferedBytes += data.length;
339 this._deflating = true;
340 perMessageDeflate.compress(data, options.fin, (_, buf) => {
341 if (this._socket.destroyed) {
342 const err = new Error(
343 'The socket was closed while data was being compressed'
344 );
345
346 if (typeof cb === 'function') cb(err);
347
348 for (let i = 0; i < this._queue.length; i++) {
349 const callback = this._queue[i][4];
350
351 if (typeof callback === 'function') callback(err);
352 }
353
354 return;
355 }
356
357 this._bufferedBytes -= data.length;
358 this._deflating = false;
359 options.readOnly = false;
360 this.sendFrame(Sender.frame(buf, options), cb);
361 this.dequeue();
362 });
363 }
364
365 /**
366 * Executes queued send operations.
367 *
368 * @private
369 */
370 dequeue() {
371 while (!this._deflating && this._queue.length) {
372 const params = this._queue.shift();
373
374 this._bufferedBytes -= params[1].length;
375 Reflect.apply(params[0], this, params.slice(1));
376 }
377 }
378
379 /**
380 * Enqueues a send operation.
381 *
382 * @param {Array} params Send operation parameters.
383 * @private
384 */
385 enqueue(params) {
386 this._bufferedBytes += params[1].length;
387 this._queue.push(params);
388 }
389
390 /**
391 * Sends a frame.
392 *
393 * @param {Buffer[]} list The frame to send
394 * @param {Function} [cb] Callback
395 * @private
396 */
397 sendFrame(list, cb) {
398 if (list.length === 2) {
399 this._socket.cork();
400 this._socket.write(list[0]);
401 this._socket.write(list[1], cb);
402 this._socket.uncork();
403 } else {
404 this._socket.write(list[0], cb);
405 }
406 }
407}
408
409module.exports = Sender;
Note: See TracBrowser for help on using the repository browser.