From 5f3a4346e1da3b4e50337e5352404aae5736794b Mon Sep 17 00:00:00 2001 From: utkarsha-deriv Date: Thu, 18 Jan 2024 11:03:08 +0400 Subject: [PATCH 1/8] feat: websocket reconnection draft logic without auth --- src/configs/websocket/index.ts | 84 +++++++++++++++++-- src/contexts/auth/auth.context.tsx | 1 + src/contexts/auth/auth.provider.tsx | 3 + .../playground/playground.provider.tsx | 1 + .../RequestResponseRenderer/index.tsx | 2 +- 5 files changed, 84 insertions(+), 7 deletions(-) diff --git a/src/configs/websocket/index.ts b/src/configs/websocket/index.ts index fd1aa630f..966f6abab 100644 --- a/src/configs/websocket/index.ts +++ b/src/configs/websocket/index.ts @@ -8,6 +8,7 @@ import { } from './types'; import { Observable } from 'rxjs'; import { getIsBrowser, getServerConfig } from '@site/src/utils'; +import useAuthContext from '@site/src/hooks/useAuthContext'; export type TDerivApi = { send: (...requestData: unknown[]) => Promise; @@ -15,12 +16,17 @@ export type TDerivApi = { authorize: (requestData: AuthorizeRequest) => Promise; }; +let attempts = 10; +const RECONNECT_INTERVAL = attempts * 1000; const PING_INTERVAL = 12000; +const is_connected_before = false; +// const { updateAuthorize } = useAuthContext(); export class ApiManager { private socket: WebSocket; private derivApi: TDerivApi; private pingInterval: NodeJS.Timer; + private reconnectInterval: NodeJS.Timer; public static instance: ApiManager; public static getInstance() { @@ -31,6 +37,7 @@ export class ApiManager { } public init() { + console.log('init called'); if (!this.socket) { const { serverUrl, appId } = getServerConfig(); this.socket = new WebSocket(`wss://${serverUrl}/websockets/v3?app_id=${appId}`); @@ -62,19 +69,30 @@ export class ApiManager { if (this.pingInterval) { clearInterval(this.pingInterval); } + if (this.reconnectInterval) { + clearInterval(this.reconnectInterval); + } this.socket.addEventListener('open', () => { this.pingInterval = setInterval(() => { this.socket.send(JSON.stringify({ ping: 1 })); }, PING_INTERVAL); + // if(is_connected_before) updateAuthorize(); }); - this.socket.addEventListener('close', () => { - clearInterval(this.pingInterval); - }); - - this.socket.addEventListener('error', () => { + this.socket.onclose = () => { + // if (!is_connected_before) { + // is_connected_before = true; + // } clearInterval(this.pingInterval); - }); + this.socket = null; + if (attempts > 0) { + this.reconnectInterval = setTimeout(this.init.bind(this), RECONNECT_INTERVAL); + attempts -= 1; + } else { + window.alert('server down!!!'); + clearInterval(this.reconnectInterval); + } + }; } public reset(appId: string, url: string, registerKeepAlive = false) { @@ -85,6 +103,60 @@ export class ApiManager { } } + // connection is made and all of a sudden server is down, two things happen -> we send ping to server repeatedly and check fo response -> if response received for that no. of attempts meaning server got up if not meaning server down show msg + // fe side, we send pings to keep ws alive, but when server goes down ping gives no response which causes ws to close + // In this case, we need to check the cause of closure + // To check if reason is server down, we need to make new ws, send pings if no response recieved ws may close on its own so we send again for x amount times + // once all attempts get exhausted, we conclude that server is down and show message + // but if in one of the attempts, server send back a request - we try to reconnect (how to reconnect?) + // handles reconnection if server goes down + // 1. triggers on connection closed -> 1. reattempt to connect first check if server is up -> ping 4-5 times, 2. if server down for long time show msg, 3. if server back up re-establish websocket connection + + // trigger this function when socket is closed -> to check if server is down + public checkServerStatus() { + console.log('checkServerStatus'); + + // open new socket + this.createNewWebsocket(); + + // if WS is open, ping server for sometime, arbitary tries + if (this.socket.readyState == WebSocket.OPEN) { + console.log('ws is open'); + + while (attempts < 10) { + console.log('inside while'); + + this.reconnectInterval = setInterval(() => { + this.socket.send(JSON.stringify({ ping: 1 })); + attempts += 1; + }, 12000); + + // listen for pongs + this.socket.addEventListener('pong', () => { + console.log('server is up'); + clearInterval(this.reconnectInterval); + // break -> attempts = 4 + attempts = 4; + }); + } + } + + if (attempts < 3) { + attempts = 0; + console.log('attempts < 3'); + window.alert('server is down'); + } else { + console.log('attempts > 3'); + this.init(); + } + } + + public createNewWebsocket() { + const { serverUrl, appId } = getServerConfig(); + this.socket = new WebSocket(`wss://${serverUrl}/websockets/v3?app_id=${appId}`); + this.derivApi = new DerivAPIBasic({ connection: this.socket }); + } + set connection(newConnection: WebSocket) { this.socket = newConnection; } diff --git a/src/contexts/auth/auth.context.tsx b/src/contexts/auth/auth.context.tsx index 960ff3795..9548e2db3 100644 --- a/src/contexts/auth/auth.context.tsx +++ b/src/contexts/auth/auth.context.tsx @@ -19,6 +19,7 @@ export interface IAuthContext { updateCurrentLoginAccount: (userAccount: IUserLoginAccount) => void; userAccounts: IUserAccounts; user: IUser; + updateAuthorize; } export const AuthContext = React.createContext(null); diff --git a/src/contexts/auth/auth.provider.tsx b/src/contexts/auth/auth.provider.tsx index d36d091e3..236515dc1 100644 --- a/src/contexts/auth/auth.provider.tsx +++ b/src/contexts/auth/auth.provider.tsx @@ -17,6 +17,7 @@ type TAuthProviderProps = { if (getIsBrowser()) { apiManager.init(); + console.log('auth provider'); } const AuthProvider = ({ children }: TAuthProviderProps) => { @@ -103,6 +104,7 @@ const AuthProvider = ({ children }: TAuthProviderProps) => { updateCurrentLoginAccount, userAccounts, user, + updateAuthorize, }; }, [ currentLoginAccount, @@ -114,6 +116,7 @@ const AuthProvider = ({ children }: TAuthProviderProps) => { updateLoginAccounts, userAccounts, user, + updateAuthorize, ]); return {children}; diff --git a/src/contexts/playground/playground.provider.tsx b/src/contexts/playground/playground.provider.tsx index 8141d9889..89329388e 100644 --- a/src/contexts/playground/playground.provider.tsx +++ b/src/contexts/playground/playground.provider.tsx @@ -11,6 +11,7 @@ type TPlaygroundProviderProps = { if (getIsBrowser()) { apiManager.init(); + console.log('PlaygroundProvider'); } const PlaygroundProvider = ({ diff --git a/src/features/Apiexplorer/RequestResponseRenderer/index.tsx b/src/features/Apiexplorer/RequestResponseRenderer/index.tsx index 73988158e..798af3ba4 100644 --- a/src/features/Apiexplorer/RequestResponseRenderer/index.tsx +++ b/src/features/Apiexplorer/RequestResponseRenderer/index.tsx @@ -59,7 +59,7 @@ function RequestResponseRenderer({