UNPKG

@cloudpss/ubrpc

Version:

70 lines (65 loc) 2.81 kB
import type { ConnectionID, RpcMetadata } from './types/payload.js'; import { decodePayload, deserializeError } from './utils/serialize.js'; import { VERSION } from './version.js'; import { WebSocketAppCode } from './codes.js'; const AUTH_TIMEOUT = 5000; /** 认证结果 */ type AuthResult = [seq: number, id: ConnectionID, meta: RpcMetadata]; /** 检查认证消息 */ function checkAuthMessage(data: unknown, seq?: number, id?: ConnectionID): AuthResult { const isResponse = seq != null && id != null; const payload = decodePayload(data); if (payload?.type !== 'auth') { if (isResponse) throw new Error(`Failed to authenticate, bad response from server`); else throw new Error(`Failed to authenticate, bad request from client`); } if (payload.version !== VERSION) { throw new Error(`Failed to authenticate, version not match, expected ${VERSION}, got ${payload.version}`); } if (isResponse) { if (payload.seq !== seq) { throw new Error(`Failed to authenticate, bad response seq from server`); } if (payload.id !== id) { throw new Error(`Failed to authenticate, bad response id from server`); } } if (payload.error) { throw deserializeError(payload.error); } const metadata = typeof payload.metadata != 'object' || payload.metadata == null ? {} : payload.metadata; return [payload.seq, payload.id, metadata]; } /** 接受认证消息 */ export async function waitAuth(socket: WebSocket, seq?: number, id?: ConnectionID): Promise<AuthResult> { return new Promise((resolve, reject) => { const onTimeout = (): void => { reject(new Error(`Failed to authenticate, no remote message in ${AUTH_TIMEOUT}ms`)); finalize(false); }; const onClose = (ev: CloseEvent): void => { reject(new Error(`Failed to authenticate, socket closed, code=${ev.code}: ${ev.reason}`)); finalize(false); }; const onMessage = (ev: MessageEvent): void => { try { const result = checkAuthMessage(ev.data, seq, id); resolve(result); finalize(true); } catch (ex) { reject(ex as Error); finalize(false); } }; const timeout = setTimeout(onTimeout, AUTH_TIMEOUT); socket.addEventListener('close', onClose); socket.addEventListener('message', onMessage); const finalize = (ok: boolean): void => { clearTimeout(timeout); socket.removeEventListener('message', onMessage); socket.removeEventListener('close', onClose); if (ok) return; socket.close(WebSocketAppCode.AUTH_ERROR); }; }); }