UNPKG

ssocket-js

Version:

仿 Koa 中间件控制的 WebSocket 服务的 Web 客户端程序

489 lines (488 loc) 19.9 kB
"use strict"; /* * @Author: Summer * @LastEditors: Summer * @Description: 用于传输数据的编码解压缩操作 * @LastEditTime: 2021-01-22 15:28:05 +0800 * @FilePath: \ssocket-js\src\code.ts */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.expandStatusCode = exports.StatusCode = exports.decode = exports.encode = exports.parseResponseJson = exports.parseRequestJson = exports.SocketStatus = exports.PackageType = void 0; var pako_1 = __importDefault(require("pako")); var logger_1 = __importDefault(require("./logger")); var UINT32_MAX = 0xFFFFFFFF; DataView.prototype.setUint64 = function (offset, value) { var big = ~~(value / UINT32_MAX); var low = (value % UINT32_MAX) - big; this.setUint32(offset, big); this.setUint32(offset + 4, low); }; DataView.prototype.getUint64 = function (offset) { if (offset === void 0) { offset = 0; } var buffer = new Uint8Array(this.buffer.slice(offset, offset + 8)); var hex = "0x"; buffer.forEach(function (value) { hex += Number(value).toString(16); }); return +hex; }; var logger = logger_1.default("code"); /** * 字符串转 Uint8Array * @param {*} str */ function string2buf(str) { return new TextEncoder().encode(str); } ; function buf2string(buf) { return new TextDecoder().decode(buf); } ; /**字段类型 */ var FieldType; (function (FieldType) { /**多层结构 */ FieldType["message"] = "message"; FieldType["required"] = "required"; FieldType["optional"] = "optional"; /**数组 */ FieldType["repeated"] = "repeated"; })(FieldType || (FieldType = {})); /**数据类型 */ var DataType; (function (DataType) { DataType["uint8"] = "uint8"; DataType["uint16"] = "uint16"; DataType["uint32"] = "uint32"; DataType["uint64"] = "uint64"; DataType["float"] = "float"; DataType["double"] = "double"; DataType["string"] = "string"; DataType["message"] = "message"; })(DataType || (DataType = {})); /** * */ var Protos = /** @class */ (function () { function Protos() { this.protos = {}; } Protos.prototype.parse = function (protos_config) { for (var key in protos_config) { this.protos[key] = this.parseObject(protos_config[key]); } logger("ProtosCode:parse", { protos_config: protos_config, proto: this.protos }); }; Protos.prototype.parseObject = function (obj) { var proto = {}; var nestProtos = {}; var tags = {}; for (var name_1 in obj) { var tag = obj[name_1]; var params = name_1.split(/\s+/); switch (params[0]) { case FieldType.message: if (params.length !== 2) { continue; } nestProtos[params[1]] = this.parseObject(tag); continue; case FieldType.required: case FieldType.optional: case FieldType.repeated: { // params length should be 3 and tag can't be duplicated if (params.length !== 3 || !!tags[tag]) { continue; } proto[params[2]] = { option: params[0], type: params[1], tag: tag }; tags[tag] = params[2]; } } } proto.__messages = nestProtos; proto.__tags = tags; return proto; }; Protos.prototype.writeTag = function (buffer, tag, offset) { buffer.setUint32(offset++, +tag); logger("ProtosCode:writeTag", { tag: tag, offset: offset }); return offset; }; Protos.prototype.readTag = function (buffer, offset) { var tag = buffer.getUint8(offset++); logger("ProtosCode:readTag", { offset: offset - 1, tag: tag }); return tag; }; Protos.prototype.encode = function (protos_name, data) { if (this.protos[protos_name] && data) { var buffer = new DataView(new ArrayBuffer(string2buf(JSON.stringify(data)).byteLength * 2)); var length_1 = this.write(this.protos[protos_name], data, buffer); logger("ProtosCode:encode", { protos_name: protos_name, data: data, length: length_1 }); return new Uint8Array(buffer.buffer.slice(0, length_1)); } return string2buf(data ? JSON.stringify(data) : ""); }; Protos.prototype.write = function (protos, data, buffer) { var offset = 0; if (protos) { logger("ProtosCode:write1", { data: data }); for (var name_2 in data) { if (!!protos[name_2]) { var proto = protos[name_2]; logger("ProtosCode:write2", { name: name_2, data: data[name_2], proto: proto }); switch (proto.option) { case FieldType.required: case FieldType.optional: offset = this.writeTag(buffer, proto.tag, offset); offset = this.writeBody(data[name_2], proto.type, buffer, offset, protos); break; case FieldType.repeated: offset = this.writeTag(buffer, proto.tag, offset); buffer.setUint32(offset, +data[name_2].length); offset += 4; for (var i = 0, l = data[name_2].length; i < l; i++) { offset = this.writeBody(data[name_2][i], proto.type, buffer, offset, protos); } break; } } } } logger("ProtosCode:write3", { offset: offset }); return offset; }; Protos.prototype.writeBody = function (value, type, buffer, offset, protos) { logger("ProtosCode:writeBody", { type: type, value: value, offset: offset }); switch (type) { case DataType.uint8: buffer.setUint8(offset, +value); offset += 1; break; case DataType.uint16: buffer.setUint16(offset, +value); offset += 2; break; case DataType.uint32: buffer.setUint32(offset, +value); offset += 4; break; case DataType.uint64: buffer.setUint64(offset, +value); offset += 8; break; case DataType.float: buffer.setFloat32(offset, +value); offset += 4; break; case DataType.double: buffer.setFloat64(offset, +value); offset += 8; break; case DataType.string: // Encode length var data = string2buf(value + ""); buffer.setUint32(offset, +data.byteLength); offset += 4; // write string data.forEach(function (uint8) { return buffer.setUint8(offset++, uint8); }); break; default: var message = protos.__messages[type]; if (message) { var tmpBuffer = new DataView(new ArrayBuffer(string2buf(JSON.stringify(value)).byteLength * 2)); var length = this.write(message, value, tmpBuffer); buffer.setUint32(offset, +length); offset += 4; new Uint8Array(tmpBuffer.buffer.slice(0, length)).forEach(function (uint8) { return buffer.setUint8(offset++, uint8); }); } break; } return offset; }; Protos.prototype.decode = function (protos_name, buffer) { if (this.protos[protos_name]) { var data = {}; this.read(this.protos[protos_name], data, buffer, 0); logger("ProtosCode:decode", { data: data }); return data; } return buffer.byteLength ? JSON.parse(buf2string(new Uint8Array(buffer)) || "{}") : {}; }; Protos.prototype.read = function (protos, data, _buffer, offset) { logger("ProtosCode:decode1", { offset: offset, data: data, protos: protos }); if (!!protos) { var buffer = new DataView(_buffer); while (offset < buffer.byteLength) { var tag = this.readTag(buffer, offset); offset += 1; var name_3 = protos.__tags[tag]; var proto = protos[name_3]; logger("ProtosCode:decode2", { offset: offset, tag: tag, name: name_3, proto: proto }); switch (proto.option) { case 'optional': case 'required': var body = this.readBody(buffer, proto.type, offset, protos); offset = body.offset; data[name_3] = body.value; break; case 'repeated': if (!data[name_3]) { data[name_3] = []; } var length_2 = buffer.getUint32(offset); offset += 4; for (var i = 0; i < length_2; i++) { var body_1 = this.readBody(buffer, proto.type, offset, protos); offset = body_1.offset; data[name_3].push(body_1.value); } break; } } return offset; } return 0; }; Protos.prototype.readBody = function (buffer, type, offset, protos) { var value = ""; switch (type) { case DataType.uint8: value = buffer.getUint8(offset); offset += 1; break; case DataType.uint16: value = buffer.getUint16(offset); offset += 2; break; case DataType.uint32: value = buffer.getUint32(offset); offset += 4; break; case DataType.uint64: value = buffer.getUint64(offset); offset += 8; break; case DataType.float: value = buffer.getFloat32(offset); offset += 4; break; case DataType.double: value = buffer.getFloat64(offset); offset += 8; break; case DataType.string: var length = buffer.getUint32(offset); offset += 4; value = buf2string(new Uint8Array(buffer.buffer.slice(offset, offset += length))); break; default: var message = protos.__messages[type]; if (message) { var length = buffer.getUint32(offset); offset += 4; this.read(message, value = {}, buffer.buffer.slice(offset, offset += length), 0); } break; } logger("ProtosCode:readBody", { offset: offset, type: type, value: value }); return { value: value, offset: offset }; }; return Protos; }()); var RequestProtos = new Protos(); var ResponseProtos = new Protos(); var PackageType; (function (PackageType) { /**握手 */ PackageType[PackageType["shakehands"] = 0] = "shakehands"; /**心跳 */ PackageType[PackageType["heartbeat"] = 1] = "heartbeat"; /**消息 */ PackageType[PackageType["data"] = 2] = "data"; })(PackageType = exports.PackageType || (exports.PackageType = {})); /**Socket 状态 */ var SocketStatus; (function (SocketStatus) { /**打开 */ SocketStatus[SocketStatus["OPEN"] = 0] = "OPEN"; /**正在握手 */ SocketStatus[SocketStatus["SHAKING_HANDS"] = 1] = "SHAKING_HANDS"; /**握手完毕 */ SocketStatus[SocketStatus["HANDSHAKE"] = 2] = "HANDSHAKE"; /**连接 */ SocketStatus[SocketStatus["CONNECTION"] = 3] = "CONNECTION"; /**关闭 */ SocketStatus[SocketStatus["CLOSE"] = 4] = "CLOSE"; /**重连 */ SocketStatus[SocketStatus["RECONNECTION"] = 5] = "RECONNECTION"; })(SocketStatus = exports.SocketStatus || (exports.SocketStatus = {})); /** * 配置 Protos 文件 * @param config */ function parseRequestJson(config) { RequestProtos.parse(config); } exports.parseRequestJson = parseRequestJson; /** * 配置 Protos 文件 * @param config */ function parseResponseJson(config) { ResponseProtos.parse(config); } exports.parseResponseJson = parseResponseJson; /** * 消息封包 * - +------+----------------------------------+------+ * - | head | This data exists when type == 0 | body | * - +------+------------+---------------------+------+ * - | type | id length | id | ack | * - +------+------------+---------------------+------+ * - | 1B | 4B | -- | 1B | * - +------+------------+---------------------+------+ * - +------+----------------------------------+------+ * - | head | This data exists when type == 1 | body | * - +------+----------------------------------+------+ * - | type | body length | time | * - +------+----------------------------------+------+ * - | 1B | 0B | 8B | * - +------+----------------------------------+------+ * - +------+---------------------------------------------------+------+ * - | head | This data exists when type == 2 | body | * - +------+------------+---------------+--------+-------------+------+ * - | type | request_id | path length | path | body length | body | * - +------+------------+---------------+--------+-------------+------+ * - | 1B | 4B | 4B | 4B | -- | 4B | * - +------+------------+---------------+--------+-------------+------+ * - * @param type 类型:0握手|1心跳|2数据 * @param package_data */ function encode(type, package_data) { var buffer = new DataView(new ArrayBuffer(package_data ? JSON.stringify(package_data).length * 2 : 10)); var index = 0; buffer.setUint8(index, type); index += 1; if (PackageType.data == type) { var _a = package_data || {}, _b = _a.path, path = _b === void 0 ? "" : _b, _c = _a.request_id, request_id = _c === void 0 ? 0 : _c, data = _a.data; var _data = RequestProtos.encode(path, data); if (_data.length > 128) { _data = pako_1.default.gzip(_data); } var _path = string2buf(path); buffer.setUint32(index, request_id); index += 4; buffer.setUint32(index, _path.byteLength); index += 4; _path.forEach(function (uint8) { return buffer.setUint8(index++, uint8); }); buffer.setUint32(index, _data.byteLength); index += 4; _data.forEach(function (uint8) { return buffer.setUint8(index++, uint8); }); } else if (type == PackageType.heartbeat) { buffer.setUint64(index, Date.now()); index += 8; } else if (type == PackageType.shakehands) { var _d = package_data || {}, id = _d.id, ack = _d.ack; var _id = string2buf(id); buffer.setUint32(index, _id.byteLength); index += 4; _id.forEach(function (uint8) { return buffer.setUint8(index++, uint8); }); buffer.setUint8(index, ack); index += 1; } return new Uint8Array(buffer.buffer.slice(0, index)); } exports.encode = encode; /** * 消息拆包 * - +------+----------------------------------+------+ * - | head | This data exists when type == 0 | body | * - +------+------------+---------------------+------+ * - | type | id length | id | ack | * - +------+------------+---------------------+------+ * - | 1B | 4B | -- | 1B | * - +------+------------+---------------------+------+ * - +------+----------------------------------+------+ * - | head | This data exists when type == 1 | body | * - +------+----------------------------------+------+ * - | type | body length | time | * - +------+----------------------------------+------+ * - | 1B | 0B | 8B | * - +------+----------------------------------+------+ * - +------+-------------------------------------------------------------------------------+------+ * - | head | This data exists when type == 2 | body | * - +------+------------+---------------+--------+--------+------------+-----+-------------+------+ * - | type | request_id | path length | path | status | msg length | msg | body length | body | * - +------+------------+---------------+--------+--------+------------+-----+-------------+------+ * - | 1B | 4B | 4B | -- | 4B | 4B | -- | 4B | -- | * - +------+------------+---------------+--------+--------+------------+-----+-------------+------+ * - * @param buffer */ function decode(_buffer) { if (_buffer instanceof ArrayBuffer) { var index = 0; var buffer = new DataView(_buffer); var type = buffer.getUint8(index++); if (type == PackageType.data) { var request_id = buffer.getUint32(index); index += 4; var path_length = buffer.getUint32(index); index += 4; var path = buf2string(new Uint8Array(buffer.buffer.slice(index, index += path_length))); var status_1 = buffer.getUint32(index); index += 4; var msg_length = buffer.getUint32(index); index += 4; var msg = buf2string(new Uint8Array(buffer.buffer.slice(index, index += msg_length))); var data_length = buffer.getUint32(index); index += 4; var data_buffer = data_length ? buffer.buffer.slice(index, index += data_length) : new ArrayBuffer(0); // 判断是否 GZIP 压缩的数据 if (data_buffer.byteLength > 2 && new Uint16Array(data_buffer.slice(0, 2))[0] == 0x8b1f) { data_buffer = pako_1.default.ungzip(new Uint8Array(data_buffer)); } var data = ResponseProtos.decode(path, data_buffer); return { type: type, request_id: request_id, path: path, status: status_1, msg: msg, data: data }; } else if (type == PackageType.heartbeat) { var data = buffer.getUint64(index); return { type: type, data: data }; } else if (type == PackageType.shakehands) { var id_length = buffer.getUint32(index); index += 4; var id = buf2string(new Uint8Array(buffer.buffer.slice(index, index += id_length))); var ack = buffer.getUint8(index); index += 1; return { type: type, id: id, ack: ack }; } } ; return {}; } exports.decode = decode; /**系统状态码:这个状态码会通过事件返回给前端 */ exports.StatusCode = { 4100: [4100, "client ping timeout"], 4102: [4102, "server ping timeout"], 4101: [4101, "connection close"], 200: [200, "ok"], }; /** * 扩展状态码 * @param code * @param msg */ function expandStatusCode(code, msg) { exports.StatusCode[code] = [code, msg]; } exports.expandStatusCode = expandStatusCode; exports.default = exports.StatusCode;