Refactor error handling in message delivery process

The error handling process in sending messages has been refactored for better accuracy and efficiency. Instead of immediately joining errors and returning, they are now collected in a slice and joined in a deferred function to ensure all errors are captured before being returned. Furthermore, the SendError structure has been updated to include a reference to the affected message, providing better context for each error.
This commit is contained in:
Winni Neessen 2024-07-16 13:07:20 +02:00
parent 32a5603496
commit 0ea9631855
Signed by: wneessen
GPG key ID: 5F3AF39B820C119D
2 changed files with 28 additions and 19 deletions

View file

@ -18,25 +18,33 @@ func (c *Client) Send(messages ...*Msg) (returnErr error) {
returnErr = &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)} returnErr = &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)}
return return
} }
for _, message := range messages {
var errs []error
defer func() {
returnErr = errors.Join(errs...)
}()
for msgid, message := range messages {
message.sendError = nil message.sendError = nil
if message.encoding == NoEncoding { if message.encoding == NoEncoding {
if ok, _ := c.smtpClient.Extension("8BITMIME"); !ok { if ok, _ := c.smtpClient.Extension("8BITMIME"); !ok {
message.sendError = &SendError{Reason: ErrNoUnencoded, isTemp: false} message.sendError = &SendError{Reason: ErrNoUnencoded, isTemp: false}
returnErr = errors.Join(returnErr, message.sendError) errs = append(errs, message.sendError)
continue continue
} }
} }
from, err := message.GetSender(false) from, err := message.GetSender(false)
if err != nil { if err != nil {
message.sendError = &SendError{Reason: ErrGetSender, errlist: []error{err}, isTemp: isTempError(err)} message.sendError = &SendError{Reason: ErrGetSender, errlist: []error{err}, isTemp: isTempError(err),
returnErr = errors.Join(returnErr, message.sendError) affectedMsg: messages[msgid]}
errs = append(errs, message.sendError)
continue continue
} }
rcpts, err := message.GetRecipients() rcpts, err := message.GetRecipients()
if err != nil { if err != nil {
message.sendError = &SendError{Reason: ErrGetRcpts, errlist: []error{err}, isTemp: isTempError(err)} message.sendError = &SendError{Reason: ErrGetRcpts, errlist: []error{err}, isTemp: isTempError(err),
returnErr = errors.Join(returnErr, message.sendError) affectedMsg: messages[msgid]}
errs = append(errs, message.sendError)
continue continue
} }
@ -47,9 +55,9 @@ func (c *Client) Send(messages ...*Msg) (returnErr error) {
} }
if err = c.smtpClient.Mail(from); err != nil { if err = c.smtpClient.Mail(from); err != nil {
message.sendError = &SendError{Reason: ErrSMTPMailFrom, errlist: []error{err}, isTemp: isTempError(err)} message.sendError = &SendError{Reason: ErrSMTPMailFrom, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError) errs = append(errs, message.sendError)
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil { if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
returnErr = errors.Join(returnErr, resetSendErr) errs = append(errs, resetSendErr)
} }
continue continue
} }
@ -70,40 +78,40 @@ func (c *Client) Send(messages ...*Msg) (returnErr error) {
} }
if failed { if failed {
if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil { if resetSendErr := c.smtpClient.Reset(); resetSendErr != nil {
returnErr = errors.Join(returnErr, resetSendErr) errs = append(errs, resetSendErr)
} }
message.sendError = rcptSendErr message.sendError = rcptSendErr
returnErr = errors.Join(returnErr, message.sendError) errs = append(errs, message.sendError)
continue continue
} }
writer, err := c.smtpClient.Data() writer, err := c.smtpClient.Data()
if err != nil { if err != nil {
message.sendError = &SendError{Reason: ErrSMTPData, errlist: []error{err}, isTemp: isTempError(err)} message.sendError = &SendError{Reason: ErrSMTPData, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError) errs = append(errs, message.sendError)
continue continue
} }
_, err = message.WriteTo(writer) _, err = message.WriteTo(writer)
if err != nil { if err != nil {
message.sendError = &SendError{Reason: ErrWriteContent, errlist: []error{err}, isTemp: isTempError(err)} message.sendError = &SendError{Reason: ErrWriteContent, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError) errs = append(errs, message.sendError)
continue continue
} }
message.isDelivered = true message.isDelivered = true
if err = writer.Close(); err != nil { if err = writer.Close(); err != nil {
message.sendError = &SendError{Reason: ErrSMTPDataClose, errlist: []error{err}, isTemp: isTempError(err)} message.sendError = &SendError{Reason: ErrSMTPDataClose, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError) errs = append(errs, message.sendError)
continue continue
} }
if err = c.Reset(); err != nil { if err = c.Reset(); err != nil {
message.sendError = &SendError{Reason: ErrSMTPReset, errlist: []error{err}, isTemp: isTempError(err)} message.sendError = &SendError{Reason: ErrSMTPReset, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError) errs = append(errs, message.sendError)
continue continue
} }
if err = c.checkConn(); err != nil { if err = c.checkConn(); err != nil {
message.sendError = &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)} message.sendError = &SendError{Reason: ErrConnCheck, errlist: []error{err}, isTemp: isTempError(err)}
returnErr = errors.Join(returnErr, message.sendError) errs = append(errs, message.sendError)
} }
} }

View file

@ -56,10 +56,11 @@ const (
// SendError is an error wrapper for delivery errors of the Msg // SendError is an error wrapper for delivery errors of the Msg
type SendError struct { type SendError struct {
Reason SendErrReason affectedMsg *Msg
isTemp bool errlist []error
errlist []error isTemp bool
rcpt []string rcpt []string
Reason SendErrReason
} }
// SendErrReason represents a comparable reason on why the delivery failed // SendErrReason represents a comparable reason on why the delivery failed