UNPKG

@cloudpss/ubrpc

Version:

Rpc server/client build on websocket and ubjson.

66 lines (61 loc) 2.52 kB
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); }); } }