2022-01-18 02:02:39 +08:00
|
|
|
import * as message from "./message.js";
|
|
|
|
import * as rendezvous from "./rendezvous.js";
|
2022-01-19 00:57:57 +08:00
|
|
|
import * as sha256 from "fast-sha256";
|
2022-01-18 02:02:39 +08:00
|
|
|
|
|
|
|
type Keys = "message" | "open" | "close" | "error";
|
2022-01-17 18:11:14 +08:00
|
|
|
|
|
|
|
export default class Websock {
|
2022-01-18 02:02:39 +08:00
|
|
|
_websocket: WebSocket;
|
|
|
|
_eventHandlers: { [key in Keys]: Function };
|
2022-01-19 22:26:23 +08:00
|
|
|
_buf: Uint8Array[];
|
|
|
|
_status: any;
|
2022-01-20 01:00:35 +08:00
|
|
|
_latency: number;
|
2022-01-17 18:11:14 +08:00
|
|
|
|
2022-01-19 22:26:23 +08:00
|
|
|
constructor(uri: string) {
|
|
|
|
this._eventHandlers = {
|
|
|
|
message: (_: any) => {},
|
|
|
|
open: () => {},
|
|
|
|
close: () => {},
|
|
|
|
error: () => {},
|
|
|
|
};
|
|
|
|
this._status = "";
|
|
|
|
this._buf = [];
|
|
|
|
this._websocket = new WebSocket(uri);
|
|
|
|
this._websocket.onmessage = this._recv_message.bind(this);
|
|
|
|
this._websocket.binaryType = "arraybuffer";
|
2022-01-20 01:00:35 +08:00
|
|
|
this._latency = new Date().getTime();
|
|
|
|
}
|
|
|
|
|
|
|
|
latency(): number {
|
|
|
|
return this._latency;
|
2022-01-19 22:26:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
sendMessage(data: any) {
|
|
|
|
this._websocket.send(
|
|
|
|
message.Message.encode(message.Message.fromJSON(data)).finish()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
sendRendezvous(data: any) {
|
|
|
|
this._websocket.send(
|
|
|
|
rendezvous.RendezvousMessage.encode(
|
|
|
|
rendezvous.RendezvousMessage.fromJSON(data)
|
|
|
|
).finish()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
parseMessage(data: Uint8Array) {
|
|
|
|
return message.Message.decode(data);
|
2022-01-18 02:02:39 +08:00
|
|
|
}
|
2022-01-17 18:11:14 +08:00
|
|
|
|
2022-01-19 22:26:23 +08:00
|
|
|
parseRendezvous(data: Uint8Array) {
|
|
|
|
return rendezvous.RendezvousMessage.decode(data);
|
2022-01-18 02:02:39 +08:00
|
|
|
}
|
2022-01-17 18:11:14 +08:00
|
|
|
|
2022-01-18 02:02:39 +08:00
|
|
|
// Event Handlers
|
|
|
|
off(evt: Keys) {
|
2022-01-19 22:26:23 +08:00
|
|
|
this._eventHandlers[evt] = () => {};
|
2022-01-18 02:02:39 +08:00
|
|
|
}
|
2022-01-17 18:11:14 +08:00
|
|
|
|
2022-01-18 02:02:39 +08:00
|
|
|
on(evt: Keys, handler: Function) {
|
|
|
|
this._eventHandlers[evt] = handler;
|
|
|
|
}
|
2022-01-17 18:11:14 +08:00
|
|
|
|
2022-01-19 22:26:23 +08:00
|
|
|
async open(timeout: number = 12000): Promise<Websock> {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
setTimeout(() => {
|
|
|
|
if (this._status != "open") {
|
|
|
|
reject(this._status || "timeout");
|
|
|
|
}
|
|
|
|
}, timeout);
|
|
|
|
this._websocket.onopen = () => {
|
2022-01-20 01:00:35 +08:00
|
|
|
this._latency = new Date().getTime() - this._latency;
|
2022-01-19 22:26:23 +08:00
|
|
|
this._status = "open";
|
|
|
|
console.debug(">> WebSock.onopen");
|
|
|
|
if (this._websocket?.protocol) {
|
|
|
|
console.info(
|
|
|
|
"Server choose sub-protocol: " + this._websocket.protocol
|
|
|
|
);
|
|
|
|
}
|
2022-01-17 18:11:14 +08:00
|
|
|
|
2022-01-19 22:26:23 +08:00
|
|
|
this._eventHandlers.open();
|
2022-01-20 01:00:35 +08:00
|
|
|
console.info("WebSock.onopen");
|
2022-01-19 22:26:23 +08:00
|
|
|
resolve(this);
|
|
|
|
};
|
|
|
|
this._websocket.onclose = (e) => {
|
|
|
|
this._status = e;
|
2022-01-20 01:00:35 +08:00
|
|
|
console.error("WebSock.onclose: " + e);
|
2022-01-19 22:26:23 +08:00
|
|
|
this._eventHandlers.close(e);
|
|
|
|
reject(e);
|
|
|
|
};
|
|
|
|
this._websocket.onerror = (e) => {
|
|
|
|
this._status = e;
|
2022-01-20 01:00:35 +08:00
|
|
|
console.error("WebSock.onerror: " + e);
|
2022-01-19 22:26:23 +08:00
|
|
|
this._eventHandlers.error(e);
|
|
|
|
reject(e);
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
2022-01-17 18:11:14 +08:00
|
|
|
|
2022-01-19 22:26:23 +08:00
|
|
|
async next(timeout = 12000): Promise<Uint8Array> {
|
2022-01-20 01:00:35 +08:00
|
|
|
const func = (
|
2022-01-19 22:26:23 +08:00
|
|
|
resolve: (value: Uint8Array) => void,
|
|
|
|
reject: (reason: any) => void,
|
|
|
|
tm0: number
|
|
|
|
) => {
|
|
|
|
if (this._buf.length) {
|
|
|
|
resolve(this._buf[0]);
|
|
|
|
this._buf.splice(0, 1);
|
|
|
|
} else {
|
2022-01-20 01:00:35 +08:00
|
|
|
if (this._status != "open") {
|
2022-01-19 22:26:23 +08:00
|
|
|
reject(this._status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (new Date().getTime() > tm0 + timeout) {
|
|
|
|
reject("timeout");
|
|
|
|
} else {
|
|
|
|
setTimeout(() => func(resolve, reject, tm0), 1);
|
|
|
|
}
|
2022-01-18 02:02:39 +08:00
|
|
|
}
|
|
|
|
};
|
2022-01-19 22:26:23 +08:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
func(resolve, reject, new Date().getTime());
|
|
|
|
});
|
2022-01-18 02:02:39 +08:00
|
|
|
}
|
2022-01-17 18:11:14 +08:00
|
|
|
|
2022-01-18 02:02:39 +08:00
|
|
|
close() {
|
|
|
|
if (this._websocket) {
|
|
|
|
if (
|
|
|
|
this._websocket.readyState === WebSocket.OPEN ||
|
|
|
|
this._websocket.readyState === WebSocket.CONNECTING
|
|
|
|
) {
|
|
|
|
console.info("Closing WebSocket connection");
|
|
|
|
this._websocket.close();
|
|
|
|
}
|
2022-01-17 18:11:14 +08:00
|
|
|
|
2022-01-19 22:26:23 +08:00
|
|
|
this._websocket.onmessage = () => {};
|
2022-01-17 18:11:14 +08:00
|
|
|
}
|
2022-01-18 02:02:39 +08:00
|
|
|
}
|
2022-01-17 18:11:14 +08:00
|
|
|
|
2022-01-18 02:02:39 +08:00
|
|
|
_recv_message(e: any) {
|
|
|
|
if (e.data instanceof window.ArrayBuffer) {
|
2022-01-20 01:00:35 +08:00
|
|
|
const bytes = new Uint8Array(e.data);
|
2022-01-19 22:26:23 +08:00
|
|
|
this._buf.push(bytes);
|
2022-01-17 18:11:14 +08:00
|
|
|
}
|
2022-01-19 19:19:29 +08:00
|
|
|
this._eventHandlers.message(e.data);
|
2022-01-18 02:02:39 +08:00
|
|
|
}
|
2022-01-19 00:57:57 +08:00
|
|
|
|
|
|
|
hash(datas: [Uint8Array]): Uint8Array {
|
|
|
|
const hasher = new sha256.Hash();
|
|
|
|
datas.forEach((data) => hasher.update(data));
|
|
|
|
return hasher.digest();
|
|
|
|
}
|
2022-01-18 02:02:39 +08:00
|
|
|
}
|