Distributed Concurrent Editor

websocket.ts at [ffc3afbe0e]
Login

File http/browser/src/websocket.ts artifact 0da28db1d1 part of check-in ffc3afbe0e


export function documentNameToURL(documentName: string): string {
    const location = document.location;
    const protocol = location.protocol === "https:" ? "wss:" : "ws:";
    return `${protocol}//${location.host}/documents/${documentName}`;
}

export class WebSocketManager {
    private currentUrl: string = "";
    private sock?: WebSocket;

    onopen: (url: string, socket: WebSocket) => void = (url, sock) => { };
    onclose: (url: string) => void = (url) => { };
    ondata: (url: string, socket: WebSocket, msg: MessageEvent) => void = (url, sock, msg) => { };

    connect = (url: string): boolean => {
        if (url === this.currentUrl) {
            return false;
        }
        this.close();
        this.currentUrl = url;
        this.reconnect(url);
        return true;
    }

    close = (): void => {
        this.sock?.close();
        this.sock = undefined;
    }

    reconnect = (url: string): void => {
        if (url !== this.currentUrl) {
            return;
        }
        const sock = new WebSocket(url);
        this.sock = sock;

        sock.onopen = (event: Event) => {
            if (sock !== this.sock) {
                sock.close();
                return;
            }
            this.onopen(url, sock);
        };

        sock.onclose = (event: Event) => {
            if (sock !== this.sock) {
                return;
            }
            this.sock = undefined;
            this.onclose(url);
            setTimeout(() => {
                if (this.sock !== undefined) {
                    return;
                }
                this.reconnect(url);
            }, 1000);
        };

        sock.onmessage = (event: MessageEvent) => {
            if (sock !== this.sock) {
                sock.close();
                return;
            }
            this.ondata(url, sock, event);
        };
    }

    send = (msg: ArrayBufferView): void => {
        const sock = this.sock;
        if (sock && sock.readyState === WebSocket.OPEN) {
            sock.send(msg);
        }
    }
}