const util = require("util"); module.exports = {}; module.exports.debug = true; { let id = 0; function uniqid() { return id++; } } module.exports.uniqid = uniqid; function lineReader(conn, lineCb) { let buffer = ""; conn.on("data", (data) => { buffer += data.toString(); let idx = buffer.indexOf("\n"); while (idx != -1) { lineCb(buffer.slice(0, idx)); buffer = buffer.slice(idx + 1); idx = buffer.indexOf("\n"); } }); } module.exports.lineReader = lineReader; class Message { constructor(/*...*/) { if (typeof arguments[0] == "number") this.makeReply(...arguments); else this.makeCommand(...arguments); } makeCommand(method, ...args) { this.isCommand = true; this.method = method; this.args = args; this.id = uniqid(); } makeReply(id, res, err) { this.isCommand = false; this.id = id; this.res = res; this.err = err; } toJSON() { if (this.isCommand) { return JSON.stringify({ method: this.method, args: this.args, id: this.id, }); } else { return JSON.stringify({ res: this.res, err: this.err, id: this.id, }); } } } module.exports.Message = Message; class Connection { constructor(conn, msgCb) { this.conn = conn; lineReader(conn, (line) => { console.log("Received line '" + line + "'"); let obj; try { obj = JSON.parse(line); } catch (e) { if (module.exports.debug) console.log("Invalid JSON received: " + line); return; } if (obj.method) { msgCb(new Message(obj.method, ...obj.args)); } else { const msg = new Message(obj.id, obj.res, obj.err); if (this.replyHandlers[msg.id]) { this.replyHandlers[msg.id](msg); this.replyHandlers[msg.id] = null; } else { if (module.exports.debug) console.log("Reply for unexpected message id " + msg.id); } } }); this.replyHandlers = {}; // Send a ping once in a while this.pingInterval = setInterval(async () => { const msg = await this.send(new Message("ping")); if (module.exports.debug) console.log("Ping reply: " + util.inspect(msg)); }, 600000); } send(msg) { const data = msg.toJSON(); if (module.exports.debug) console.log("Writing to conn: " + data); this.conn.write(data + "\n"); return new Promise((resolve, reject) => { this.replyHandlers[msg.id] = resolve; }); } end() { clearInterval(this.pingInterval); this.conn.end(); } } module.exports.Connection = Connection;