summaryrefslogtreecommitdiff
path: root/websocket_client.js
diff options
context:
space:
mode:
Diffstat (limited to 'websocket_client.js')
-rw-r--r--websocket_client.js110
1 files changed, 110 insertions, 0 deletions
diff --git a/websocket_client.js b/websocket_client.js
new file mode 100644
index 0000000..3cfd1c7
--- /dev/null
+++ b/websocket_client.js
@@ -0,0 +1,110 @@
+// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info>
+// SPDX-License-Identifier: GPL-3.0-or-later
+const AUTO_RECONNECT_INTERVAL = 5000;
+export class WuiWebSocketClient {
+ constructor(opts) {
+ this.opts = opts;
+ this.requestQueue = [];
+ this.reconnect_id = 0;
+ this.isOpen = false;
+ this.error = "";
+ this.address = opts.address;
+ if (opts.auto_reconnect) {
+ if (opts.auto_reconnect_interval <= 0) {
+ opts.auto_reconnect_interval = AUTO_RECONNECT_INTERVAL;
+ }
+ }
+ this.connect();
+ }
+ //
+ // Send the request and wait for response similar to HTTP
+ // request-response.
+ //
+ async Send(req) {
+ return new Promise((resolve) => {
+ const wuiRes = {
+ code: 0,
+ message: "",
+ };
+ const reqQueue = {
+ req: req,
+ cbSuccess: (res) => {
+ wuiRes.code = res.code;
+ wuiRes.message = res.message;
+ if (res.code === 200 && res.body.length > 0) {
+ wuiRes.data = JSON.parse(atob(res.body));
+ }
+ resolve(wuiRes);
+ },
+ cbFail: (err) => {
+ wuiRes.code = 500;
+ wuiRes.message = err;
+ resolve(wuiRes);
+ },
+ };
+ this.requestQueue.push(reqQueue);
+ this.conn.send(JSON.stringify(req));
+ });
+ }
+ connect() {
+ this.conn = new WebSocket(this.address);
+ this.conn.onclose = () => {
+ this.onClose();
+ };
+ this.conn.onerror = () => {
+ this.onError();
+ };
+ this.conn.onmessage = (ev) => {
+ this.onMessage(ev);
+ };
+ this.conn.onopen = () => {
+ this.onOpen();
+ };
+ }
+ // onClose handle connection closed by cleaning up the request
+ // queue.
+ onClose() {
+ this.requestQueue.forEach((reqq) => {
+ reqq.cbFail("connection closed");
+ });
+ this.isOpen = false;
+ this.error = "connection is closed by server";
+ if (this.opts.auto_reconnect && !this.reconnect_id) {
+ this.reconnect_id = setInterval(() => {
+ this.connect();
+ }, this.opts.auto_reconnect_interval);
+ }
+ if (this.opts.onDisconnected) {
+ this.opts.onDisconnected();
+ }
+ }
+ onError() {
+ if (this.opts.onError) {
+ this.opts.onError();
+ }
+ }
+ onMessage(ev) {
+ const res = JSON.parse(ev.data);
+ this.requestQueue.forEach((reqq, x) => {
+ if (reqq.req.id === res.id) {
+ reqq.cbSuccess(res);
+ this.requestQueue.splice(x, 1);
+ return;
+ }
+ });
+ if (this.opts.onBroadcast && res.id == 0) {
+ this.opts.onBroadcast(res);
+ }
+ }
+ onOpen() {
+ this.isOpen = true;
+ this.error = "";
+ if (this.reconnect_id) {
+ clearInterval(this.reconnect_id);
+ this.reconnect_id = 0;
+ }
+ if (this.opts.onConnected) {
+ this.opts.onConnected();
+ }
+ }
+}