@cloudpss/ubrpc
Version:
70 lines (65 loc) • 2.81 kB
text/typescript
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);
};
});
}