0% found this document useful (0 votes)
37 views3 pages

Message

Uploaded by

sup87945487
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
37 views3 pages

Message

Uploaded by

sup87945487
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

import { w3cwebsocket } from 'websocket';

import { IWebSocketClient } from './types';


import { randomUUID } from 'crypto';
import { EventEmitter } from 'stream';
import { pack } from '@/utils/transfer';
import debug from 'debug';
import * as CryptoJS from 'crypto-js';
import { exit } from 'process';

const dMsg = debug('client');

interface EventData<T = any> {


requestToken?: string;
eventType: string;
result: T;
}

type Fn = (...args: any[]) => any;

export class WebSocketClient extends w3cwebsocket implements IWebSocketClient {


public evCount = new Map<string, number>();
public evPool: EventEmitter;
public algo: 'aes' | 'hmac';
public secret: string = '';
public timeoutMilli: number = 3000;
public hstSize: number = 30;

constructor(url: string, algo: 'aes' | 'hmac') {


super(url);
this.evPool = new EventEmitter();
this.algo = algo;
this.onopen = () => {
dMsg('CLIENT CONNECTED!');
this.send(this.algo);
};

this.onclose = () => {
dMsg('BYE BYE');
exit(0);
};

this.initial();
}

dispatch(eventType: string, payload?: any): void {


this.evPool.emit(eventType, payload);
}

on(eventType: string, listener: Fn): number {


let currentCount = this.evCount.get(eventType) ?? 0;
this.evPool.on(eventType, listener);
this.evCount.set(eventType, ++currentCount);
return currentCount;
}

once(eventType: string, listener: Fn): number {


let currentCount = this.evCount.get(eventType) ?? 0;
this.evPool.once(eventType, (...args: any[]) => {
listener(...args);
const count = this.evCount.get(eventType) ?? 0;
if (count) this.evCount.set(eventType, count - 1);
});
this.evCount.set(eventType, ++currentCount);
return currentCount;
}

off(eventType: string): boolean {


const containEvent = this.evPool.eventNames().includes(eventType);
this.evPool.removeAllListeners(eventType);
return containEvent;
}

async ready() {
return new Promise<string>((resolve) => {
if (this.secret !== '') return resolve(this.secret);

this.onmessage = ({ data }) => resolve(data.toString());


});
}

async initial() {
this.ready().then((secret) => {
this.secret = secret;
this.onmessage = ({ data }) => {
dMsg("Receive Raw'%s'", data);
try {
const { requestToken = '', eventType, result } =
JSON.parse(data.toString()) as EventData;

dMsg('Resolve: %O', {
requestToken,
eventType,
result,
});

if (requestToken !== '') {


this.dispatch(requestToken, result);
} else {
this.dispatch(eventType, result);
}
} catch (e) {
dMsg('ERROR_REASON: ', (e as Error).message);
}
};
});
}

async sendout(payload: any, requestToken: string = '$NONE') {


await this.ready();

const signature =
this.algo === 'hmac'
? CryptoJS.HmacSHA1(requestToken, this.secret).toString()
: CryptoJS.AES.encrypt(requestToken, this.secret).toString();

this.send(pack({ requestToken, signature, payload }));


}
async request<T = any>(eventType: string, params?: any): Promise<T> {
const requestToken = `request::${randomUUID()}`;
this.sendout({ eventType, params }, requestToken);

return new Promise((resolve, failed) => {


const timer = setTimeout(() => {
this.off(requestToken);
resolve('TIMEOUT' as any);
}, this.timeoutMilli);

this.once(requestToken, (data) => {


clearTimeout(timer);
resolve(data);
});
});
}

history() {
return [];
}
}

You might also like