forked from vikunja/vikunja
Compare commits
2 Commits
main
...
smtp-retry
Author | SHA1 | Date | |
---|---|---|---|
bfb71926a3 | |||
1e7871b5c0 |
@ -19,6 +19,9 @@ package mail
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
@ -46,15 +49,14 @@ func getClient() (*mail.Client, error) {
|
||||
if config.MailerForceSSL.GetBool() {
|
||||
tlsPolicy = mail.TLSMandatory
|
||||
}
|
||||
|
||||
opts := []mail.Option{
|
||||
mail.WithPort(config.MailerPort.GetInt()),
|
||||
mail.WithTLSPortPolicy(tlsPolicy),
|
||||
mail.WithTLSConfig(&tls.Config{
|
||||
//#nosec G402
|
||||
InsecureSkipVerify: config.MailerSkipTLSVerify.GetBool(),
|
||||
ServerName: config.MailerHost.GetString(),
|
||||
}),
|
||||
mail.WithPort(config.MailerPort.GetInt()),
|
||||
mail.WithTimeout((config.MailerQueueTimeout.GetDuration() + 3) * time.Second), // 3s more for us to close before mail server timeout
|
||||
mail.WithLogger(log.NewMailLogger(config.LogEnabled.GetBool(), config.LogMail.GetString(), config.LogMailLevel.GetString())),
|
||||
mail.WithDebugLog(),
|
||||
@ -82,6 +84,38 @@ func getClient() (*mail.Client, error) {
|
||||
)
|
||||
}
|
||||
|
||||
func resilientSend(client *mail.Client, message *mail.Msg) error {
|
||||
const maxRetries = 3
|
||||
retries := 0
|
||||
|
||||
for retries < maxRetries {
|
||||
log.Debug("Sending mail")
|
||||
err := client.Send(message)
|
||||
if err == nil {
|
||||
log.Debug("Mail sent")
|
||||
return nil
|
||||
}
|
||||
|
||||
var sendError *mail.SendError
|
||||
_ = errors.As(err, &sendError)
|
||||
|
||||
if sendError.Reason != mail.ErrConnCheck {
|
||||
// only ErrConnCheck is retried, other exceptions are thrown
|
||||
return err
|
||||
}
|
||||
|
||||
log.Warning("Connection was closed, reconnecting before retrying")
|
||||
err = client.DialWithContext(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("Connection re-established")
|
||||
retries++
|
||||
}
|
||||
|
||||
return errors.New(fmt.Sprintf("could not send mail after %d retries", retries))
|
||||
}
|
||||
|
||||
// StartMailDaemon starts the mail daemon
|
||||
func StartMailDaemon() {
|
||||
Queue = make(chan *mail.Msg, config.MailerQueuelength.GetInt())
|
||||
@ -110,25 +144,32 @@ func StartMailDaemon() {
|
||||
return
|
||||
}
|
||||
if !open {
|
||||
log.Debug("Opening connection to mail server")
|
||||
err = c.DialWithContext(context.Background())
|
||||
if err != nil {
|
||||
log.Errorf("Error during connect to smtp server: %s", err)
|
||||
break
|
||||
}
|
||||
log.Debug("Connected to mail server")
|
||||
open = true
|
||||
}
|
||||
err = c.Send(m)
|
||||
err = resilientSend(c, m)
|
||||
if err != nil {
|
||||
log.Errorf("Error when sending mail: %s", err)
|
||||
log.Errorf("Error sending mail: %s", err)
|
||||
break
|
||||
}
|
||||
// Close the connection to the SMTP server if no email was sent in
|
||||
// the last 30 seconds.
|
||||
case <-time.After(config.MailerQueueTimeout.GetDuration() * time.Second):
|
||||
if open {
|
||||
log.Debug("Closing connection to mail server due to queue timeout")
|
||||
open = false
|
||||
err = c.Close()
|
||||
if err != nil {
|
||||
if strings.HasPrefix(err.Error(), "not connected to SMTP server") {
|
||||
log.Warning("Connection to mail server was already closed")
|
||||
break
|
||||
}
|
||||
log.Errorf("Error closing the mail server connection: %s\n", err)
|
||||
break
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user