UNPKG

@cloudpss/ubrpc

Version:

Rpc server/client build on websocket and ubjson.

148 lines 4.89 kB
import { waitAuth } from './auth.js'; import { WebSocketAppCode } from './codes.js'; import { logger } from './logger.js'; import { RpcSocket } from './socket.js'; import { send } from './utils/messaging.js'; import { serializeError } from './utils/serialize.js'; import { VERSION } from './version.js'; const kOnClose = Symbol('kOnClose'); const kMetadata = Symbol('kMetadata'); const kReplaceSocket = Symbol('kReplaceSocket'); let tempIdGen = 0; /** 由 WS Server 建立的 RPC 连接组 */ export class RpcServer { authenticator; constructor( /** 服务端 RPC 实现 */ local, /** 客户端认证方法 */ authenticator = () => ({})) { this.authenticator = authenticator; if (local == null) { this.local = undefined; } else if (typeof local == 'function') { this.local = local; } else { this.local = () => local; } } /** 用于响应调用的本地对象 */ local; /** 已建立连接的客户端 */ _sockets = new Map(); /** 已建立连接的客户端 */ get sockets() { return this._sockets; } /** WebSocket 连接后调用此方法建立 RPC 连接 */ async connect(socket) { const tempId = `#${++tempIdGen}`; logger('[%s] incoming connection', tempId); socket.binaryType = 'arraybuffer'; const s = socket; const [id, metadata] = await this.authSocket(socket, tempId); s[kMetadata] = metadata; let client = this._sockets.get(id); if (!client) { logger('[%s] new connection created', id); client = new RpcServerSocket(id, this); this._sockets.set(id, client); } else { logger('[%s] connection resumed', id); const tid = this.disconnectingSockets.get(id); if (tid != null) { this.disconnectingSockets.delete(id); clearTimeout(tid); } } await client[kReplaceSocket](s); return client; } /** 认证客户端 */ async authSocket(socket, tempId) { let seq, id, remoteMetadata; try { logger('[%s] authenticating...', tempId); [seq, id, remoteMetadata] = await waitAuth(socket); logger('[%s -> %s] got server auth. remoteMeta=%o', tempId, id, remoteMetadata); const localMetadata = await this.authenticator(remoteMetadata); logger('[%s] server auth success. localMeta=%o', id, localMetadata); send(socket, 'auth', { seq, id, version: VERSION, metadata: localMetadata, }); return [id, remoteMetadata]; } catch (ex) { logger('[%s] server auth failed. remoteMeta=%o, error=%o', id ?? tempId, remoteMetadata, ex); if (seq != null && id != null) { send(socket, 'auth', { seq, id, version: VERSION, metadata: {}, error: serializeError(ex), }); } else { send(socket, 'error', { seq: 1, error: serializeError(ex), }); } socket.close(WebSocketAppCode.AUTH_ERROR); throw ex; } } disconnectingSockets = new Map(); /** WebSocket 断开时调用 */ [kOnClose](socket) { const delay = socket.destroyed ? 0 : 5000; logger('[%s] socket closed, drop connection in %dms', socket.id, delay); const oldTid = this.disconnectingSockets.get(socket.id); if (oldTid != null) { clearTimeout(oldTid); } const tid = setTimeout(() => { logger('[%s] connection dropped', socket.id); socket.destroy(); this._sockets.delete(socket.id); this.disconnectingSockets.delete(socket.id); }, delay); this.disconnectingSockets.set(socket.id, tid); } } /** 由 WS Server 建立的 RPC 连接 */ export class RpcServerSocket extends RpcSocket { server; constructor(id, server) { super(id); this.server = server; this.seq = 1; } /** @inheritdoc */ // eslint-disable-next-line @typescript-eslint/promise-function-async authSocket() { return Promise.resolve(this.socket[kMetadata]); } /** @inheritdoc */ get local() { return this.server.local?.(this); } /** @inheritdoc */ onClose(ev) { super.onClose(ev); this.server[kOnClose](this); } /** 替换 socket */ async [kReplaceSocket](newSocket) { this.socket = newSocket; return this.ready; } } //# sourceMappingURL=server.js.map