summaryrefslogtreecommitdiff
path: root/protocol.js
diff options
context:
space:
mode:
Diffstat (limited to 'protocol.js')
-rw-r--r--protocol.js114
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;