2020-03-21 07:47:42 +00:00
|
|
|
import { Packet } from "./packet";
|
|
|
|
import { PacketTypes, SubPacketTypes } from "./types";
|
|
|
|
|
|
|
|
export class Parser {
|
|
|
|
encode(packet: Packet): string {
|
2020-03-24 05:27:11 +00:00
|
|
|
let origin = JSON.stringify(packet)
|
2020-03-21 07:47:42 +00:00
|
|
|
// first is type
|
2020-03-24 05:27:11 +00:00
|
|
|
let str = '' + packet.type;
|
2020-03-21 07:47:42 +00:00
|
|
|
if (packet.type == PacketTypes.PONG) {
|
|
|
|
if (packet.data) { str += packet.data };
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
if (packet.sub_type != undefined) {
|
|
|
|
str += packet.sub_type;
|
|
|
|
}
|
|
|
|
// attachments if we have them
|
|
|
|
if ([SubPacketTypes.BINARY_EVENT, SubPacketTypes.BINARY_ACK].includes(packet.sub_type)) {
|
|
|
|
str += packet.attachments + '-';
|
|
|
|
}
|
|
|
|
// if we have a namespace other than `/`
|
|
|
|
// we append it followed by a comma `,`
|
|
|
|
if (packet.nsp && '/' !== packet.nsp) {
|
|
|
|
str += packet.nsp + ',';
|
|
|
|
}
|
|
|
|
// immediately followed by the id
|
|
|
|
if (null != packet.id) {
|
|
|
|
str += packet.id;
|
|
|
|
}
|
|
|
|
if (packet.sub_type == SubPacketTypes.EVENT) {
|
2020-03-24 05:27:11 +00:00
|
|
|
if (packet.name == undefined) { throw new Error(`SubPacketTypes.EVENT name can't be empty!`) }
|
2020-03-24 06:52:50 +00:00
|
|
|
packet.data = [packet.name, ...packet.data]
|
2020-03-21 07:47:42 +00:00
|
|
|
}
|
|
|
|
// json data
|
|
|
|
if (null != packet.data) {
|
2020-03-24 05:27:11 +00:00
|
|
|
let payload = this.tryStringify(packet.data);
|
2020-03-21 07:47:42 +00:00
|
|
|
if (payload !== false) {
|
|
|
|
str += payload;
|
|
|
|
} else {
|
|
|
|
return '4"encode error"'
|
|
|
|
}
|
|
|
|
}
|
2020-03-31 06:53:23 +00:00
|
|
|
console.trace(`encoded ${origin} as ${str}`);
|
2020-03-21 07:47:42 +00:00
|
|
|
return str;
|
|
|
|
}
|
2020-03-31 06:53:23 +00:00
|
|
|
tryStringify(str: any) {
|
2020-03-21 07:47:42 +00:00
|
|
|
try {
|
|
|
|
return JSON.stringify(str);
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
decode(str: string): Packet {
|
2020-03-24 05:27:11 +00:00
|
|
|
let i = 0;
|
2020-03-21 07:47:42 +00:00
|
|
|
// ignore parse binary
|
|
|
|
// if ((frame.getByte(0) == 'b' && frame.getByte(1) == '4')
|
|
|
|
// || frame.getByte(0) == 4 || frame.getByte(0) == 1) {
|
|
|
|
// return parseBinary(head, frame);
|
|
|
|
// }
|
|
|
|
// look up type
|
2020-03-24 05:27:11 +00:00
|
|
|
let p: Packet = {
|
2020-03-21 07:47:42 +00:00
|
|
|
type: Number(str.charAt(i))
|
|
|
|
};
|
|
|
|
if (null == PacketTypes[p.type]) {
|
|
|
|
return this.error('unknown packet type ' + p.type);
|
|
|
|
}
|
|
|
|
// if str empty return
|
|
|
|
if (str.length == i + 1) {
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
// if is ping packet read data and return
|
|
|
|
if (PacketTypes.PING == p.type) {
|
|
|
|
p.data = str.substr(++i);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
// look up sub type
|
|
|
|
p.sub_type = Number(str.charAt(++i));
|
|
|
|
if (null == PacketTypes[p.sub_type]) {
|
|
|
|
return this.error('unknown sub packet type ' + p.type);
|
|
|
|
}
|
|
|
|
// look up attachments if type binary
|
|
|
|
if ([SubPacketTypes.BINARY_ACK, SubPacketTypes.BINARY_EVENT].includes(p.sub_type)) {
|
2020-03-24 05:27:11 +00:00
|
|
|
let buf = '';
|
2020-03-21 07:47:42 +00:00
|
|
|
while (str.charAt(++i) !== '-') {
|
|
|
|
buf += str.charAt(i);
|
|
|
|
if (i == str.length) break;
|
|
|
|
}
|
|
|
|
if (buf != `${Number(buf)}` || str.charAt(i) !== '-') {
|
2020-03-24 06:52:50 +00:00
|
|
|
return this.error('Illegal attachments');
|
2020-03-21 07:47:42 +00:00
|
|
|
}
|
|
|
|
p.attachments = Number(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
// look up namespace (if any)
|
|
|
|
if ('/' === str.charAt(i + 1)) {
|
|
|
|
p.nsp = '';
|
|
|
|
while (++i) {
|
2020-03-24 05:27:11 +00:00
|
|
|
let c = str.charAt(i);
|
2020-03-21 07:47:42 +00:00
|
|
|
if (',' === c) break;
|
|
|
|
p.nsp += c;
|
|
|
|
if (i === str.length) break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
p.nsp = '/';
|
|
|
|
}
|
|
|
|
|
|
|
|
// look up id
|
2020-03-24 05:27:11 +00:00
|
|
|
let next = str.charAt(i + 1);
|
2020-03-24 06:52:50 +00:00
|
|
|
if ('' !== next && !isNaN(Number(next))) {
|
2020-03-24 05:27:11 +00:00
|
|
|
let id = ''
|
2020-03-21 07:47:42 +00:00
|
|
|
while (++i) {
|
2020-03-24 05:27:11 +00:00
|
|
|
let c = str.charAt(i);
|
2020-03-24 06:52:50 +00:00
|
|
|
if (null == c || isNaN(Number(c))) {
|
2020-03-21 07:47:42 +00:00
|
|
|
--i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
id += str.charAt(i);
|
|
|
|
if (i === str.length) break;
|
|
|
|
}
|
|
|
|
p.id = Number(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// ignore binary packet
|
|
|
|
if (p.sub_type == SubPacketTypes.BINARY_EVENT) {
|
|
|
|
return this.error('not support binary parse...')
|
|
|
|
}
|
|
|
|
|
|
|
|
// look up json data
|
|
|
|
if (str.charAt(++i)) {
|
2020-03-24 05:27:11 +00:00
|
|
|
let payload = this.tryParse(str.substr(i));
|
|
|
|
let isPayloadValid = payload !== false && (p.sub_type == SubPacketTypes.ERROR || Array.isArray(payload));
|
2020-03-21 07:47:42 +00:00
|
|
|
if (isPayloadValid) {
|
|
|
|
p.name = payload[0];
|
2020-03-24 06:52:50 +00:00
|
|
|
p.data = payload.slice(1);
|
2020-03-21 07:47:42 +00:00
|
|
|
} else {
|
2020-03-24 06:52:50 +00:00
|
|
|
return this.error('invalid payload ' + str.substr(i));
|
2020-03-21 07:47:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-31 06:53:23 +00:00
|
|
|
console.trace(`decoded ${str} as ${JSON.stringify(p)}`);
|
2020-03-21 07:47:42 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2020-03-31 06:53:23 +00:00
|
|
|
tryParse(str: string) {
|
2020-03-21 07:47:42 +00:00
|
|
|
try {
|
|
|
|
return JSON.parse(str);
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error(error: string): Packet {
|
|
|
|
return {
|
|
|
|
type: PacketTypes.MESSAGE,
|
|
|
|
sub_type: SubPacketTypes.ERROR,
|
|
|
|
data: 'parser error: ' + error
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|