1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00
lens/src/renderer/api/websocket-api.ts
Sebastian Malton 7451869c25
add no-unused and react/recommended to eslint (#1523)
* add no-unused-vars and no-unused-imports

* added quotes: double, and remove ignore pattern

* move itif and describeif into utils

Signed-off-by: Sebastian Malton <sebastian@malton.name>
2020-11-27 08:48:38 -05:00

170 lines
4.5 KiB
TypeScript

import { observable } from "mobx";
import { EventEmitter } from "../../common/event-emitter";
interface IParams {
url?: string; // connection url, starts with ws:// or wss://
autoConnect?: boolean; // auto-connect in constructor
flushOnOpen?: boolean; // flush pending commands on open socket
reconnectDelaySeconds?: number; // reconnect timeout in case of error (0 - don't reconnect)
pingIntervalSeconds?: number; // send ping message for keeping connection alive in some env, e.g. AWS (0 - don't ping)
logging?: boolean; // show logs in console
}
interface IMessage {
id: string;
data: string;
}
export enum WebSocketApiState {
PENDING = -1,
OPEN,
CONNECTING,
RECONNECTING,
CLOSED,
}
export class WebSocketApi {
protected socket: WebSocket;
protected pendingCommands: IMessage[] = [];
protected reconnectTimer: any;
protected pingTimer: any;
protected pingMessage = "PING";
@observable readyState = WebSocketApiState.PENDING;
public onOpen = new EventEmitter<[]>();
public onData = new EventEmitter<[string]>();
public onClose = new EventEmitter<[]>();
static defaultParams: Partial<IParams> = {
autoConnect: true,
logging: false,
reconnectDelaySeconds: 10,
pingIntervalSeconds: 0,
flushOnOpen: true,
};
constructor(protected params: IParams) {
this.params = Object.assign({}, WebSocketApi.defaultParams, params);
const { autoConnect, pingIntervalSeconds } = this.params;
if (autoConnect) {
setTimeout(() => this.connect());
}
if (pingIntervalSeconds) {
this.pingTimer = setInterval(() => this.ping(), pingIntervalSeconds * 1000);
}
}
get isConnected() {
const state = this.socket ? this.socket.readyState : -1;
return state === WebSocket.OPEN && this.isOnline;
}
get isOnline() {
return navigator.onLine;
}
setParams(params: Partial<IParams>) {
Object.assign(this.params, params);
}
connect(url = this.params.url) {
if (this.socket) {
this.socket.close(); // close previous connection first
}
this.socket = new WebSocket(url);
this.socket.onopen = this._onOpen.bind(this);
this.socket.onmessage = this._onMessage.bind(this);
this.socket.onerror = this._onError.bind(this);
this.socket.onclose = this._onClose.bind(this);
this.readyState = WebSocketApiState.CONNECTING;
}
ping() {
if (!this.isConnected) return;
this.send(this.pingMessage);
}
reconnect() {
const { reconnectDelaySeconds } = this.params;
if (!reconnectDelaySeconds) return;
this.writeLog("reconnect after", reconnectDelaySeconds + "ms");
this.reconnectTimer = setTimeout(() => this.connect(), reconnectDelaySeconds * 1000);
this.readyState = WebSocketApiState.RECONNECTING;
}
destroy() {
if (!this.socket) return;
this.socket.close();
this.socket = null;
this.pendingCommands = [];
this.removeAllListeners();
clearTimeout(this.reconnectTimer);
clearInterval(this.pingTimer);
this.readyState = WebSocketApiState.PENDING;
}
removeAllListeners() {
this.onOpen.removeAllListeners();
this.onData.removeAllListeners();
this.onClose.removeAllListeners();
}
send(command: string) {
const msg: IMessage = {
id: (Math.random() * Date.now()).toString(16).replace(".", ""),
data: command,
};
if (this.isConnected) {
this.socket.send(msg.data);
}
else {
this.pendingCommands.push(msg);
}
}
protected flush() {
this.pendingCommands.forEach(msg => this.send(msg.data));
this.pendingCommands.length = 0;
}
protected parseMessage(data: string) {
return data;
}
protected _onOpen(evt: Event) {
this.onOpen.emit();
if (this.params.flushOnOpen) this.flush();
this.readyState = WebSocketApiState.OPEN;
this.writeLog("%cOPEN", "color:green;font-weight:bold;", evt);
}
protected _onMessage(evt: MessageEvent) {
const data = this.parseMessage(evt.data);
this.onData.emit(data);
this.writeLog("%cMESSAGE", "color:black;font-weight:bold;", data);
}
protected _onError(evt: Event) {
this.writeLog("%cERROR", "color:red;font-weight:bold;", evt);
}
protected _onClose(evt: CloseEvent) {
const error = evt.code !== 1000 || !evt.wasClean;
if (error) {
this.reconnect();
}
else {
this.readyState = WebSocketApiState.CLOSED;
this.onClose.emit();
}
this.writeLog("%cCLOSE", `color:${error ? "red" : "black"};font-weight:bold;`, evt);
}
protected writeLog(...data: any[]) {
if (this.params.logging) {
console.log(...data);
}
}
}