source: trip-planner-front/node_modules/websocket-driver/lib/websocket/driver/hybi.js

Last change on this file was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 14.1 KB
RevLine 
[6a3a178]1'use strict';
2
3var Buffer = require('safe-buffer').Buffer,
4 crypto = require('crypto'),
5 util = require('util'),
6 Extensions = require('websocket-extensions'),
7 Base = require('./base'),
8 Frame = require('./hybi/frame'),
9 Message = require('./hybi/message');
10
11var Hybi = function(request, url, options) {
12 Base.apply(this, arguments);
13
14 this._extensions = new Extensions();
15 this._stage = 0;
16 this._masking = this._options.masking;
17 this._protocols = this._options.protocols || [];
18 this._requireMasking = this._options.requireMasking;
19 this._pingCallbacks = {};
20
21 if (typeof this._protocols === 'string')
22 this._protocols = this._protocols.split(/ *, */);
23
24 if (!this._request) return;
25
26 var protos = this._request.headers['sec-websocket-protocol'],
27 supported = this._protocols;
28
29 if (protos !== undefined) {
30 if (typeof protos === 'string') protos = protos.split(/ *, */);
31 this.protocol = protos.filter(function(p) { return supported.indexOf(p) >= 0 })[0];
32 }
33
34 this.version = 'hybi-' + Hybi.VERSION;
35};
36util.inherits(Hybi, Base);
37
38Hybi.VERSION = '13';
39
40Hybi.mask = function(payload, mask, offset) {
41 if (!mask || mask.length === 0) return payload;
42 offset = offset || 0;
43
44 for (var i = 0, n = payload.length - offset; i < n; i++) {
45 payload[offset + i] = payload[offset + i] ^ mask[i % 4];
46 }
47 return payload;
48};
49
50Hybi.generateAccept = function(key) {
51 var sha1 = crypto.createHash('sha1');
52 sha1.update(key + Hybi.GUID);
53 return sha1.digest('base64');
54};
55
56Hybi.GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
57
58var instance = {
59 FIN: 0x80,
60 MASK: 0x80,
61 RSV1: 0x40,
62 RSV2: 0x20,
63 RSV3: 0x10,
64 OPCODE: 0x0F,
65 LENGTH: 0x7F,
66
67 OPCODES: {
68 continuation: 0,
69 text: 1,
70 binary: 2,
71 close: 8,
72 ping: 9,
73 pong: 10
74 },
75
76 OPCODE_CODES: [0, 1, 2, 8, 9, 10],
77 MESSAGE_OPCODES: [0, 1, 2],
78 OPENING_OPCODES: [1, 2],
79
80 ERRORS: {
81 normal_closure: 1000,
82 going_away: 1001,
83 protocol_error: 1002,
84 unacceptable: 1003,
85 encoding_error: 1007,
86 policy_violation: 1008,
87 too_large: 1009,
88 extension_error: 1010,
89 unexpected_condition: 1011
90 },
91
92 ERROR_CODES: [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011],
93 DEFAULT_ERROR_CODE: 1000,
94 MIN_RESERVED_ERROR: 3000,
95 MAX_RESERVED_ERROR: 4999,
96
97 // http://www.w3.org/International/questions/qa-forms-utf-8.en.php
98 UTF8_MATCH: /^([\x00-\x7F]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})*$/,
99
100 addExtension: function(extension) {
101 this._extensions.add(extension);
102 return true;
103 },
104
105 parse: function(chunk) {
106 this._reader.put(chunk);
107 var buffer = true;
108 while (buffer) {
109 switch (this._stage) {
110 case 0:
111 buffer = this._reader.read(1);
112 if (buffer) this._parseOpcode(buffer[0]);
113 break;
114
115 case 1:
116 buffer = this._reader.read(1);
117 if (buffer) this._parseLength(buffer[0]);
118 break;
119
120 case 2:
121 buffer = this._reader.read(this._frame.lengthBytes);
122 if (buffer) this._parseExtendedLength(buffer);
123 break;
124
125 case 3:
126 buffer = this._reader.read(4);
127 if (buffer) {
128 this._stage = 4;
129 this._frame.maskingKey = buffer;
130 }
131 break;
132
133 case 4:
134 buffer = this._reader.read(this._frame.length);
135 if (buffer) {
136 this._stage = 0;
137 this._emitFrame(buffer);
138 }
139 break;
140
141 default:
142 buffer = null;
143 }
144 }
145 },
146
147 text: function(message) {
148 if (this.readyState > 1) return false;
149 return this.frame(message, 'text');
150 },
151
152 binary: function(message) {
153 if (this.readyState > 1) return false;
154 return this.frame(message, 'binary');
155 },
156
157 ping: function(message, callback) {
158 if (this.readyState > 1) return false;
159 message = message || '';
160 if (callback) this._pingCallbacks[message] = callback;
161 return this.frame(message, 'ping');
162 },
163
164 pong: function(message) {
165 if (this.readyState > 1) return false;
166 message = message ||'';
167 return this.frame(message, 'pong');
168 },
169
170 close: function(reason, code) {
171 reason = reason || '';
172 code = code || this.ERRORS.normal_closure;
173
174 if (this.readyState <= 0) {
175 this.readyState = 3;
176 this.emit('close', new Base.CloseEvent(code, reason));
177 return true;
178 } else if (this.readyState === 1) {
179 this.readyState = 2;
180 this._extensions.close(function() { this.frame(reason, 'close', code) }, this);
181 return true;
182 } else {
183 return false;
184 }
185 },
186
187 frame: function(buffer, type, code) {
188 if (this.readyState <= 0) return this._queue([buffer, type, code]);
189 if (this.readyState > 2) return false;
190
191 if (buffer instanceof Array) buffer = Buffer.from(buffer);
192 if (typeof buffer === 'number') buffer = buffer.toString();
193
194 var message = new Message(),
195 isText = (typeof buffer === 'string'),
196 payload, copy;
197
198 message.rsv1 = message.rsv2 = message.rsv3 = false;
199 message.opcode = this.OPCODES[type || (isText ? 'text' : 'binary')];
200
201 payload = isText ? Buffer.from(buffer, 'utf8') : buffer;
202
203 if (code) {
204 copy = payload;
205 payload = Buffer.allocUnsafe(2 + copy.length);
206 payload.writeUInt16BE(code, 0);
207 copy.copy(payload, 2);
208 }
209 message.data = payload;
210
211 var onMessageReady = function(message) {
212 var frame = new Frame();
213
214 frame.final = true;
215 frame.rsv1 = message.rsv1;
216 frame.rsv2 = message.rsv2;
217 frame.rsv3 = message.rsv3;
218 frame.opcode = message.opcode;
219 frame.masked = !!this._masking;
220 frame.length = message.data.length;
221 frame.payload = message.data;
222
223 if (frame.masked) frame.maskingKey = crypto.randomBytes(4);
224
225 this._sendFrame(frame);
226 };
227
228 if (this.MESSAGE_OPCODES.indexOf(message.opcode) >= 0)
229 this._extensions.processOutgoingMessage(message, function(error, message) {
230 if (error) return this._fail('extension_error', error.message);
231 onMessageReady.call(this, message);
232 }, this);
233 else
234 onMessageReady.call(this, message);
235
236 return true;
237 },
238
239 _sendFrame: function(frame) {
240 var length = frame.length,
241 header = (length <= 125) ? 2 : (length <= 65535 ? 4 : 10),
242 offset = header + (frame.masked ? 4 : 0),
243 buffer = Buffer.allocUnsafe(offset + length),
244 masked = frame.masked ? this.MASK : 0;
245
246 buffer[0] = (frame.final ? this.FIN : 0) |
247 (frame.rsv1 ? this.RSV1 : 0) |
248 (frame.rsv2 ? this.RSV2 : 0) |
249 (frame.rsv3 ? this.RSV3 : 0) |
250 frame.opcode;
251
252 if (length <= 125) {
253 buffer[1] = masked | length;
254 } else if (length <= 65535) {
255 buffer[1] = masked | 126;
256 buffer.writeUInt16BE(length, 2);
257 } else {
258 buffer[1] = masked | 127;
259 buffer.writeUInt32BE(Math.floor(length / 0x100000000), 2);
260 buffer.writeUInt32BE(length % 0x100000000, 6);
261 }
262
263 frame.payload.copy(buffer, offset);
264
265 if (frame.masked) {
266 frame.maskingKey.copy(buffer, header);
267 Hybi.mask(buffer, frame.maskingKey, offset);
268 }
269
270 this._write(buffer);
271 },
272
273 _handshakeResponse: function() {
274 var secKey = this._request.headers['sec-websocket-key'],
275 version = this._request.headers['sec-websocket-version'];
276
277 if (version !== Hybi.VERSION)
278 throw new Error('Unsupported WebSocket version: ' + version);
279
280 if (typeof secKey !== 'string')
281 throw new Error('Missing handshake request header: Sec-WebSocket-Key');
282
283 this._headers.set('Upgrade', 'websocket');
284 this._headers.set('Connection', 'Upgrade');
285 this._headers.set('Sec-WebSocket-Accept', Hybi.generateAccept(secKey));
286
287 if (this.protocol) this._headers.set('Sec-WebSocket-Protocol', this.protocol);
288
289 var extensions = this._extensions.generateResponse(this._request.headers['sec-websocket-extensions']);
290 if (extensions) this._headers.set('Sec-WebSocket-Extensions', extensions);
291
292 var start = 'HTTP/1.1 101 Switching Protocols',
293 headers = [start, this._headers.toString(), ''];
294
295 return Buffer.from(headers.join('\r\n'), 'utf8');
296 },
297
298 _shutdown: function(code, reason, error) {
299 delete this._frame;
300 delete this._message;
301 this._stage = 5;
302
303 var sendCloseFrame = (this.readyState === 1);
304 this.readyState = 2;
305
306 this._extensions.close(function() {
307 if (sendCloseFrame) this.frame(reason, 'close', code);
308 this.readyState = 3;
309 if (error) this.emit('error', new Error(reason));
310 this.emit('close', new Base.CloseEvent(code, reason));
311 }, this);
312 },
313
314 _fail: function(type, message) {
315 if (this.readyState > 1) return;
316 this._shutdown(this.ERRORS[type], message, true);
317 },
318
319 _parseOpcode: function(octet) {
320 var rsvs = [this.RSV1, this.RSV2, this.RSV3].map(function(rsv) {
321 return (octet & rsv) === rsv;
322 });
323
324 var frame = this._frame = new Frame();
325
326 frame.final = (octet & this.FIN) === this.FIN;
327 frame.rsv1 = rsvs[0];
328 frame.rsv2 = rsvs[1];
329 frame.rsv3 = rsvs[2];
330 frame.opcode = (octet & this.OPCODE);
331
332 this._stage = 1;
333
334 if (!this._extensions.validFrameRsv(frame))
335 return this._fail('protocol_error',
336 'One or more reserved bits are on: reserved1 = ' + (frame.rsv1 ? 1 : 0) +
337 ', reserved2 = ' + (frame.rsv2 ? 1 : 0) +
338 ', reserved3 = ' + (frame.rsv3 ? 1 : 0));
339
340 if (this.OPCODE_CODES.indexOf(frame.opcode) < 0)
341 return this._fail('protocol_error', 'Unrecognized frame opcode: ' + frame.opcode);
342
343 if (this.MESSAGE_OPCODES.indexOf(frame.opcode) < 0 && !frame.final)
344 return this._fail('protocol_error', 'Received fragmented control frame: opcode = ' + frame.opcode);
345
346 if (this._message && this.OPENING_OPCODES.indexOf(frame.opcode) >= 0)
347 return this._fail('protocol_error', 'Received new data frame but previous continuous frame is unfinished');
348 },
349
350 _parseLength: function(octet) {
351 var frame = this._frame;
352 frame.masked = (octet & this.MASK) === this.MASK;
353 frame.length = (octet & this.LENGTH);
354
355 if (frame.length >= 0 && frame.length <= 125) {
356 this._stage = frame.masked ? 3 : 4;
357 if (!this._checkFrameLength()) return;
358 } else {
359 this._stage = 2;
360 frame.lengthBytes = (frame.length === 126 ? 2 : 8);
361 }
362
363 if (this._requireMasking && !frame.masked)
364 return this._fail('unacceptable', 'Received unmasked frame but masking is required');
365 },
366
367 _parseExtendedLength: function(buffer) {
368 var frame = this._frame;
369 frame.length = this._readUInt(buffer);
370
371 this._stage = frame.masked ? 3 : 4;
372
373 if (this.MESSAGE_OPCODES.indexOf(frame.opcode) < 0 && frame.length > 125)
374 return this._fail('protocol_error', 'Received control frame having too long payload: ' + frame.length);
375
376 if (!this._checkFrameLength()) return;
377 },
378
379 _checkFrameLength: function() {
380 var length = this._message ? this._message.length : 0;
381
382 if (length + this._frame.length > this._maxLength) {
383 this._fail('too_large', 'WebSocket frame length too large');
384 return false;
385 } else {
386 return true;
387 }
388 },
389
390 _emitFrame: function(buffer) {
391 var frame = this._frame,
392 payload = frame.payload = Hybi.mask(buffer, frame.maskingKey),
393 opcode = frame.opcode,
394 message,
395 code, reason,
396 callbacks, callback;
397
398 delete this._frame;
399
400 if (opcode === this.OPCODES.continuation) {
401 if (!this._message) return this._fail('protocol_error', 'Received unexpected continuation frame');
402 this._message.pushFrame(frame);
403 }
404
405 if (opcode === this.OPCODES.text || opcode === this.OPCODES.binary) {
406 this._message = new Message();
407 this._message.pushFrame(frame);
408 }
409
410 if (frame.final && this.MESSAGE_OPCODES.indexOf(opcode) >= 0)
411 return this._emitMessage(this._message);
412
413 if (opcode === this.OPCODES.close) {
414 code = (payload.length >= 2) ? payload.readUInt16BE(0) : null;
415 reason = (payload.length > 2) ? this._encode(payload.slice(2)) : null;
416
417 if (!(payload.length === 0) &&
418 !(code !== null && code >= this.MIN_RESERVED_ERROR && code <= this.MAX_RESERVED_ERROR) &&
419 this.ERROR_CODES.indexOf(code) < 0)
420 code = this.ERRORS.protocol_error;
421
422 if (payload.length > 125 || (payload.length > 2 && !reason))
423 code = this.ERRORS.protocol_error;
424
425 this._shutdown(code || this.DEFAULT_ERROR_CODE, reason || '');
426 }
427
428 if (opcode === this.OPCODES.ping) {
429 this.frame(payload, 'pong');
430 this.emit('ping', new Base.PingEvent(payload.toString()))
431 }
432
433 if (opcode === this.OPCODES.pong) {
434 callbacks = this._pingCallbacks;
435 message = this._encode(payload);
436 callback = callbacks[message];
437
438 delete callbacks[message];
439 if (callback) callback()
440
441 this.emit('pong', new Base.PongEvent(payload.toString()))
442 }
443 },
444
445 _emitMessage: function(message) {
446 var message = this._message;
447 message.read();
448
449 delete this._message;
450
451 this._extensions.processIncomingMessage(message, function(error, message) {
452 if (error) return this._fail('extension_error', error.message);
453
454 var payload = message.data;
455 if (message.opcode === this.OPCODES.text) payload = this._encode(payload);
456
457 if (payload === null)
458 return this._fail('encoding_error', 'Could not decode a text frame as UTF-8');
459 else
460 this.emit('message', new Base.MessageEvent(payload));
461 }, this);
462 },
463
464 _encode: function(buffer) {
465 try {
466 var string = buffer.toString('binary', 0, buffer.length);
467 if (!this.UTF8_MATCH.test(string)) return null;
468 } catch (e) {}
469 return buffer.toString('utf8', 0, buffer.length);
470 },
471
472 _readUInt: function(buffer) {
473 if (buffer.length === 2) return buffer.readUInt16BE(0);
474
475 return buffer.readUInt32BE(0) * 0x100000000 +
476 buffer.readUInt32BE(4);
477 }
478};
479
480for (var key in instance)
481 Hybi.prototype[key] = instance[key];
482
483module.exports = Hybi;
Note: See TracBrowser for help on using the repository browser.