@cloudpss/ubrpc
Version:
Rpc server/client build on websocket and ubjson.
66 lines (61 loc) • 2.52 kB
text/typescript
import { WebSocket, WebSocketStatusCode } from '@cloudpss/fetch';
import { v4 } from 'uuid';
import { waitAuth } from './auth.js';
import { RpcSocket } from './socket.js';
import type { ConnectionID, RpcMetadata } from './types/payload.js';
import type { RpcObject } from './types/utils.js';
import { send } from './utils/messaging.js';
import { VERSION } from './version.js';
import { WebSocketAppCode } from './codes.js';
import { logger } from './logger.js';
/** 由 WS Client 建立的 RPC 连接 */
export class RpcClientSocket<TRemote extends object, TLocal extends object> extends RpcSocket<TRemote, TLocal> {
/** 建立连接使用的 WebSocket 实现 */
static WebSocket = WebSocket;
constructor(url: string | URL, metadata?: RpcMetadata, local?: RpcObject<TLocal>) {
super(v4() as ConnectionID, local);
this._localMetadata = metadata;
this.url = typeof url == 'string' ? url : url.href;
this.seq = 2;
logger('[%s] client socket created. url=', this.id, this.url);
this.connect();
}
readonly url: string;
/** @inheritdoc */
protected override async authSocket(): Promise<RpcMetadata> {
logger('[%s] authenticating...', this.id);
const { socket } = this;
const seq = this.nextSeq();
send(socket, 'auth', {
seq,
id: this.id,
version: VERSION,
metadata: this.localMetadata ?? {},
});
const metadata = (await waitAuth(socket, seq, this.id))[2];
logger('[%s] authenticated. remoteMeta=%o', this.id, metadata);
return metadata;
}
/** @inheritdoc */
protected override onClose(ev: CloseEvent): void {
super.onClose(ev);
const code = ev.code as WebSocketStatusCode | WebSocketAppCode;
if (code === WebSocketStatusCode.NORMAL_CLOSURE || code === WebSocketAppCode.AUTH_ERROR) return;
logger('[%s] connect closed. reconnecting...', this.id);
setTimeout(() => {
this.connect();
}, 1000);
}
/** 建立连接 */
protected connect(): void {
this.socket = new ((this.constructor as typeof RpcClientSocket).WebSocket ?? WebSocket)(this.url);
this.socket.binaryType = 'arraybuffer';
logger('[%s] connecting...', this.id);
this.ready.catch(() => {
logger('[%s] connect failed. retrying...', this.id);
setTimeout(() => {
this.connect();
}, 2000);
});
}
}