Tear down at once on a peer unbind and stop reporting a reconnect after close

This commit is contained in:
2026-08-30 21:01:46 +02:00
parent 66b7d90bbf
commit 7e8af3c308
7 changed files with 125 additions and 24 deletions
+2 -1
View File
@@ -70,7 +70,8 @@ export class IncomingRequests {
break;
case 'unbind':
await this.session.sendReturn(pduObj);
await this.session.close();
// A peer that has said it is finished will not answer what we still have outstanding.
await this.session.close({ signal: AbortSignal.abort() });
break;
default:
await this.unhandled(pduObj);
-1
View File
@@ -118,7 +118,6 @@ export class SmppServer extends EventEmitter<ServerEvents> {
/** Stops listening, then drains and closes every session that was live when it stopped. */
async close(options: CloseOptions = {}): Promise<void> {
// Before the drain, or the listener keeps accepting connections nothing will ever close.
const stopped = new Promise<void>(resolve => { this.server.close(() => { resolve(); }); });
const live = [...this.sessions];
+10 -6
View File
@@ -273,6 +273,13 @@ export class Session extends EventEmitter<SessionEvents> {
return { err: bound.err };
}
// close() can land while the rebind is in flight, and it has nothing left to tear down.
if (this.reconnectLoop?.isStopped() === true) {
this.teardown();
return { err: new Error('Session closed while it was coming back up') };
}
this.resetTimers();
this.log.info('session - reconnected');
this.emit('reconnected');
@@ -327,10 +334,7 @@ export class Session extends EventEmitter<SessionEvents> {
return response;
}
/**
* Stops new sends and waits out the ones already issued, which only covers what this end sent:
* a request the peer sent us is answered through sendReturn(), which never enters the window.
*/
/** Stops new sends and waits out the ones already issued. */
private async drain(signal: AbortSignal | undefined): Promise<VoidResult> {
this.reconnectLoop?.stop();
this.draining = true;
@@ -340,8 +344,8 @@ export class Session extends EventEmitter<SessionEvents> {
const timeout = this.options.shutdownTimeout ?? defaults.shutdownTimeout;
const unfinished = await this.window.idle(timeout, signal);
// The window empties on a drop too: teardown() settles everything the link was carrying.
if (this.isClosed()) return { err: new Error('The link dropped before the drain finished') };
// The window empties on a teardown too, which settles everything the link was carrying.
if (this.isClosed()) return { err: new Error('The session closed before the drain finished') };
if (unfinished === 0) return {};