diff options
Diffstat (limited to 'protocol.js')
-rw-r--r-- | protocol.js | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/protocol.js b/protocol.js new file mode 100644 index 0000000..a1be542 --- /dev/null +++ b/protocol.js @@ -0,0 +1,114 @@ +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) => { + let obj; + try { + obj = JSON.parse(line); + } catch (e) { + if (module.exports.debug) console.log("Invalid JSON received: " + line); + return; + } + if (obj.res || obj.err) { + 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); + } + } else { + msgCb(new Message(obj.method, ...obj.args)); + } + }); + + 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)); + }, 30000); + } + + 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; |