Files
pythagoras/client/pythagoras_client.ts

81 lines
2.1 KiB
TypeScript

import { uint_bytes_to_num, utf8_decode } from "./tools";
import { WSClient } from "./ws";
enum PythagorasIncomingMessageType {
SubUpdateCur,
SubFinishCur,
SelectedMessage,
};
type PythagorasIncomingMessage = (
{
type: PythagorasIncomingMessageType.SubFinishCur | PythagorasIncomingMessageType.SubUpdateCur,
text: string,
} |
{
type: PythagorasIncomingMessageType.SelectedMessage,
message: string,
}
);
export class PythagorasClient {
private sock: WSClient | null;
private addr: string;
private buf: Uint8Array;
private static max_recv_retry: number = 10;
public constructor(addr: string) {
this.sock = null;
this.addr = addr;
this.buf = new Uint8Array(0);
}
private async reconnect(): Promise<void> {
this.sock = new WSClient(this.addr);
await this.sock.wait_for_connection();
}
/**
* Force receive from the underlying `WSClient`.
* This will try to receive (or reconnect) up to `PythagorasClient.max_recv_retry` times.
*/
private async recv_inner(): Promise<Blob> {
if (this.sock === null) { await this.reconnect(); }
for (let i = 0; i < PythagorasClient.max_recv_retry; ++i) {
const received = await this.sock!.receive();
if (received !== null) { return received; }
await this.reconnect();
}
throw new Error("max reconnection attempts reached");
}
/**
* Receive `target` bytes from the underlying stream (or leftovers from previous reads).
*/
private async recv_length(target: number): Promise<Uint8Array> {
if (target == 0) { return new Uint8Array(0); }
while (this.buf.length < target) {
const received = await (await this.recv_inner()).bytes();
const merged = new Uint8Array(this.buf.length + received.length);
merged.set(this.buf);
merged.set(received, this.buf.length);
this.buf = merged;
}
const total = this.buf;
this.buf = new Uint8Array(total.length - target);
this.buf.set(total.slice(target));
return total.slice(0, target);
}
public async recv(): Promise<PythagorasIncomingMessage> {
const advertised_length = uint_bytes_to_num(await this.recv_length(4));
const payload = utf8_decode(await this.recv_length(advertised_length));
return JSON.parse(payload);
}
}