141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
/**
|
|
* SMTP Client Error Handler
|
|
* Error classification and recovery strategies
|
|
*/
|
|
|
|
import { SmtpErrorType } from './constants.js';
|
|
import type { ISmtpResponse, ISmtpErrorContext, ISmtpClientOptions } from './interfaces.js';
|
|
import { logDebug } from './utils/logging.js';
|
|
|
|
export class SmtpErrorHandler {
|
|
private options: ISmtpClientOptions;
|
|
|
|
constructor(options: ISmtpClientOptions) {
|
|
this.options = options;
|
|
}
|
|
|
|
/**
|
|
* Classify error type based on response or error
|
|
*/
|
|
public classifyError(error: Error | ISmtpResponse, context?: ISmtpErrorContext): SmtpErrorType {
|
|
logDebug('Classifying error', this.options, { errorMessage: error instanceof Error ? error.message : String(error), context });
|
|
|
|
// Handle Error objects
|
|
if (error instanceof Error) {
|
|
return this.classifyErrorByMessage(error);
|
|
}
|
|
|
|
// Handle SMTP response codes
|
|
if (typeof error === 'object' && 'code' in error) {
|
|
return this.classifyErrorByCode(error.code);
|
|
}
|
|
|
|
return SmtpErrorType.UNKNOWN_ERROR;
|
|
}
|
|
|
|
/**
|
|
* Determine if error is retryable
|
|
*/
|
|
public isRetryable(errorType: SmtpErrorType, response?: ISmtpResponse): boolean {
|
|
switch (errorType) {
|
|
case SmtpErrorType.CONNECTION_ERROR:
|
|
case SmtpErrorType.TIMEOUT_ERROR:
|
|
return true;
|
|
|
|
case SmtpErrorType.PROTOCOL_ERROR:
|
|
// Only retry on temporary failures (4xx codes)
|
|
return response ? response.code >= 400 && response.code < 500 : false;
|
|
|
|
case SmtpErrorType.AUTHENTICATION_ERROR:
|
|
case SmtpErrorType.TLS_ERROR:
|
|
case SmtpErrorType.SYNTAX_ERROR:
|
|
case SmtpErrorType.MAILBOX_ERROR:
|
|
case SmtpErrorType.QUOTA_ERROR:
|
|
return false;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get retry delay for error type
|
|
*/
|
|
public getRetryDelay(attempt: number, errorType: SmtpErrorType): number {
|
|
const baseDelay = 1000; // 1 second
|
|
const maxDelay = 30000; // 30 seconds
|
|
|
|
// Exponential backoff with jitter
|
|
const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
|
|
const jitter = Math.random() * 0.1 * delay; // 10% jitter
|
|
|
|
return Math.floor(delay + jitter);
|
|
}
|
|
|
|
/**
|
|
* Create enhanced error with context
|
|
*/
|
|
public createError(
|
|
message: string,
|
|
errorType: SmtpErrorType,
|
|
context?: ISmtpErrorContext,
|
|
originalError?: Error
|
|
): Error {
|
|
const error = new Error(message);
|
|
(error as any).type = errorType;
|
|
(error as any).context = context;
|
|
(error as any).originalError = originalError;
|
|
|
|
return error;
|
|
}
|
|
|
|
private classifyErrorByMessage(error: Error): SmtpErrorType {
|
|
const message = error.message.toLowerCase();
|
|
|
|
if (message.includes('timeout') || message.includes('etimedout')) {
|
|
return SmtpErrorType.TIMEOUT_ERROR;
|
|
}
|
|
|
|
if (message.includes('connect') || message.includes('econnrefused') ||
|
|
message.includes('enotfound') || message.includes('enetunreach')) {
|
|
return SmtpErrorType.CONNECTION_ERROR;
|
|
}
|
|
|
|
if (message.includes('tls') || message.includes('ssl') ||
|
|
message.includes('certificate') || message.includes('handshake')) {
|
|
return SmtpErrorType.TLS_ERROR;
|
|
}
|
|
|
|
if (message.includes('auth')) {
|
|
return SmtpErrorType.AUTHENTICATION_ERROR;
|
|
}
|
|
|
|
return SmtpErrorType.UNKNOWN_ERROR;
|
|
}
|
|
|
|
private classifyErrorByCode(code: number): SmtpErrorType {
|
|
if (code >= 500) {
|
|
// Permanent failures
|
|
if (code === 550 || code === 551 || code === 553) {
|
|
return SmtpErrorType.MAILBOX_ERROR;
|
|
}
|
|
if (code === 552) {
|
|
return SmtpErrorType.QUOTA_ERROR;
|
|
}
|
|
if (code === 500 || code === 501 || code === 502 || code === 504) {
|
|
return SmtpErrorType.SYNTAX_ERROR;
|
|
}
|
|
return SmtpErrorType.PROTOCOL_ERROR;
|
|
}
|
|
|
|
if (code >= 400) {
|
|
// Temporary failures
|
|
if (code === 450 || code === 451 || code === 452) {
|
|
return SmtpErrorType.QUOTA_ERROR;
|
|
}
|
|
return SmtpErrorType.PROTOCOL_ERROR;
|
|
}
|
|
|
|
return SmtpErrorType.UNKNOWN_ERROR;
|
|
}
|
|
} |