You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.7 KiB
121 lines
3.7 KiB
/** Translates between websocket messages and Commands. */
|
|
import {ClientCommand} from "../actions/ClientAction";
|
|
import {
|
|
SERVER_GOODBYE,
|
|
SERVER_MALFORMED,
|
|
SERVER_SOCKET_STARTUP,
|
|
ServerCommand,
|
|
ServerGoodbyeAction,
|
|
ServerMalformedAction,
|
|
ServerSocketStartupAction,
|
|
SocketState
|
|
} from "../actions/ServerAction";
|
|
import {clientToPb} from "./ClientToPb";
|
|
import {ClientCommandPB} from "../proto/client";
|
|
import {Reader} from "protobufjs";
|
|
import {ServerCommandPB} from "../proto/server";
|
|
import {serverFromPb} from "./ServerFromPb";
|
|
|
|
export class WebsocketTranslator {
|
|
readonly url: string
|
|
readonly protocols: readonly string[]
|
|
readonly onStartup: (startup: ServerSocketStartupAction) => void
|
|
readonly onMessage: (command: ServerCommand) => void
|
|
readonly onError: (error: ServerMalformedAction) => void
|
|
readonly onGoodbye: (goodbye: ServerGoodbyeAction) => void
|
|
private socket: WebSocket|null
|
|
|
|
constructor({
|
|
url,
|
|
protocols,
|
|
onStartup,
|
|
onMessage,
|
|
onError,
|
|
onGoodbye,
|
|
}: {
|
|
url: string,
|
|
protocols: readonly string[],
|
|
onStartup: (startup: ServerSocketStartupAction) => void,
|
|
onMessage: (command: ServerCommand) => void,
|
|
onError: (error: ServerMalformedAction) => void,
|
|
onGoodbye: (goodbye: ServerGoodbyeAction) => void,
|
|
}) {
|
|
this.url = url
|
|
this.protocols = protocols.slice()
|
|
this.onStartup = onStartup
|
|
this.onMessage = onMessage
|
|
this.onError = onError
|
|
this.onGoodbye = onGoodbye
|
|
this.socket = null
|
|
}
|
|
|
|
connect() {
|
|
console.log("entering connect()", this)
|
|
if (this.socket !== null) {
|
|
throw Error("Already running")
|
|
}
|
|
this.socket = new WebSocket(this.url, this.protocols.slice())
|
|
this.socket.binaryType = "arraybuffer"
|
|
this.socket.addEventListener("open", () => this.handleOpen())
|
|
this.socket.addEventListener("message", (m) => this.handleMessage(m))
|
|
this.socket.addEventListener("close", (c) => this.handleClose(c))
|
|
this.socket.addEventListener("error", () => this.handleError())
|
|
this.onStartup({
|
|
type: SERVER_SOCKET_STARTUP,
|
|
state: SocketState.CONNECTING,
|
|
})
|
|
}
|
|
|
|
send(message: ClientCommand) {
|
|
this.socket?.send(ClientCommandPB.encode(clientToPb(message)).finish())
|
|
}
|
|
|
|
close(code: number, reason: string) {
|
|
this.socket?.close(code, reason)
|
|
}
|
|
|
|
private handleOpen() {
|
|
this.onStartup({
|
|
type: SERVER_SOCKET_STARTUP,
|
|
state: SocketState.OPEN,
|
|
})
|
|
}
|
|
|
|
private handleMessage(e: MessageEvent) {
|
|
const data: ArrayBuffer = e.data
|
|
const reader: Reader = Reader.create(new Uint8Array(data))
|
|
const proto = ServerCommandPB.decode(reader)
|
|
let decoded;
|
|
try {
|
|
decoded = serverFromPb((proto))
|
|
} catch (e) {
|
|
this.onError({
|
|
type: SERVER_MALFORMED,
|
|
error: e,
|
|
})
|
|
return
|
|
}
|
|
this.onMessage(decoded)
|
|
}
|
|
|
|
private handleError() {
|
|
this.onError({
|
|
type: SERVER_MALFORMED,
|
|
error: null
|
|
})
|
|
}
|
|
|
|
private handleClose(e: CloseEvent) {
|
|
this.onGoodbye({
|
|
type: SERVER_GOODBYE,
|
|
code: e.code,
|
|
reason: e.reason,
|
|
currentTime: new Date(),
|
|
})
|
|
this.clearSocket()
|
|
}
|
|
|
|
private clearSocket() {
|
|
this.socket = null
|
|
}
|
|
} |