feat: bump version to 1.2.7 and enhance connection handling in SMTPOverWSClient to prevent race conditions
This commit is contained in:
parent
ad1424a79a
commit
3526902a72
2 changed files with 32 additions and 6 deletions
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@siwats/mxrelay-consumer",
|
"name": "@siwats/mxrelay-consumer",
|
||||||
"version": "1.2.5",
|
"version": "1.2.7",
|
||||||
"description": "An internal TypeScript client library for transporting SMTP messages",
|
"description": "An internal TypeScript client library for transporting SMTP messages",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"module": "lib/index.esm.js",
|
"module": "lib/index.esm.js",
|
||||||
|
|
|
@ -82,10 +82,12 @@ export class SMTPOverWSClient extends EventEmitter {
|
||||||
private reconnectTimer: NodeJS.Timeout | null = null;
|
private reconnectTimer: NodeJS.Timeout | null = null;
|
||||||
private authTimer: NodeJS.Timeout | null = null;
|
private authTimer: NodeJS.Timeout | null = null;
|
||||||
private channelTimer: NodeJS.Timeout | null = null;
|
private channelTimer: NodeJS.Timeout | null = null;
|
||||||
|
private channelCloseTimer: NodeJS.Timeout | null = null;
|
||||||
private heartbeatTimer: NodeJS.Timeout | null = null;
|
private heartbeatTimer: NodeJS.Timeout | null = null;
|
||||||
private sessionTimer: NodeJS.Timeout | null = null;
|
private sessionTimer: NodeJS.Timeout | null = null;
|
||||||
private isProcessingQueue = false;
|
private isProcessingQueue = false;
|
||||||
private isShuttingDown = false;
|
private isShuttingDown = false;
|
||||||
|
private connectionPromise: Promise<void> | null = null;
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
private stats: ClientStats;
|
private stats: ClientStats;
|
||||||
private connectionStartTime: number = 0;
|
private connectionStartTime: number = 0;
|
||||||
|
@ -314,9 +316,17 @@ export class SMTPOverWSClient extends EventEmitter {
|
||||||
let failed = 0;
|
let failed = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Connect if not connected
|
// Connect if not connected - use connection promise to prevent race conditions
|
||||||
if (this.state === ConnectionState.DISCONNECTED) {
|
if (this.state === ConnectionState.DISCONNECTED || this.state === ConnectionState.FAILED) {
|
||||||
await this.connect();
|
if (!this.connectionPromise) {
|
||||||
|
this.connectionPromise = this.connect().finally(() => {
|
||||||
|
this.connectionPromise = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await this.connectionPromise;
|
||||||
|
} else if (this.connectionPromise) {
|
||||||
|
// Wait for ongoing connection attempt to complete
|
||||||
|
await this.connectionPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process sessions sequentially one at a time (SMTP sessions are mutex)
|
// Process sessions sequentially one at a time (SMTP sessions are mutex)
|
||||||
|
@ -789,6 +799,11 @@ export class SMTPOverWSClient extends EventEmitter {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
if (this.state === ConnectionState.CHANNEL_READY) {
|
if (this.state === ConnectionState.CHANNEL_READY) {
|
||||||
const onChannelClosed = () => {
|
const onChannelClosed = () => {
|
||||||
|
// Clear the fallback timer since channel closed properly
|
||||||
|
if (this.channelCloseTimer) {
|
||||||
|
clearTimeout(this.channelCloseTimer);
|
||||||
|
this.channelCloseTimer = null;
|
||||||
|
}
|
||||||
this.setState(ConnectionState.AUTHENTICATED);
|
this.setState(ConnectionState.AUTHENTICATED);
|
||||||
this.emit('channelClosed');
|
this.emit('channelClosed');
|
||||||
this.logger.debug('SMTP channel closed');
|
this.logger.debug('SMTP channel closed');
|
||||||
|
@ -798,9 +813,14 @@ export class SMTPOverWSClient extends EventEmitter {
|
||||||
this.once('smtp_channel_closed', onChannelClosed);
|
this.once('smtp_channel_closed', onChannelClosed);
|
||||||
|
|
||||||
// Fallback timeout
|
// Fallback timeout
|
||||||
setTimeout(() => {
|
this.channelCloseTimer = setTimeout(() => {
|
||||||
this.removeListener('smtp_channel_closed', onChannelClosed);
|
this.removeListener('smtp_channel_closed', onChannelClosed);
|
||||||
|
// Only set to authenticated if we're still connected
|
||||||
|
if (this.ws && this.ws.readyState === WebSocket.OPEN &&
|
||||||
|
this.state !== ConnectionState.DISCONNECTED) {
|
||||||
this.setState(ConnectionState.AUTHENTICATED);
|
this.setState(ConnectionState.AUTHENTICATED);
|
||||||
|
}
|
||||||
|
this.channelCloseTimer = null;
|
||||||
resolve();
|
resolve();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
} else {
|
} else {
|
||||||
|
@ -883,6 +903,7 @@ export class SMTPOverWSClient extends EventEmitter {
|
||||||
this.emit('disconnected', reason);
|
this.emit('disconnected', reason);
|
||||||
this.stopHeartbeat();
|
this.stopHeartbeat();
|
||||||
this.clearTimers();
|
this.clearTimers();
|
||||||
|
this.connectionPromise = null;
|
||||||
|
|
||||||
// Always try to reconnect if we have queued sessions and not shutting down
|
// Always try to reconnect if we have queued sessions and not shutting down
|
||||||
// NEVER reject emails - keep trying with periodic retries
|
// NEVER reject emails - keep trying with periodic retries
|
||||||
|
@ -1010,6 +1031,11 @@ export class SMTPOverWSClient extends EventEmitter {
|
||||||
this.channelTimer = null;
|
this.channelTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.channelCloseTimer) {
|
||||||
|
clearTimeout(this.channelCloseTimer);
|
||||||
|
this.channelCloseTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.sessionTimer) {
|
if (this.sessionTimer) {
|
||||||
clearTimeout(this.sessionTimer);
|
clearTimeout(this.sessionTimer);
|
||||||
this.sessionTimer = null;
|
this.sessionTimer = null;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue