[6a3a178] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | var 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 |
|
---|
| 11 | var 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 | };
|
---|
| 36 | util.inherits(Hybi, Base);
|
---|
| 37 |
|
---|
| 38 | Hybi.VERSION = '13';
|
---|
| 39 |
|
---|
| 40 | Hybi.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 |
|
---|
| 50 | Hybi.generateAccept = function(key) {
|
---|
| 51 | var sha1 = crypto.createHash('sha1');
|
---|
| 52 | sha1.update(key + Hybi.GUID);
|
---|
| 53 | return sha1.digest('base64');
|
---|
| 54 | };
|
---|
| 55 |
|
---|
| 56 | Hybi.GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
---|
| 57 |
|
---|
| 58 | var 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 |
|
---|
| 480 | for (var key in instance)
|
---|
| 481 | Hybi.prototype[key] = instance[key];
|
---|
| 482 |
|
---|
| 483 | module.exports = Hybi;
|
---|