diff options
| author | Shulhan <ms@kilabit.info> | 2021-09-27 01:37:03 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2021-09-27 01:38:23 +0700 |
| commit | a3e4a44ba024f478192a807b94e858d523ad4599 (patch) | |
| tree | 5d64f7f4574d59e1f57fbd0f86755f0e034a9eac /websocket_client.ts | |
| parent | 665bda4fe5ccf0e12b74262309da39695dd69317 (diff) | |
| download | pakakeh.ts-a3e4a44ba024f478192a807b94e858d523ad4599.tar.xz | |
all: implement WebSocket client
The WebSocket client have only one method "Send" that send request
to the server based on predefined format WuiWebSocketRequest in
synchronous way, which means it will wait for the response and pass it
back to the caller based on the request ID.
Diffstat (limited to 'websocket_client.ts')
| -rw-r--r-- | websocket_client.ts | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/websocket_client.ts b/websocket_client.ts new file mode 100644 index 0000000..62d11fc --- /dev/null +++ b/websocket_client.ts @@ -0,0 +1,167 @@ +import { WuiResponseInterface } from "./response.js" + +const AUTO_RECONNECT_INTERVAL = 5000 + +interface RequestQueue { + req: WuiWebSocketRequest + cbSuccess: (res: WuiWebSocketResponse) => void + cbFail: (err: string) => void +} + +export interface WuiWebSocketOptions { + address: string + insecure: boolean // If true the client will connect without SSL. + auto_reconnect: boolean // If true the client will handle auto-reconnect. + auto_reconnect_interval: number // The interval for auto-reconnect, default to 5 seconds. + onBroadcast: (res: WuiWebSocketResponse) => void + onConnected: () => void + onDisconnected: () => void + onError: () => void +} + +export interface WuiWebSocketRequest { + id: number + method: string + target: string + body?: string +} + +export interface WuiWebSocketResponse { + id: number + code: number + message: string + body: string +} + +export class WuiWebSocketClient { + address: string + conn!: WebSocket + requestQueue: RequestQueue[] = [] + reconnect_id: number = 0 + isOpen: boolean = false + error: string = "" + + constructor(public opts: WuiWebSocketOptions) { + if (opts.insecure) { + this.address = "ws://" + opts.address + } else { + this.address = "wss://" + 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: WuiWebSocketRequest): Promise<WuiResponseInterface> { + return new Promise((resolve, reject) => { + let wuiRes: WuiResponseInterface = { + code: 0, + message: "", + } + let reqQueue: RequestQueue = { + req: req, + cbSuccess: (res: WuiWebSocketResponse) => { + 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: string) => { + 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 = (ev: CloseEvent) => { + this.onClose(ev) + } + this.conn.onerror = (ev: Event) => { + this.onError(ev) + } + this.conn.onmessage = (ev: MessageEvent) => { + this.onMessage(ev) + } + this.conn.onopen = (ev: Event) => { + this.onOpen(ev) + } + } + + // onClose handle connection closed by cleaning up the request + // queue. + onClose(ev: CloseEvent) { + for (let x = 0; x < this.requestQueue.length; x++) { + this.requestQueue[x].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(ev: Event) { + if (this.opts.onError) { + this.opts.onError() + } + } + + onMessage(ev: MessageEvent) { + let res: WuiWebSocketResponse = JSON.parse(ev.data) + + for (let x = 0; x < this.requestQueue.length; x++) { + let reqq = this.requestQueue[x] + if (reqq.req.id === res.id) { + reqq.cbSuccess(res) + this.requestQueue.splice(x, 1) + return + } + } + + if (this.opts.onBroadcast) { + this.opts.onBroadcast(res) + } + } + + onOpen(ev: Event) { + this.isOpen = true + this.error = "" + + if (this.reconnect_id) { + clearInterval(this.reconnect_id) + this.reconnect_id = 0 + } + if (this.opts.onConnected) { + this.opts.onConnected() + } + } +} |
