UNPKG

@cloudpss/ubrpc

Version:

89 lines (82 loc) 3.27 kB
import { WebSocket } 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 { logger } from './logger.js'; const INIT_RECONNECT_TIMEOUT = 250; const MAX_RECONNECT_TIMEOUT = 10000; /** 由 WS Client 建立的 RPC 连接 */ export class RpcClientSocket<TRemote extends object, TLocal extends object> extends RpcSocket<TRemote, TLocal> { /** 建立连接使用的 WebSocket 实现 */ static WebSocket: typeof WebSocket | null = null; constructor(url: string | URL, metadata?: RpcMetadata, local?: RpcObject<TLocal>) { super(v4() as ConnectionID); this.#local = 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 #local: RpcObject<TLocal> | undefined; /** @inheritdoc */ protected override get local(): RpcObject<TLocal> | undefined { return this.#local; } readonly url: string; /** 等待连接成功或失败 */ get connected(): Promise<void> { return Promise.resolve(this.ready.value); } /** @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; } #reconnectTimeout = INIT_RECONNECT_TIMEOUT; /** @inheritdoc */ protected override onClose(ev: CloseEvent): void { super.onClose(ev); if (this.destroyed) return; logger('[%s] reconnecting in %dms...', this.id, this.#reconnectTimeout); setTimeout(() => { this.connect(); }, this.#reconnectTimeout); } /** 建立连接 */ protected connect(): void { const socket = new ((this.constructor as typeof RpcClientSocket).WebSocket ?? WebSocket)(this.url); socket.binaryType = 'arraybuffer'; this.socket = socket; logger('[%s] connecting...', this.id); this.connected.then( () => { logger('[%s] connected.', this.id); this.#reconnectTimeout = INIT_RECONNECT_TIMEOUT; }, () => { if (this.destroyed) return; this.resetReady(); this.#reconnectTimeout = Math.min(this.#reconnectTimeout * 2, MAX_RECONNECT_TIMEOUT); logger('[%s] connect failed. retrying in %dms...', this.id, this.#reconnectTimeout); setTimeout(() => { this.connect(); }, this.#reconnectTimeout); }, ); } }