update
This commit is contained in:
141
ts/mail/delivery/smtpclient/error-handler.ts
Normal file
141
ts/mail/delivery/smtpclient/error-handler.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user