Contain a throw from the application's logger instead of letting it escape
This commit is contained in:
+2
-2
@@ -11,7 +11,7 @@ import { checkSessionOptions, undeclaredInterfaceVersion } from './session-optio
|
||||
import { connect as netConnect } from 'node:net';
|
||||
import { connect as tlsConnect } from 'node:tls';
|
||||
import { defaultInterfaceVersion } from './defs/constants.ts';
|
||||
import { silentLog } from './log.ts';
|
||||
import { guardedLog } from './log.ts';
|
||||
|
||||
export type ClientOptions = {
|
||||
addressRange?: string;
|
||||
@@ -194,7 +194,7 @@ async function connect(options: ClientOptions, log: SmppLog): Promise<Result<{ s
|
||||
|
||||
/** Connects to an SMSC and binds. */
|
||||
export async function client(options: ClientOptions = {}): Promise<Result<{ session: Session }>> {
|
||||
const log = options.log ?? silentLog;
|
||||
const log = guardedLog(options.log);
|
||||
const opened = await connect(options, log);
|
||||
|
||||
if (opened.err) return { err: opened.err };
|
||||
|
||||
+23
@@ -20,3 +20,26 @@ export const silentLog: SmppLog = {
|
||||
verbose: noop,
|
||||
warn: noop,
|
||||
};
|
||||
|
||||
function contained(log: SmppLog, method: keyof SmppLog): LogMethod {
|
||||
return (msg, metadata) => {
|
||||
try {
|
||||
log[method](msg, metadata);
|
||||
} catch {
|
||||
// Reporting a broken logger would need a logger, and hard rule 1 outranks the report.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** The application's logger with a throw from it contained, or silence when it supplied none. */
|
||||
export function guardedLog(log?: SmppLog): SmppLog {
|
||||
if (log === undefined) return silentLog;
|
||||
|
||||
return {
|
||||
debug: contained(log, 'debug'),
|
||||
error: contained(log, 'error'),
|
||||
info: contained(log, 'info'),
|
||||
verbose: contained(log, 'verbose'),
|
||||
warn: contained(log, 'warn'),
|
||||
};
|
||||
}
|
||||
|
||||
+6
-6
@@ -12,7 +12,7 @@ import { createServer as createTlsServer } from 'node:tls';
|
||||
import { defaultInterfaceVersion } from './defs/constants.ts';
|
||||
import { errorFrom } from './error-from.ts';
|
||||
import { paramText } from './defs/types.ts';
|
||||
import { silentLog } from './log.ts';
|
||||
import { guardedLog } from './log.ts';
|
||||
|
||||
export type AuthenticateResult = { userData?: unknown } | boolean;
|
||||
|
||||
@@ -185,7 +185,7 @@ async function acceptBind(
|
||||
}
|
||||
|
||||
async function onBind(session: Session, pduObj: PduObject, options: ServerOptions): Promise<void> {
|
||||
const log = options.log ?? silentLog;
|
||||
const log = guardedLog(options.log);
|
||||
const identity = { system_id: options.systemId ?? defaults.systemId };
|
||||
const systemId = paramText(pduObj.params.system_id);
|
||||
|
||||
@@ -212,7 +212,7 @@ async function onRequest(
|
||||
if (session.loggedIn || pduObj.cmdName === 'unbind') return false;
|
||||
|
||||
if (!bindCommands.includes(pduObj.cmdName)) {
|
||||
const log = options.log ?? silentLog;
|
||||
const log = guardedLog(options.log);
|
||||
|
||||
log.debug('server - command before bind', { cmdName: pduObj.cmdName });
|
||||
await session.sendReturn(pduObj, 'ESME_RINVBNDSTS');
|
||||
@@ -226,7 +226,7 @@ async function onRequest(
|
||||
}
|
||||
|
||||
function onConnection(sock: Socket, options: ServerOptions, server: SmppServer): void {
|
||||
const log = options.log ?? silentLog;
|
||||
const log = guardedLog(options.log);
|
||||
const session = new Session({
|
||||
idleTimeout: options.idleTimeout ?? defaults.idleTimeout,
|
||||
log,
|
||||
@@ -304,7 +304,7 @@ function createListener(
|
||||
}
|
||||
|
||||
function onListening(listener: NetServer, smpp: SmppServer, options: ServerOptions): void {
|
||||
const log = options.log ?? silentLog;
|
||||
const log = guardedLog(options.log);
|
||||
|
||||
// Past startup, a listener error is a runtime event, not a failed start.
|
||||
listener.on('error', (err: Error) => {
|
||||
@@ -321,7 +321,7 @@ function onListening(listener: NetServer, smpp: SmppServer, options: ServerOptio
|
||||
|
||||
/** Starts listening for SMPP connections. Resolves once the socket is bound. */
|
||||
export function server(options: ServerOptions = {}): Promise<Result<{ server: SmppServer }>> {
|
||||
const log = options.log ?? silentLog;
|
||||
const log = guardedLog(options.log);
|
||||
const port = options.port ?? defaults.port;
|
||||
const created = createListener(options, log, port);
|
||||
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ import { errorFrom } from './error-from.ts';
|
||||
import { optionalParamsMinVersion } from './defs/constants.ts';
|
||||
import { bindCarries, bindCommands, defaultSystemId, defaults } from './session-options.ts';
|
||||
import { isResp, pduReturn } from './pdu.ts';
|
||||
import { silentLog } from './log.ts';
|
||||
import { guardedLog } from './log.ts';
|
||||
import { submitSms, unsent } from './send-sms.ts';
|
||||
import { ConcatReference } from './udh.ts';
|
||||
|
||||
@@ -106,7 +106,7 @@ export class Session extends EventEmitter<SessionEvents> {
|
||||
constructor(options: SessionOptions) {
|
||||
super({ captureRejections: true });
|
||||
|
||||
this.log = options.log ?? silentLog;
|
||||
this.log = guardedLog(options.log);
|
||||
this.options = options;
|
||||
this.dlrMerger = new DlrMerger({
|
||||
log: this.log,
|
||||
|
||||
@@ -1329,6 +1329,24 @@ describe('application hooks that throw or reject', () => {
|
||||
assert.equal(smpp.sessions.size, 0);
|
||||
});
|
||||
|
||||
test('sends on through an application logger that throws', async t => {
|
||||
const thrower = (): void => { throw new Error('the logger exploded'); };
|
||||
const log: SmppLog = { debug: thrower, error: thrower, info: thrower, verbose: thrower, warn: thrower };
|
||||
const smpp = await startServer(t, { log });
|
||||
|
||||
smpp.on('session', session => {
|
||||
session.on('sms', sms => { void sms.sendResp(); });
|
||||
});
|
||||
|
||||
const { session } = await connect(t, smpp, { log });
|
||||
|
||||
assert.ok(session);
|
||||
|
||||
const sent = await session.sendSms({ from: '46701113311', message: 'logged', to: '46709771337' });
|
||||
|
||||
assert.equal(sent.err, undefined);
|
||||
});
|
||||
|
||||
test('refuses a send window that can never free a slot', async t => {
|
||||
const smpp = await startServer(t);
|
||||
const { err, session } = await connect(t, smpp, { maxOutstanding: 0 });
|
||||
|
||||
Reference in New Issue
Block a user