aboutsummaryrefslogtreecommitdiff
path: root/websocket_client.ts
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2021-09-27 01:37:03 +0700
committerShulhan <ms@kilabit.info>2021-09-27 01:38:23 +0700
commita3e4a44ba024f478192a807b94e858d523ad4599 (patch)
tree5d64f7f4574d59e1f57fbd0f86755f0e034a9eac /websocket_client.ts
parent665bda4fe5ccf0e12b74262309da39695dd69317 (diff)
downloadpakakeh.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.ts167
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()
+ }
+ }
+}