diff options
| author | altaf-creator <dev@altafcreator.com> | 2025-11-09 11:15:19 +0800 |
|---|---|---|
| committer | altaf-creator <dev@altafcreator.com> | 2025-11-09 11:15:19 +0800 |
| commit | 8eff962cab608341a6f2fedc640a0e32d96f26e2 (patch) | |
| tree | 05534d1a720ddc3691d346c69b4972555820a061 /frontend-old/node_modules/websocket-driver/lib/websocket/driver | |
pain
Diffstat (limited to 'frontend-old/node_modules/websocket-driver/lib/websocket/driver')
11 files changed, 1428 insertions, 0 deletions
diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/base.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/base.js new file mode 100644 index 0000000..f05af5f --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/base.js @@ -0,0 +1,193 @@ +'use strict'; + +var Buffer = require('safe-buffer').Buffer, + Emitter = require('events').EventEmitter, + util = require('util'), + streams = require('../streams'), + Headers = require('./headers'), + Reader = require('./stream_reader'); + +var Base = function(request, url, options) { + Emitter.call(this); + Base.validateOptions(options || {}, ['maxLength', 'masking', 'requireMasking', 'protocols']); + + this._request = request; + this._reader = new Reader(); + this._options = options || {}; + this._maxLength = this._options.maxLength || this.MAX_LENGTH; + this._headers = new Headers(); + this.__queue = []; + this.readyState = 0; + this.url = url; + + this.io = new streams.IO(this); + this.messages = new streams.Messages(this); + this._bindEventListeners(); +}; +util.inherits(Base, Emitter); + +Base.isWebSocket = function(request) { + var connection = request.headers.connection || '', + upgrade = request.headers.upgrade || ''; + + return request.method === 'GET' && + connection.toLowerCase().split(/ *, */).indexOf('upgrade') >= 0 && + upgrade.toLowerCase() === 'websocket'; +}; + +Base.validateOptions = function(options, validKeys) { + for (var key in options) { + if (validKeys.indexOf(key) < 0) + throw new Error('Unrecognized option: ' + key); + } +}; + +var instance = { + // This is 64MB, small enough for an average VPS to handle without + // crashing from process out of memory + MAX_LENGTH: 0x3ffffff, + + STATES: ['connecting', 'open', 'closing', 'closed'], + + _bindEventListeners: function() { + var self = this; + + // Protocol errors are informational and do not have to be handled + this.messages.on('error', function() {}); + + this.on('message', function(event) { + var messages = self.messages; + if (messages.readable) messages.emit('data', event.data); + }); + + this.on('error', function(error) { + var messages = self.messages; + if (messages.readable) messages.emit('error', error); + }); + + this.on('close', function() { + var messages = self.messages; + if (!messages.readable) return; + messages.readable = messages.writable = false; + messages.emit('end'); + }); + }, + + getState: function() { + return this.STATES[this.readyState] || null; + }, + + addExtension: function(extension) { + return false; + }, + + setHeader: function(name, value) { + if (this.readyState > 0) return false; + this._headers.set(name, value); + return true; + }, + + start: function() { + if (this.readyState !== 0) return false; + + if (!Base.isWebSocket(this._request)) + return this._failHandshake(new Error('Not a WebSocket request')); + + var response; + + try { + response = this._handshakeResponse(); + } catch (error) { + return this._failHandshake(error); + } + + this._write(response); + if (this._stage !== -1) this._open(); + return true; + }, + + _failHandshake: function(error) { + var headers = new Headers(); + headers.set('Content-Type', 'text/plain'); + headers.set('Content-Length', Buffer.byteLength(error.message, 'utf8')); + + headers = ['HTTP/1.1 400 Bad Request', headers.toString(), error.message]; + this._write(Buffer.from(headers.join('\r\n'), 'utf8')); + this._fail('protocol_error', error.message); + + return false; + }, + + text: function(message) { + return this.frame(message); + }, + + binary: function(message) { + return false; + }, + + ping: function() { + return false; + }, + + pong: function() { + return false; + }, + + close: function(reason, code) { + if (this.readyState !== 1) return false; + this.readyState = 3; + this.emit('close', new Base.CloseEvent(null, null)); + return true; + }, + + _open: function() { + this.readyState = 1; + this.__queue.forEach(function(args) { this.frame.apply(this, args) }, this); + this.__queue = []; + this.emit('open', new Base.OpenEvent()); + }, + + _queue: function(message) { + this.__queue.push(message); + return true; + }, + + _write: function(chunk) { + var io = this.io; + if (io.readable) io.emit('data', chunk); + }, + + _fail: function(type, message) { + this.readyState = 2; + this.emit('error', new Error(message)); + this.close(); + } +}; + +for (var key in instance) + Base.prototype[key] = instance[key]; + + +Base.ConnectEvent = function() {}; + +Base.OpenEvent = function() {}; + +Base.CloseEvent = function(code, reason) { + this.code = code; + this.reason = reason; +}; + +Base.MessageEvent = function(data) { + this.data = data; +}; + +Base.PingEvent = function(data) { + this.data = data; +}; + +Base.PongEvent = function(data) { + this.data = data; +}; + +module.exports = Base; diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/client.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/client.js new file mode 100644 index 0000000..f24d645 --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/client.js @@ -0,0 +1,142 @@ +'use strict'; + +var Buffer = require('safe-buffer').Buffer, + crypto = require('crypto'), + url = require('url'), + util = require('util'), + HttpParser = require('../http_parser'), + Base = require('./base'), + Hybi = require('./hybi'), + Proxy = require('./proxy'); + +var Client = function(_url, options) { + this.version = 'hybi-' + Hybi.VERSION; + Hybi.call(this, null, _url, options); + + this.readyState = -1; + this._key = Client.generateKey(); + this._accept = Hybi.generateAccept(this._key); + this._http = new HttpParser('response'); + + var uri = url.parse(this.url), + auth = uri.auth && Buffer.from(uri.auth, 'utf8').toString('base64'); + + if (this.VALID_PROTOCOLS.indexOf(uri.protocol) < 0) + throw new Error(this.url + ' is not a valid WebSocket URL'); + + this._pathname = (uri.pathname || '/') + (uri.search || ''); + + this._headers.set('Host', uri.host); + this._headers.set('Upgrade', 'websocket'); + this._headers.set('Connection', 'Upgrade'); + this._headers.set('Sec-WebSocket-Key', this._key); + this._headers.set('Sec-WebSocket-Version', Hybi.VERSION); + + if (this._protocols.length > 0) + this._headers.set('Sec-WebSocket-Protocol', this._protocols.join(', ')); + + if (auth) + this._headers.set('Authorization', 'Basic ' + auth); +}; +util.inherits(Client, Hybi); + +Client.generateKey = function() { + return crypto.randomBytes(16).toString('base64'); +}; + +var instance = { + VALID_PROTOCOLS: ['ws:', 'wss:'], + + proxy: function(origin, options) { + return new Proxy(this, origin, options); + }, + + start: function() { + if (this.readyState !== -1) return false; + this._write(this._handshakeRequest()); + this.readyState = 0; + return true; + }, + + parse: function(chunk) { + if (this.readyState === 3) return; + if (this.readyState > 0) return Hybi.prototype.parse.call(this, chunk); + + this._http.parse(chunk); + if (!this._http.isComplete()) return; + + this._validateHandshake(); + if (this.readyState === 3) return; + + this._open(); + this.parse(this._http.body); + }, + + _handshakeRequest: function() { + var extensions = this._extensions.generateOffer(); + if (extensions) + this._headers.set('Sec-WebSocket-Extensions', extensions); + + var start = 'GET ' + this._pathname + ' HTTP/1.1', + headers = [start, this._headers.toString(), '']; + + return Buffer.from(headers.join('\r\n'), 'utf8'); + }, + + _failHandshake: function(message) { + message = 'Error during WebSocket handshake: ' + message; + this.readyState = 3; + this.emit('error', new Error(message)); + this.emit('close', new Base.CloseEvent(this.ERRORS.protocol_error, message)); + }, + + _validateHandshake: function() { + this.statusCode = this._http.statusCode; + this.headers = this._http.headers; + + if (this._http.error) + return this._failHandshake(this._http.error.message); + + if (this._http.statusCode !== 101) + return this._failHandshake('Unexpected response code: ' + this._http.statusCode); + + var headers = this._http.headers, + upgrade = headers['upgrade'] || '', + connection = headers['connection'] || '', + accept = headers['sec-websocket-accept'] || '', + protocol = headers['sec-websocket-protocol'] || ''; + + if (upgrade === '') + return this._failHandshake("'Upgrade' header is missing"); + if (upgrade.toLowerCase() !== 'websocket') + return this._failHandshake("'Upgrade' header value is not 'WebSocket'"); + + if (connection === '') + return this._failHandshake("'Connection' header is missing"); + if (connection.toLowerCase() !== 'upgrade') + return this._failHandshake("'Connection' header value is not 'Upgrade'"); + + if (accept !== this._accept) + return this._failHandshake('Sec-WebSocket-Accept mismatch'); + + this.protocol = null; + + if (protocol !== '') { + if (this._protocols.indexOf(protocol) < 0) + return this._failHandshake('Sec-WebSocket-Protocol mismatch'); + else + this.protocol = protocol; + } + + try { + this._extensions.activate(this.headers['sec-websocket-extensions']); + } catch (e) { + return this._failHandshake(e.message); + } + } +}; + +for (var key in instance) + Client.prototype[key] = instance[key]; + +module.exports = Client; diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/draft75.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/draft75.js new file mode 100644 index 0000000..583f985 --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/draft75.js @@ -0,0 +1,123 @@ +'use strict'; + +var Buffer = require('safe-buffer').Buffer, + Base = require('./base'), + util = require('util'); + +var Draft75 = function(request, url, options) { + Base.apply(this, arguments); + this._stage = 0; + this.version = 'hixie-75'; + + this._headers.set('Upgrade', 'WebSocket'); + this._headers.set('Connection', 'Upgrade'); + this._headers.set('WebSocket-Origin', this._request.headers.origin); + this._headers.set('WebSocket-Location', this.url); +}; +util.inherits(Draft75, Base); + +var instance = { + close: function() { + if (this.readyState === 3) return false; + this.readyState = 3; + this.emit('close', new Base.CloseEvent(null, null)); + return true; + }, + + parse: function(chunk) { + if (this.readyState > 1) return; + + this._reader.put(chunk); + + this._reader.eachByte(function(octet) { + var message; + + switch (this._stage) { + case -1: + this._body.push(octet); + this._sendHandshakeBody(); + break; + + case 0: + this._parseLeadingByte(octet); + break; + + case 1: + this._length = (octet & 0x7F) + 128 * this._length; + + if (this._closing && this._length === 0) { + return this.close(); + } + else if ((octet & 0x80) !== 0x80) { + if (this._length === 0) { + this._stage = 0; + } + else { + this._skipped = 0; + this._stage = 2; + } + } + break; + + case 2: + if (octet === 0xFF) { + this._stage = 0; + message = Buffer.from(this._buffer).toString('utf8', 0, this._buffer.length); + this.emit('message', new Base.MessageEvent(message)); + } + else { + if (this._length) { + this._skipped += 1; + if (this._skipped === this._length) + this._stage = 0; + } else { + this._buffer.push(octet); + if (this._buffer.length > this._maxLength) return this.close(); + } + } + break; + } + }, this); + }, + + frame: function(buffer) { + if (this.readyState === 0) return this._queue([buffer]); + if (this.readyState > 1) return false; + + if (typeof buffer !== 'string') buffer = buffer.toString(); + + var length = Buffer.byteLength(buffer), + frame = Buffer.allocUnsafe(length + 2); + + frame[0] = 0x00; + frame.write(buffer, 1); + frame[frame.length - 1] = 0xFF; + + this._write(frame); + return true; + }, + + _handshakeResponse: function() { + var start = 'HTTP/1.1 101 Web Socket Protocol Handshake', + headers = [start, this._headers.toString(), '']; + + return Buffer.from(headers.join('\r\n'), 'utf8'); + }, + + _parseLeadingByte: function(octet) { + if ((octet & 0x80) === 0x80) { + this._length = 0; + this._stage = 1; + } else { + delete this._length; + delete this._skipped; + this._buffer = []; + this._stage = 2; + } + } +}; + +for (var key in instance) + Draft75.prototype[key] = instance[key]; + +module.exports = Draft75; diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/draft76.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/draft76.js new file mode 100644 index 0000000..bdaab31 --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/draft76.js @@ -0,0 +1,117 @@ +'use strict'; + +var Buffer = require('safe-buffer').Buffer, + Base = require('./base'), + Draft75 = require('./draft75'), + crypto = require('crypto'), + util = require('util'); + + +var numberFromKey = function(key) { + return parseInt((key.match(/[0-9]/g) || []).join(''), 10); +}; + +var spacesInKey = function(key) { + return (key.match(/ /g) || []).length; +}; + + +var Draft76 = function(request, url, options) { + Draft75.apply(this, arguments); + this._stage = -1; + this._body = []; + this.version = 'hixie-76'; + + this._headers.clear(); + + this._headers.set('Upgrade', 'WebSocket'); + this._headers.set('Connection', 'Upgrade'); + this._headers.set('Sec-WebSocket-Origin', this._request.headers.origin); + this._headers.set('Sec-WebSocket-Location', this.url); +}; +util.inherits(Draft76, Draft75); + +var instance = { + BODY_SIZE: 8, + + start: function() { + if (!Draft75.prototype.start.call(this)) return false; + this._started = true; + this._sendHandshakeBody(); + return true; + }, + + close: function() { + if (this.readyState === 3) return false; + if (this.readyState === 1) this._write(Buffer.from([0xFF, 0x00])); + this.readyState = 3; + this.emit('close', new Base.CloseEvent(null, null)); + return true; + }, + + _handshakeResponse: function() { + var headers = this._request.headers, + key1 = headers['sec-websocket-key1'], + key2 = headers['sec-websocket-key2']; + + if (!key1) throw new Error('Missing required header: Sec-WebSocket-Key1'); + if (!key2) throw new Error('Missing required header: Sec-WebSocket-Key2'); + + var number1 = numberFromKey(key1), + spaces1 = spacesInKey(key1), + + number2 = numberFromKey(key2), + spaces2 = spacesInKey(key2); + + if (number1 % spaces1 !== 0 || number2 % spaces2 !== 0) + throw new Error('Client sent invalid Sec-WebSocket-Key headers'); + + this._keyValues = [number1 / spaces1, number2 / spaces2]; + + var start = 'HTTP/1.1 101 WebSocket Protocol Handshake', + headers = [start, this._headers.toString(), '']; + + return Buffer.from(headers.join('\r\n'), 'binary'); + }, + + _handshakeSignature: function() { + if (this._body.length < this.BODY_SIZE) return null; + + var md5 = crypto.createHash('md5'), + buffer = Buffer.allocUnsafe(8 + this.BODY_SIZE); + + buffer.writeUInt32BE(this._keyValues[0], 0); + buffer.writeUInt32BE(this._keyValues[1], 4); + Buffer.from(this._body).copy(buffer, 8, 0, this.BODY_SIZE); + + md5.update(buffer); + return Buffer.from(md5.digest('binary'), 'binary'); + }, + + _sendHandshakeBody: function() { + if (!this._started) return; + var signature = this._handshakeSignature(); + if (!signature) return; + + this._write(signature); + this._stage = 0; + this._open(); + + if (this._body.length > this.BODY_SIZE) + this.parse(this._body.slice(this.BODY_SIZE)); + }, + + _parseLeadingByte: function(octet) { + if (octet !== 0xFF) + return Draft75.prototype._parseLeadingByte.call(this, octet); + + this._closing = true; + this._length = 0; + this._stage = 1; + } +}; + +for (var key in instance) + Draft76.prototype[key] = instance[key]; + +module.exports = Draft76; diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/headers.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/headers.js new file mode 100644 index 0000000..bc96b7d --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/headers.js @@ -0,0 +1,35 @@ +'use strict'; + +var Headers = function() { + this.clear(); +}; + +Headers.prototype.ALLOWED_DUPLICATES = ['set-cookie', 'set-cookie2', 'warning', 'www-authenticate']; + +Headers.prototype.clear = function() { + this._sent = {}; + this._lines = []; +}; + +Headers.prototype.set = function(name, value) { + if (value === undefined) return; + + name = this._strip(name); + value = this._strip(value); + + var key = name.toLowerCase(); + if (!this._sent.hasOwnProperty(key) || this.ALLOWED_DUPLICATES.indexOf(key) >= 0) { + this._sent[key] = true; + this._lines.push(name + ': ' + value + '\r\n'); + } +}; + +Headers.prototype.toString = function() { + return this._lines.join(''); +}; + +Headers.prototype._strip = function(string) { + return string.toString().replace(/^ */, '').replace(/ *$/, ''); +}; + +module.exports = Headers; diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/hybi.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/hybi.js new file mode 100644 index 0000000..9027f90 --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/hybi.js @@ -0,0 +1,483 @@ +'use strict'; + +var Buffer = require('safe-buffer').Buffer, + crypto = require('crypto'), + util = require('util'), + Extensions = require('websocket-extensions'), + Base = require('./base'), + Frame = require('./hybi/frame'), + Message = require('./hybi/message'); + +var Hybi = function(request, url, options) { + Base.apply(this, arguments); + + this._extensions = new Extensions(); + this._stage = 0; + this._masking = this._options.masking; + this._protocols = this._options.protocols || []; + this._requireMasking = this._options.requireMasking; + this._pingCallbacks = {}; + + if (typeof this._protocols === 'string') + this._protocols = this._protocols.split(/ *, */); + + if (!this._request) return; + + var protos = this._request.headers['sec-websocket-protocol'], + supported = this._protocols; + + if (protos !== undefined) { + if (typeof protos === 'string') protos = protos.split(/ *, */); + this.protocol = protos.filter(function(p) { return supported.indexOf(p) >= 0 })[0]; + } + + this.version = 'hybi-' + Hybi.VERSION; +}; +util.inherits(Hybi, Base); + +Hybi.VERSION = '13'; + +Hybi.mask = function(payload, mask, offset) { + if (!mask || mask.length === 0) return payload; + offset = offset || 0; + + for (var i = 0, n = payload.length - offset; i < n; i++) { + payload[offset + i] = payload[offset + i] ^ mask[i % 4]; + } + return payload; +}; + +Hybi.generateAccept = function(key) { + var sha1 = crypto.createHash('sha1'); + sha1.update(key + Hybi.GUID); + return sha1.digest('base64'); +}; + +Hybi.GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; + +var instance = { + FIN: 0x80, + MASK: 0x80, + RSV1: 0x40, + RSV2: 0x20, + RSV3: 0x10, + OPCODE: 0x0F, + LENGTH: 0x7F, + + OPCODES: { + continuation: 0, + text: 1, + binary: 2, + close: 8, + ping: 9, + pong: 10 + }, + + OPCODE_CODES: [0, 1, 2, 8, 9, 10], + MESSAGE_OPCODES: [0, 1, 2], + OPENING_OPCODES: [1, 2], + + ERRORS: { + normal_closure: 1000, + going_away: 1001, + protocol_error: 1002, + unacceptable: 1003, + encoding_error: 1007, + policy_violation: 1008, + too_large: 1009, + extension_error: 1010, + unexpected_condition: 1011 + }, + + ERROR_CODES: [1000, 1001, 1002, 1003, 1007, 1008, 1009, 1010, 1011], + DEFAULT_ERROR_CODE: 1000, + MIN_RESERVED_ERROR: 3000, + MAX_RESERVED_ERROR: 4999, + + // http://www.w3.org/International/questions/qa-forms-utf-8.en.php + 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})*$/, + + addExtension: function(extension) { + this._extensions.add(extension); + return true; + }, + + parse: function(chunk) { + this._reader.put(chunk); + var buffer = true; + while (buffer) { + switch (this._stage) { + case 0: + buffer = this._reader.read(1); + if (buffer) this._parseOpcode(buffer[0]); + break; + + case 1: + buffer = this._reader.read(1); + if (buffer) this._parseLength(buffer[0]); + break; + + case 2: + buffer = this._reader.read(this._frame.lengthBytes); + if (buffer) this._parseExtendedLength(buffer); + break; + + case 3: + buffer = this._reader.read(4); + if (buffer) { + this._stage = 4; + this._frame.maskingKey = buffer; + } + break; + + case 4: + buffer = this._reader.read(this._frame.length); + if (buffer) { + this._stage = 0; + this._emitFrame(buffer); + } + break; + + default: + buffer = null; + } + } + }, + + text: function(message) { + if (this.readyState > 1) return false; + return this.frame(message, 'text'); + }, + + binary: function(message) { + if (this.readyState > 1) return false; + return this.frame(message, 'binary'); + }, + + ping: function(message, callback) { + if (this.readyState > 1) return false; + message = message || ''; + if (callback) this._pingCallbacks[message] = callback; + return this.frame(message, 'ping'); + }, + + pong: function(message) { + if (this.readyState > 1) return false; + message = message ||''; + return this.frame(message, 'pong'); + }, + + close: function(reason, code) { + reason = reason || ''; + code = code || this.ERRORS.normal_closure; + + if (this.readyState <= 0) { + this.readyState = 3; + this.emit('close', new Base.CloseEvent(code, reason)); + return true; + } else if (this.readyState === 1) { + this.readyState = 2; + this._extensions.close(function() { this.frame(reason, 'close', code) }, this); + return true; + } else { + return false; + } + }, + + frame: function(buffer, type, code) { + if (this.readyState <= 0) return this._queue([buffer, type, code]); + if (this.readyState > 2) return false; + + if (buffer instanceof Array) buffer = Buffer.from(buffer); + if (typeof buffer === 'number') buffer = buffer.toString(); + + var message = new Message(), + isText = (typeof buffer === 'string'), + payload, copy; + + message.rsv1 = message.rsv2 = message.rsv3 = false; + message.opcode = this.OPCODES[type || (isText ? 'text' : 'binary')]; + + payload = isText ? Buffer.from(buffer, 'utf8') : buffer; + + if (code) { + copy = payload; + payload = Buffer.allocUnsafe(2 + copy.length); + payload.writeUInt16BE(code, 0); + copy.copy(payload, 2); + } + message.data = payload; + + var onMessageReady = function(message) { + var frame = new Frame(); + + frame.final = true; + frame.rsv1 = message.rsv1; + frame.rsv2 = message.rsv2; + frame.rsv3 = message.rsv3; + frame.opcode = message.opcode; + frame.masked = !!this._masking; + frame.length = message.data.length; + frame.payload = message.data; + + if (frame.masked) frame.maskingKey = crypto.randomBytes(4); + + this._sendFrame(frame); + }; + + if (this.MESSAGE_OPCODES.indexOf(message.opcode) >= 0) + this._extensions.processOutgoingMessage(message, function(error, message) { + if (error) return this._fail('extension_error', error.message); + onMessageReady.call(this, message); + }, this); + else + onMessageReady.call(this, message); + + return true; + }, + + _sendFrame: function(frame) { + var length = frame.length, + header = (length <= 125) ? 2 : (length <= 65535 ? 4 : 10), + offset = header + (frame.masked ? 4 : 0), + buffer = Buffer.allocUnsafe(offset + length), + masked = frame.masked ? this.MASK : 0; + + buffer[0] = (frame.final ? this.FIN : 0) | + (frame.rsv1 ? this.RSV1 : 0) | + (frame.rsv2 ? this.RSV2 : 0) | + (frame.rsv3 ? this.RSV3 : 0) | + frame.opcode; + + if (length <= 125) { + buffer[1] = masked | length; + } else if (length <= 65535) { + buffer[1] = masked | 126; + buffer.writeUInt16BE(length, 2); + } else { + buffer[1] = masked | 127; + buffer.writeUInt32BE(Math.floor(length / 0x100000000), 2); + buffer.writeUInt32BE(length % 0x100000000, 6); + } + + frame.payload.copy(buffer, offset); + + if (frame.masked) { + frame.maskingKey.copy(buffer, header); + Hybi.mask(buffer, frame.maskingKey, offset); + } + + this._write(buffer); + }, + + _handshakeResponse: function() { + var secKey = this._request.headers['sec-websocket-key'], + version = this._request.headers['sec-websocket-version']; + + if (version !== Hybi.VERSION) + throw new Error('Unsupported WebSocket version: ' + version); + + if (typeof secKey !== 'string') + throw new Error('Missing handshake request header: Sec-WebSocket-Key'); + + this._headers.set('Upgrade', 'websocket'); + this._headers.set('Connection', 'Upgrade'); + this._headers.set('Sec-WebSocket-Accept', Hybi.generateAccept(secKey)); + + if (this.protocol) this._headers.set('Sec-WebSocket-Protocol', this.protocol); + + var extensions = this._extensions.generateResponse(this._request.headers['sec-websocket-extensions']); + if (extensions) this._headers.set('Sec-WebSocket-Extensions', extensions); + + var start = 'HTTP/1.1 101 Switching Protocols', + headers = [start, this._headers.toString(), '']; + + return Buffer.from(headers.join('\r\n'), 'utf8'); + }, + + _shutdown: function(code, reason, error) { + delete this._frame; + delete this._message; + this._stage = 5; + + var sendCloseFrame = (this.readyState === 1); + this.readyState = 2; + + this._extensions.close(function() { + if (sendCloseFrame) this.frame(reason, 'close', code); + this.readyState = 3; + if (error) this.emit('error', new Error(reason)); + this.emit('close', new Base.CloseEvent(code, reason)); + }, this); + }, + + _fail: function(type, message) { + if (this.readyState > 1) return; + this._shutdown(this.ERRORS[type], message, true); + }, + + _parseOpcode: function(octet) { + var rsvs = [this.RSV1, this.RSV2, this.RSV3].map(function(rsv) { + return (octet & rsv) === rsv; + }); + + var frame = this._frame = new Frame(); + + frame.final = (octet & this.FIN) === this.FIN; + frame.rsv1 = rsvs[0]; + frame.rsv2 = rsvs[1]; + frame.rsv3 = rsvs[2]; + frame.opcode = (octet & this.OPCODE); + + this._stage = 1; + + if (!this._extensions.validFrameRsv(frame)) + return this._fail('protocol_error', + 'One or more reserved bits are on: reserved1 = ' + (frame.rsv1 ? 1 : 0) + + ', reserved2 = ' + (frame.rsv2 ? 1 : 0) + + ', reserved3 = ' + (frame.rsv3 ? 1 : 0)); + + if (this.OPCODE_CODES.indexOf(frame.opcode) < 0) + return this._fail('protocol_error', 'Unrecognized frame opcode: ' + frame.opcode); + + if (this.MESSAGE_OPCODES.indexOf(frame.opcode) < 0 && !frame.final) + return this._fail('protocol_error', 'Received fragmented control frame: opcode = ' + frame.opcode); + + if (this._message && this.OPENING_OPCODES.indexOf(frame.opcode) >= 0) + return this._fail('protocol_error', 'Received new data frame but previous continuous frame is unfinished'); + }, + + _parseLength: function(octet) { + var frame = this._frame; + frame.masked = (octet & this.MASK) === this.MASK; + frame.length = (octet & this.LENGTH); + + if (frame.length >= 0 && frame.length <= 125) { + this._stage = frame.masked ? 3 : 4; + if (!this._checkFrameLength()) return; + } else { + this._stage = 2; + frame.lengthBytes = (frame.length === 126 ? 2 : 8); + } + + if (this._requireMasking && !frame.masked) + return this._fail('unacceptable', 'Received unmasked frame but masking is required'); + }, + + _parseExtendedLength: function(buffer) { + var frame = this._frame; + frame.length = this._readUInt(buffer); + + this._stage = frame.masked ? 3 : 4; + + if (this.MESSAGE_OPCODES.indexOf(frame.opcode) < 0 && frame.length > 125) + return this._fail('protocol_error', 'Received control frame having too long payload: ' + frame.length); + + if (!this._checkFrameLength()) return; + }, + + _checkFrameLength: function() { + var length = this._message ? this._message.length : 0; + + if (length + this._frame.length > this._maxLength) { + this._fail('too_large', 'WebSocket frame length too large'); + return false; + } else { + return true; + } + }, + + _emitFrame: function(buffer) { + var frame = this._frame, + payload = frame.payload = Hybi.mask(buffer, frame.maskingKey), + opcode = frame.opcode, + message, + code, reason, + callbacks, callback; + + delete this._frame; + + if (opcode === this.OPCODES.continuation) { + if (!this._message) return this._fail('protocol_error', 'Received unexpected continuation frame'); + this._message.pushFrame(frame); + } + + if (opcode === this.OPCODES.text || opcode === this.OPCODES.binary) { + this._message = new Message(); + this._message.pushFrame(frame); + } + + if (frame.final && this.MESSAGE_OPCODES.indexOf(opcode) >= 0) + return this._emitMessage(this._message); + + if (opcode === this.OPCODES.close) { + code = (payload.length >= 2) ? payload.readUInt16BE(0) : null; + reason = (payload.length > 2) ? this._encode(payload.slice(2)) : null; + + if (!(payload.length === 0) && + !(code !== null && code >= this.MIN_RESERVED_ERROR && code <= this.MAX_RESERVED_ERROR) && + this.ERROR_CODES.indexOf(code) < 0) + code = this.ERRORS.protocol_error; + + if (payload.length > 125 || (payload.length > 2 && !reason)) + code = this.ERRORS.protocol_error; + + this._shutdown(code || this.DEFAULT_ERROR_CODE, reason || ''); + } + + if (opcode === this.OPCODES.ping) { + this.frame(payload, 'pong'); + this.emit('ping', new Base.PingEvent(payload.toString())) + } + + if (opcode === this.OPCODES.pong) { + callbacks = this._pingCallbacks; + message = this._encode(payload); + callback = callbacks[message]; + + delete callbacks[message]; + if (callback) callback() + + this.emit('pong', new Base.PongEvent(payload.toString())) + } + }, + + _emitMessage: function(message) { + var message = this._message; + message.read(); + + delete this._message; + + this._extensions.processIncomingMessage(message, function(error, message) { + if (error) return this._fail('extension_error', error.message); + + var payload = message.data; + if (message.opcode === this.OPCODES.text) payload = this._encode(payload); + + if (payload === null) + return this._fail('encoding_error', 'Could not decode a text frame as UTF-8'); + else + this.emit('message', new Base.MessageEvent(payload)); + }, this); + }, + + _encode: function(buffer) { + try { + var string = buffer.toString('binary', 0, buffer.length); + if (!this.UTF8_MATCH.test(string)) return null; + } catch (e) {} + return buffer.toString('utf8', 0, buffer.length); + }, + + _readUInt: function(buffer) { + if (buffer.length === 2) return buffer.readUInt16BE(0); + + return buffer.readUInt32BE(0) * 0x100000000 + + buffer.readUInt32BE(4); + } +}; + +for (var key in instance) + Hybi.prototype[key] = instance[key]; + +module.exports = Hybi; diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/hybi/frame.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/hybi/frame.js new file mode 100644 index 0000000..0fb003f --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/hybi/frame.js @@ -0,0 +1,21 @@ +'use strict'; + +var Frame = function() {}; + +var instance = { + final: false, + rsv1: false, + rsv2: false, + rsv3: false, + opcode: null, + masked: false, + maskingKey: null, + lengthBytes: 1, + length: 0, + payload: null +}; + +for (var key in instance) + Frame.prototype[key] = instance[key]; + +module.exports = Frame; diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/hybi/message.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/hybi/message.js new file mode 100644 index 0000000..e881273 --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/hybi/message.js @@ -0,0 +1,34 @@ +'use strict'; + +var Buffer = require('safe-buffer').Buffer; + +var Message = function() { + this.rsv1 = false; + this.rsv2 = false; + this.rsv3 = false; + this.opcode = null; + this.length = 0; + this._chunks = []; +}; + +var instance = { + read: function() { + return this.data = this.data || Buffer.concat(this._chunks, this.length); + }, + + pushFrame: function(frame) { + this.rsv1 = this.rsv1 || frame.rsv1; + this.rsv2 = this.rsv2 || frame.rsv2; + this.rsv3 = this.rsv3 || frame.rsv3; + + if (this.opcode === null) this.opcode = frame.opcode; + + this._chunks.push(frame.payload); + this.length += frame.length; + } +}; + +for (var key in instance) + Message.prototype[key] = instance[key]; + +module.exports = Message; diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/proxy.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/proxy.js new file mode 100644 index 0000000..2fdd32e --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/proxy.js @@ -0,0 +1,99 @@ +'use strict'; + +var Buffer = require('safe-buffer').Buffer, + Stream = require('stream').Stream, + url = require('url'), + util = require('util'), + Base = require('./base'), + Headers = require('./headers'), + HttpParser = require('../http_parser'); + +var PORTS = { 'ws:': 80, 'wss:': 443 }; + +var Proxy = function(client, origin, options) { + this._client = client; + this._http = new HttpParser('response'); + this._origin = (typeof client.url === 'object') ? client.url : url.parse(client.url); + this._url = (typeof origin === 'object') ? origin : url.parse(origin); + this._options = options || {}; + this._state = 0; + + this.readable = this.writable = true; + this._paused = false; + + this._headers = new Headers(); + this._headers.set('Host', this._origin.host); + this._headers.set('Connection', 'keep-alive'); + this._headers.set('Proxy-Connection', 'keep-alive'); + + var auth = this._url.auth && Buffer.from(this._url.auth, 'utf8').toString('base64'); + if (auth) this._headers.set('Proxy-Authorization', 'Basic ' + auth); +}; +util.inherits(Proxy, Stream); + +var instance = { + setHeader: function(name, value) { + if (this._state !== 0) return false; + this._headers.set(name, value); + return true; + }, + + start: function() { + if (this._state !== 0) return false; + this._state = 1; + + var origin = this._origin, + port = origin.port || PORTS[origin.protocol], + start = 'CONNECT ' + origin.hostname + ':' + port + ' HTTP/1.1'; + + var headers = [start, this._headers.toString(), '']; + + this.emit('data', Buffer.from(headers.join('\r\n'), 'utf8')); + return true; + }, + + pause: function() { + this._paused = true; + }, + + resume: function() { + this._paused = false; + this.emit('drain'); + }, + + write: function(chunk) { + if (!this.writable) return false; + + this._http.parse(chunk); + if (!this._http.isComplete()) return !this._paused; + + this.statusCode = this._http.statusCode; + this.headers = this._http.headers; + + if (this.statusCode === 200) { + this.emit('connect', new Base.ConnectEvent()); + } else { + var message = "Can't establish a connection to the server at " + this._origin.href; + this.emit('error', new Error(message)); + } + this.end(); + return !this._paused; + }, + + end: function(chunk) { + if (!this.writable) return; + if (chunk !== undefined) this.write(chunk); + this.readable = this.writable = false; + this.emit('close'); + this.emit('end'); + }, + + destroy: function() { + this.end(); + } +}; + +for (var key in instance) + Proxy.prototype[key] = instance[key]; + +module.exports = Proxy; diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/server.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/server.js new file mode 100644 index 0000000..dc635b0 --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/server.js @@ -0,0 +1,112 @@ +'use strict'; + +var util = require('util'), + HttpParser = require('../http_parser'), + Base = require('./base'), + Draft75 = require('./draft75'), + Draft76 = require('./draft76'), + Hybi = require('./hybi'); + +var Server = function(options) { + Base.call(this, null, null, options); + this._http = new HttpParser('request'); +}; +util.inherits(Server, Base); + +var instance = { + EVENTS: ['open', 'message', 'error', 'close', 'ping', 'pong'], + + _bindEventListeners: function() { + this.messages.on('error', function() {}); + this.on('error', function() {}); + }, + + parse: function(chunk) { + if (this._delegate) return this._delegate.parse(chunk); + + this._http.parse(chunk); + if (!this._http.isComplete()) return; + + this.method = this._http.method; + this.url = this._http.url; + this.headers = this._http.headers; + this.body = this._http.body; + + var self = this; + this._delegate = Server.http(this, this._options); + this._delegate.messages = this.messages; + this._delegate.io = this.io; + this._open(); + + this.EVENTS.forEach(function(event) { + this._delegate.on(event, function(e) { self.emit(event, e) }); + }, this); + + this.protocol = this._delegate.protocol; + this.version = this._delegate.version; + + this.parse(this._http.body); + this.emit('connect', new Base.ConnectEvent()); + }, + + _open: function() { + this.__queue.forEach(function(msg) { + this._delegate[msg[0]].apply(this._delegate, msg[1]); + }, this); + this.__queue = []; + } +}; + +['addExtension', 'setHeader', 'start', 'frame', 'text', 'binary', 'ping', 'close'].forEach(function(method) { + instance[method] = function() { + if (this._delegate) { + return this._delegate[method].apply(this._delegate, arguments); + } else { + this.__queue.push([method, arguments]); + return true; + } + }; +}); + +for (var key in instance) + Server.prototype[key] = instance[key]; + +Server.isSecureRequest = function(request) { + if (request.connection && request.connection.authorized !== undefined) return true; + if (request.socket && request.socket.secure) return true; + + var headers = request.headers; + if (!headers) return false; + if (headers['https'] === 'on') return true; + if (headers['x-forwarded-ssl'] === 'on') return true; + if (headers['x-forwarded-scheme'] === 'https') return true; + if (headers['x-forwarded-proto'] === 'https') return true; + + return false; +}; + +Server.determineUrl = function(request) { + var scheme = this.isSecureRequest(request) ? 'wss:' : 'ws:'; + return scheme + '//' + request.headers.host + request.url; +}; + +Server.http = function(request, options) { + options = options || {}; + if (options.requireMasking === undefined) options.requireMasking = true; + + var headers = request.headers, + version = headers['sec-websocket-version'], + key = headers['sec-websocket-key'], + key1 = headers['sec-websocket-key1'], + key2 = headers['sec-websocket-key2'], + url = this.determineUrl(request); + + if (version || key) + return new Hybi(request, url, options); + else if (key1 || key2) + return new Draft76(request, url, options); + else + return new Draft75(request, url, options); +}; + +module.exports = Server; diff --git a/frontend-old/node_modules/websocket-driver/lib/websocket/driver/stream_reader.js b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/stream_reader.js new file mode 100644 index 0000000..3564da8 --- /dev/null +++ b/frontend-old/node_modules/websocket-driver/lib/websocket/driver/stream_reader.js @@ -0,0 +1,69 @@ +'use strict'; + +var Buffer = require('safe-buffer').Buffer; + +var StreamReader = function() { + this._queue = []; + this._queueSize = 0; + this._offset = 0; +}; + +StreamReader.prototype.put = function(buffer) { + if (!buffer || buffer.length === 0) return; + if (!Buffer.isBuffer(buffer)) buffer = Buffer.from(buffer); + this._queue.push(buffer); + this._queueSize += buffer.length; +}; + +StreamReader.prototype.read = function(length) { + if (length > this._queueSize) return null; + if (length === 0) return Buffer.alloc(0); + + this._queueSize -= length; + + var queue = this._queue, + remain = length, + first = queue[0], + buffers, buffer; + + if (first.length >= length) { + if (first.length === length) { + return queue.shift(); + } else { + buffer = first.slice(0, length); + queue[0] = first.slice(length); + return buffer; + } + } + + for (var i = 0, n = queue.length; i < n; i++) { + if (remain < queue[i].length) break; + remain -= queue[i].length; + } + buffers = queue.splice(0, i); + + if (remain > 0 && queue.length > 0) { + buffers.push(queue[0].slice(0, remain)); + queue[0] = queue[0].slice(remain); + } + return Buffer.concat(buffers, length); +}; + +StreamReader.prototype.eachByte = function(callback, context) { + var buffer, n, index; + + while (this._queue.length > 0) { + buffer = this._queue[0]; + n = buffer.length; + + while (this._offset < n) { + index = this._offset; + this._offset += 1; + callback.call(context, buffer[index]); + } + this._offset = 0; + this._queue.shift(); + } +}; + +module.exports = StreamReader; |
