89 lines
2.2 KiB
TypeScript
89 lines
2.2 KiB
TypeScript
import type { PduObject } from './pdu.ts';
|
|
import type { Result } from './result.ts';
|
|
import type { SmppLog } from './log.ts';
|
|
import { maxSeqNr } from './pdu.ts';
|
|
|
|
export type WaitOptions = {
|
|
signal?: AbortSignal | undefined;
|
|
timeout: number;
|
|
};
|
|
|
|
type Pending = {
|
|
settle: (result: Result<{ pduObj: PduObject }>) => void;
|
|
};
|
|
|
|
/** Hands out sequence numbers and matches responses to the requests waiting for them. */
|
|
export class PendingRequests {
|
|
private readonly log: SmppLog;
|
|
private readonly pending = new Map<number, Pending>();
|
|
private ourSeqNr = 1;
|
|
|
|
constructor(log: SmppLog) {
|
|
this.log = log;
|
|
}
|
|
|
|
nextSeqNr(): number {
|
|
const seqNr = this.ourSeqNr;
|
|
|
|
this.ourSeqNr = this.ourSeqNr >= maxSeqNr ? 1 : this.ourSeqNr + 1;
|
|
|
|
return seqNr;
|
|
}
|
|
|
|
wait(seqNr: number, options: WaitOptions): Promise<Result<{ pduObj: PduObject }>> {
|
|
const { signal, timeout } = options;
|
|
|
|
return new Promise(resolve => {
|
|
const abort = (): void => {
|
|
this.settle(seqNr, { err: new Error('Aborted before a response arrived') });
|
|
};
|
|
const timer = timeout > 0 ? this.expire(seqNr, timeout) : undefined;
|
|
|
|
this.pending.set(seqNr, {
|
|
settle: result => {
|
|
if (timer) clearTimeout(timer);
|
|
|
|
signal?.removeEventListener('abort', abort);
|
|
this.pending.delete(seqNr);
|
|
resolve(result);
|
|
},
|
|
});
|
|
|
|
if (signal?.aborted === true) abort();
|
|
else signal?.addEventListener('abort', abort, { once: true });
|
|
});
|
|
}
|
|
|
|
/** Hands a response to whoever is waiting for it. False means nobody was. */
|
|
deliver(pduObj: PduObject): boolean {
|
|
const pending = this.pending.get(pduObj.seqNr);
|
|
|
|
if (!pending) return false;
|
|
|
|
pending.settle({ pduObj });
|
|
|
|
return true;
|
|
}
|
|
|
|
settle(seqNr: number, result: Result<{ pduObj: PduObject }>): void {
|
|
this.pending.get(seqNr)?.settle(result);
|
|
}
|
|
|
|
settleAll(err: Error): void {
|
|
for (const [seqNr] of this.pending) {
|
|
this.settle(seqNr, { err });
|
|
}
|
|
}
|
|
|
|
private expire(seqNr: number, timeout: number): NodeJS.Timeout {
|
|
const timer = setTimeout(() => {
|
|
this.log.warn('pendingRequests - no response before the timeout', { seqNr, timeout });
|
|
this.settle(seqNr, { err: new Error(`No response to seqNr ${String(seqNr)}`) });
|
|
}, timeout);
|
|
|
|
timer.unref();
|
|
|
|
return timer;
|
|
}
|
|
}
|