diff --git a/elements/lisk-api-client/package.json b/elements/lisk-api-client/package.json index 6bc0734a822..1aae4f7334e 100644 --- a/elements/lisk-api-client/package.json +++ b/elements/lisk-api-client/package.json @@ -38,8 +38,10 @@ "dependencies": { "@liskhq/lisk-codec": "^0.1.0-alpha.0", "@liskhq/lisk-cryptography": "^3.0.0-alpha.0", + "isomorphic-ws": "4.0.1", "pm2-axon": "4.0.0", - "pm2-axon-rpc": "0.6.0" + "pm2-axon-rpc": "0.6.0", + "ws": "7.4.0" }, "devDependencies": { "@liskhq/lisk-chain": "^0.2.0-alpha.2", diff --git a/elements/lisk-api-client/src/create_ws_client.ts b/elements/lisk-api-client/src/create_ws_client.ts new file mode 100644 index 00000000000..82bf21f9827 --- /dev/null +++ b/elements/lisk-api-client/src/create_ws_client.ts @@ -0,0 +1,26 @@ +/* + * Copyright © 2020 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ + +import { WSClient } from './ws_client'; + +export const createWSClient = async (url: string, autoConnect = true): Promise => { + const client = new WSClient(url); + + if (autoConnect) { + await client.connect(); + } + + return client; +}; diff --git a/elements/lisk-api-client/src/index.ts b/elements/lisk-api-client/src/index.ts index d720a59d2d2..83be63047b1 100644 --- a/elements/lisk-api-client/src/index.ts +++ b/elements/lisk-api-client/src/index.ts @@ -12,6 +12,7 @@ * Removal or modification of this copyright notice is prohibited. * */ -export { createWSClient } from './ws_client'; + export { createClient } from './client'; export { createAPIClient } from './create_api_client'; +export { createWSClient } from './create_ws_client'; diff --git a/elements/lisk-api-client/src/ws_client.ts b/elements/lisk-api-client/src/ws_client.ts index f6986cd7be7..cf347cdc9af 100644 --- a/elements/lisk-api-client/src/ws_client.ts +++ b/elements/lisk-api-client/src/ws_client.ts @@ -13,15 +13,65 @@ * */ -import { APIClient } from './api_client'; -import { Channel } from './types'; - -export const createWSClient = async (url: string): Promise => { - // FIXME: requires real implementation - const channel = ({ url } as unknown) as Channel; - await channel.connect(); - const client = new APIClient(channel); - await client.init(); - - return client; -}; +import * as WebSocket from 'isomorphic-ws'; + +const CONNECTION_TIMEOUT = 2000; +const ACKNOWLEDGMENT_TIMEOUT = 2000; + +const timeout = async (ms: number, message?: string): Promise => + new Promise((_, reject) => { + const id = setTimeout(() => { + clearTimeout(id); + reject(message ?? `Timed out in ${ms}ms.`); + }, ms); + }); + +export class WSClient { + public isAlive = false; + private readonly _url: string; + private _ws?: WebSocket; + + public constructor(url: string) { + this._url = url; + } + + public async connect(): Promise { + this._ws = new WebSocket(this._url); + + const connect = new Promise(resolve => { + this._ws?.on('open', () => { + this.isAlive = true; + resolve(); + }); + }); + + this._ws.on('ping', () => { + this.isAlive = true; + }); + + return Promise.race([ + connect, + timeout(CONNECTION_TIMEOUT, `Could not connect in ${CONNECTION_TIMEOUT}ms`), + ]); + } + + public async disconnect(): Promise { + if (!this._ws) { + return Promise.resolve(); + } + + return new Promise(resolve => { + this._ws?.on('close', () => { + this.isAlive = false; + this._ws = undefined; + resolve(); + }); + this._ws?.close(); + }); + } + + // public async invoke(actionName: string, params?: Record): Promise { + // } + + // public subscribe(eventName: string, cb: EventCallback): void {} +} diff --git a/yarn.lock b/yarn.lock index 8d3b674d255..88cde533772 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6807,6 +6807,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isomorphic-ws@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -12258,6 +12263,11 @@ ws@7.3.1: resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== +ws@7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7" + integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== + ws@^7.2.3: version "7.3.0" resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.0.tgz#4b2f7f219b3d3737bc1a2fbf145d825b94d38ffd"