Skip to content

Commit

Permalink
fix: make sure Weird will reconnect when the RPC server restarts.
Browse files Browse the repository at this point in the history
  • Loading branch information
zicklag committed Sep 4, 2024
1 parent c324dc4 commit a8b1a30
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 23 deletions.
2 changes: 2 additions & 0 deletions leaf/leaf-rpc-server/src/proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ async fn handle_client(
async fn handle_req(leaf: &LeafIroh, req: Req) -> Resp {
let kind = match req.kind {
ReqKind::Authenticate(_) => {
// TODO: we can hit this somehow when restarting the RPC server while Weird tries to
// reconnect to it and send it messages.
panic!("authenticate request should be handled outside this function")
}
ReqKind::ReadEntity(link) => read_entity(leaf, link).await,
Expand Down
57 changes: 35 additions & 22 deletions leaf/ts/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BorshSchema, borshSerialize, borshDeserialize, type Unit } from 'borsher';
import { blake3 } from '@noble/hashes/blake3';
export { BorshSchema, borshDeserialize, borshSerialize, type Unit };
import ReconnectingWebSocket from 'reconnecting-websocket';

import rawBase32encode from 'base32-encode';
import rawBase32decode from 'base32-decode';
Expand Down Expand Up @@ -378,8 +379,11 @@ export class GetComponentsResult {
}

export class RpcClient {
#ws: WebSocket;
#ws: ReconnectingWebSocket;
#auth_token: undefined | string;
#authenticated: boolean = false;
#ready: Promise<void>;
#set_websocket_ready: undefined | (() => void) = undefined;
#next_req_id: bigint = 0n;
#pending_reqs: Map<bigint, (resp: Resp) => void> = new Map();

Expand All @@ -390,6 +394,23 @@ export class RpcClient {
}

async #send_req(kind: ReqKind): Promise<Resp> {
if (this.#auth_token && !this.#authenticated) {
const resp = await this.#send_req_inner({ Authenticate: this.#auth_token });
if (resp.result && 'Ok' in resp.result) {
const respKind = resp.result.Ok;
if (!('Authenticated' in respKind)) {
throw 'Error authenticating';
}
} else {
throw `Error while authenticating: ${resp.result.Err}`;
}
this.#authenticated = true;
}

return await this.#send_req_inner(kind);
}

async #send_req_inner(kind: ReqKind): Promise<Resp> {
return new Promise(async (resolve) => {
await this.#ready;
const req: Req = {
Expand All @@ -404,33 +425,25 @@ export class RpcClient {
}

constructor(url: string, auth_token?: string) {
this.#ws = new WebSocket(url);
let auth_req: Promise<Resp> | undefined = undefined;
let websocket_ready: () => void;
this.#auth_token = auth_token;
this.#ws = new ReconnectingWebSocket(url) as any;
this.#ready = new Promise((resolve) => {
websocket_ready = resolve;
this.#set_websocket_ready = resolve;
});

if (auth_token) {
auth_req = this.#send_req({ Authenticate: auth_token });
}
this.#ws.onclose = () => {
this.#authenticated = false;
this.#ready = new Promise((resolve) => {
this.#set_websocket_ready = resolve;
});
};
this.#ws.onopen = async () => {
websocket_ready();
if (auth_req) {
const resp = await auth_req;
if (resp.result && 'Ok' in resp.result) {
const respKind = resp.result.Ok;
if (!('Authenticated' in respKind)) {
throw 'Error authenticating';
}
} else {
throw `Error while authenticating: ${resp.result.Err}`;
}
}
this.#set_websocket_ready!();
};

this.#ws.onmessage = (ev: MessageEvent) => {
const resp: Resp = borshDeserialize(RespSchema, ev.data);
this.#ws.onmessage = async (ev: MessageEvent) => {
const data = 'arrayBuffer' in ev.data ? new Uint8Array(await ev.data.arrayBuffer()) : ev.data;
const resp: Resp = borshDeserialize(RespSchema, data);
this.#pending_reqs.get(resp.id)!(resp);
this.#pending_reqs.delete(resp.id);
};
Expand Down
3 changes: 2 additions & 1 deletion leaf/ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@noble/hashes": "^1.4.0",
"base32-decode": "^1.0.0",
"base32-encode": "^1.0.0",
"borsher": "^3.5.0"
"borsher": "^3.5.0",
"reconnecting-websocket": "^4.4.0"
}
}
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit a8b1a30

Please sign in to comment.