update
This commit is contained in:
@ -53,6 +53,11 @@ export class ConnectionManager implements IConnectionManager {
|
||||
*/
|
||||
private resourceCheckInterval: NodeJS.Timeout | null = null;
|
||||
|
||||
/**
|
||||
* Track cleanup timers so we can clear them
|
||||
*/
|
||||
private cleanupTimers: Set<NodeJS.Timeout> = new Set();
|
||||
|
||||
/**
|
||||
* SMTP server options with enhanced resource controls
|
||||
*/
|
||||
@ -531,7 +536,7 @@ export class ConnectionManager implements IConnectionManager {
|
||||
let buffer = '';
|
||||
let totalBytesReceived = 0;
|
||||
|
||||
socket.on('data', (data) => {
|
||||
socket.on('data', async (data) => {
|
||||
try {
|
||||
// Get current session and update activity timestamp
|
||||
const session = this.smtpServer.getSessionManager().getSession(socket);
|
||||
@ -546,7 +551,8 @@ export class ConnectionManager implements IConnectionManager {
|
||||
try {
|
||||
const dataString = data.toString('utf8');
|
||||
// Use a special prefix to indicate this is raw data, not a command line
|
||||
this.smtpServer.getCommandHandler().processCommand(socket, `__RAW_DATA__${dataString}`);
|
||||
// CRITICAL FIX: Must await to prevent async pile-up
|
||||
await this.smtpServer.getCommandHandler().processCommand(socket, `__RAW_DATA__${dataString}`);
|
||||
return;
|
||||
} catch (dataError) {
|
||||
SmtpLogger.error(`Data handler error during DATA mode: ${dataError instanceof Error ? dataError.message : String(dataError)}`);
|
||||
@ -599,15 +605,17 @@ export class ConnectionManager implements IConnectionManager {
|
||||
// Process non-empty lines
|
||||
if (line.length > 0) {
|
||||
try {
|
||||
// In DATA state, the command handler will process the data differently
|
||||
this.smtpServer.getCommandHandler().processCommand(socket, line);
|
||||
} catch (cmdError) {
|
||||
// CRITICAL FIX: Must await processCommand to prevent async pile-up
|
||||
// This was causing the busy loop with high CPU usage when many empty lines were processed
|
||||
await this.smtpServer.getCommandHandler().processCommand(socket, line);
|
||||
} catch (error) {
|
||||
// Handle any errors in command processing
|
||||
SmtpLogger.error(`Command handler error: ${cmdError instanceof Error ? cmdError.message : String(cmdError)}`);
|
||||
SmtpLogger.error(`Command handler error: ${error instanceof Error ? error.message : String(error)}`);
|
||||
this.sendResponse(socket, `${SmtpResponseCode.LOCAL_ERROR} Internal server error`);
|
||||
|
||||
// If there's a severe error, close the connection
|
||||
if (cmdError instanceof Error &&
|
||||
(cmdError.message.includes('fatal') || cmdError.message.includes('critical'))) {
|
||||
if (error instanceof Error &&
|
||||
(error.message.includes('fatal') || error.message.includes('critical'))) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
@ -685,10 +693,25 @@ export class ConnectionManager implements IConnectionManager {
|
||||
// Send service closing notification
|
||||
this.sendServiceClosing(socket);
|
||||
|
||||
// End the socket
|
||||
// End the socket gracefully
|
||||
socket.end();
|
||||
|
||||
// Force destroy after a short delay if not already destroyed
|
||||
const destroyTimer = setTimeout(() => {
|
||||
if (!socket.destroyed) {
|
||||
socket.destroy();
|
||||
}
|
||||
this.cleanupTimers.delete(destroyTimer);
|
||||
}, 100);
|
||||
this.cleanupTimers.add(destroyTimer);
|
||||
} catch (error) {
|
||||
SmtpLogger.error(`Error closing connection: ${error instanceof Error ? error.message : String(error)}`);
|
||||
// Force destroy on error
|
||||
try {
|
||||
socket.destroy();
|
||||
} catch (e) {
|
||||
// Ignore destroy errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -858,12 +881,14 @@ export class ConnectionManager implements IConnectionManager {
|
||||
socket.end();
|
||||
|
||||
// Set a forced close timeout in case socket.end() doesn't close the connection
|
||||
setTimeout(() => {
|
||||
const timeoutDestroyTimer = setTimeout(() => {
|
||||
if (!socket.destroyed) {
|
||||
SmtpLogger.warn(`Forcing destroy of timed out socket: ${socketId}`);
|
||||
socket.destroy();
|
||||
}
|
||||
this.cleanupTimers.delete(timeoutDestroyTimer);
|
||||
}, 5000); // 5 second grace period for socket to end properly
|
||||
this.cleanupTimers.add(timeoutDestroyTimer);
|
||||
} catch (error) {
|
||||
SmtpLogger.error(`Error ending timed out socket: ${error instanceof Error ? error.message : String(error)}`);
|
||||
|
||||
@ -989,6 +1014,12 @@ export class ConnectionManager implements IConnectionManager {
|
||||
this.resourceCheckInterval = null;
|
||||
}
|
||||
|
||||
// Clear all cleanup timers
|
||||
for (const timer of this.cleanupTimers) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
this.cleanupTimers.clear();
|
||||
|
||||
// Close all active connections
|
||||
this.closeAllConnections();
|
||||
|
||||
|
Reference in New Issue
Block a user