Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go: Connect to SMTP server and send multiple emails in one connection?

I'm writing a package which I intend to make one initial connection to the local SMTP server and then it waits on a channel which will be populated with emails to send when they are needed to be sent.

Looking at net/http it appears to be expecting that the SMTP server should be dialed into and authenticated each time an email is to be sent. Surely I can dial and authenticate once, keep the connection open and just add new emails as they come?

Looking at the source for smtp.SendMail, I don't see how this can be done, as there does not appear to be a way to recycle the *Client: http://golang.org/src/net/smtp/smtp.go?s=7610:7688#L263

There is a Reset function for *Client, but the description for that is:

 Reset sends the RSET command to the server, aborting the current mail transaction.

I don't want to abort the current mail transaction, I want to have multiple mail transactions.

How can I keep the connection to the SMTP server open and send multiple emails on that one connection?

like image 374
Alasdair Avatar asked May 26 '15 05:05

Alasdair


2 Answers

You are correct that smtp.SendMail does not provide a way to reuse the connection.

If you want that fine grained control, you should use a persistent client connection. This will require knowing a bit more about the smtp commands and protocol.

  1. Use smtp.Dial to open a connection and create a client object.
  2. Use client.Hello, client.Auth, and client.StartTLS as appropriate.
  3. Use Rcpt, Mail, Data as approprate to send messages.
  4. client.Quit() and close connection when you are all done.
like image 107
captncraig Avatar answered Oct 22 '22 08:10

captncraig


Gomail v2 now supports sending multiple emails in one connection. The Daemon example from the documentation seems to match your use case:

ch := make(chan *gomail.Message)

go func() {
    d := gomail.NewPlainDialer("smtp.example.com", 587, "user", "123456")

    var s gomail.SendCloser
    var err error
    open := false
    for {
        select {
        case m, ok := <-ch:
            if !ok {
                return
            }
            if !open {
                if s, err = d.Dial(); err != nil {
                    panic(err)
                }
                open = true
            }
            if err := gomail.Send(s, m); err != nil {
                log.Print(err)
            }
        // Close the connection to the SMTP server if no email was sent in
        // the last 30 seconds.
        case <-time.After(30 * time.Second):
            if open {
                if err := s.Close(); err != nil {
                    panic(err)
                }
                open = false
            }
        }
    }
}()

// Use the channel in your program to send emails.

// Close the channel to stop the mail daemon.
close(ch)
like image 45
Ale Avatar answered Oct 22 '22 07:10

Ale