servatrice/webclient/src/websocket/services/WebSocketService.ts
Jeremy Letto bb16ae09ef
Webatrice: fix login bugs (#4557)
* fix login after failed connection attempts, limit connection attempt time

* fix register hashed password and salt

* add feature detection and Unsupported Browser screen

* nit

Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
2022-02-04 14:07:15 -05:00

95 lines
2.6 KiB
TypeScript

import { Subject } from 'rxjs';
import { ServerStatus, StatusEnum, WebSocketConnectOptions } from 'types';
import { KeepAliveService } from './KeepAliveService';
import { WebClient } from '../WebClient';
import { SessionPersistence } from '../persistence';
export class WebSocketService {
private socket: WebSocket;
private webClient: WebClient;
private keepAliveService: KeepAliveService;
public message$: Subject<MessageEvent> = new Subject();
public statusChange$: Subject<ServerStatus> = new Subject();
private status: StatusEnum = StatusEnum.DISCONNECTED;
private keepalive: number;
constructor(webClient: WebClient) {
this.webClient = webClient;
this.keepAliveService = new KeepAliveService(this);
this.keepAliveService.disconnected$.subscribe(() => {
this.disconnect();
this.updateStatus(StatusEnum.DISCONNECTED, 'Connection timeout');
});
}
public connect(options: WebSocketConnectOptions, protocol: string = 'wss'): void {
if (window.location.hostname === 'localhost') {
protocol = 'ws';
}
const { host, port, keepalive } = options;
this.keepalive = keepalive;
this.socket = this.createWebSocket(`${protocol}://${host}:${port}`);
}
public disconnect(): void {
if (this.socket) {
this.socket.close();
}
}
public checkReadyState(state: number): boolean {
return this.socket?.readyState === state;
}
public send(message): void {
this.socket.send(message);
}
public updateStatus(status: StatusEnum, description: string): void {
this.status = status;
this.statusChange$.next({ status, description });
}
private createWebSocket(url: string): WebSocket {
const socket = new WebSocket(url);
socket.binaryType = 'arraybuffer';
const connectionTimer = setTimeout(() => socket.close(), this.keepalive);
socket.onopen = () => {
clearTimeout(connectionTimer);
this.updateStatus(StatusEnum.CONNECTED, 'Connected');
this.keepAliveService.startPingLoop(this.keepalive, (pingReceived: Function) => {
this.webClient.keepAlive(pingReceived);
});
};
socket.onclose = () => {
// dont overwrite failure messages
if (this.status !== StatusEnum.DISCONNECTED) {
this.updateStatus(StatusEnum.DISCONNECTED, 'Connection Closed');
}
this.keepAliveService.endPingLoop();
};
socket.onerror = () => {
this.updateStatus(StatusEnum.DISCONNECTED, 'Connection Failed');
SessionPersistence.connectionFailed();
};
socket.onmessage = (event: MessageEvent) => {
this.message$.next(event);
}
return socket;
}
}