~bigbes/core-go

bbcb2a1096d86b989e5d82dc45289daec38ba4a6 — Robin Jarry 1 year, 8 months ago 7457c44
email: keep smtp client connection alive

Instead of connecting and disconnecting each and every time we need to
send an email, keep the SMTP connection alive attached to a context
variable.

Before sending an email, check if the connection is still valid and
reconnect if it is not.

Signed-off-by: Robin Jarry <robin@jarry.cc>
2 files changed, 29 insertions(+), 9 deletions(-)

M email/send.go
M email/worker.go
M email/send.go => email/send.go +18 -5
@@ 4,10 4,10 @@ import (
	"context"
	"fmt"
	"io"
	"net/mail"
	"strconv"

	_ "github.com/emersion/go-message/charset"
	"github.com/emersion/go-message/mail"
	"github.com/emersion/go-sasl"
	"github.com/emersion/go-smtp"



@@ 111,10 111,23 @@ func mailSetup(ctx context.Context) (*smtp.Client, *mail.Address, error) {

// Sends an email. Blocks until it's sent or an error occurs.
func Send(ctx context.Context, msg io.Reader, rcpts []string) error {
	c, sender, err := mailSetup(ctx)
	worker := ForContext(ctx)

	if worker.client == nil || worker.client.Noop() != nil {
		worker.client = nil

		c, sender, err := mailSetup(ctx)
		if err != nil {
			return err
		}
		worker.client = c
		worker.sender = sender
	}

	err := worker.client.SendMail(worker.sender.Address, rcpts, msg)
	if err != nil {
		return err
		worker.client.Close()
		worker.client = nil
	}
	defer c.Close()
	return c.SendMail(sender.Address, rcpts, msg)
	return err
}

M email/worker.go => email/worker.go +11 -4
@@ 18,6 18,7 @@ import (
	_ "github.com/emersion/go-message/charset"
	"github.com/emersion/go-message/mail"
	"github.com/emersion/go-pgpmail"
	"github.com/emersion/go-smtp"
	"github.com/vaughan0/go-ini"
)



@@ 27,6 28,12 @@ type contextKey struct {
	name string
}

type workerContext struct {
	client *smtp.Client
	sender *mail.Address
	queue  *Queue
}

// Returns a task which will send this email for the work queue. If the caller
// does not need to customize the task parameters, the Enqueue function may be
// more desirable.


@@ 81,7 88,7 @@ func prepareSigned(header mail.Header, buf *bytes.Buffer,
func EnqueueStd(ctx context.Context, header mail.Header,
	bodyReader io.Reader, rcptKey *string) error {

	queue := ForContext(ctx)
	queue := ForContext(ctx).queue

	to, err := header.AddressList("To")
	if err != nil {


@@ 246,8 253,8 @@ func NewQueue(conf ini.File) *Queue {
}

// Returns the email worker for this context.
func ForContext(ctx context.Context) *Queue {
	q, ok := ctx.Value(emailCtxKey).(*Queue)
func ForContext(ctx context.Context) *workerContext {
	q, ok := ctx.Value(emailCtxKey).(*workerContext)
	if !ok {
		panic(errors.New("No email worker for this context"))
	}


@@ 256,7 263,7 @@ func ForContext(ctx context.Context) *Queue {

// Returns a context which includes the given mail worker.
func Context(ctx context.Context, queue *Queue) context.Context {
	return context.WithValue(ctx, emailCtxKey, queue)
	return context.WithValue(ctx, emailCtxKey, &workerContext{queue: queue})
}

// Adds HTTP middleware to provide an email work queue to this context.