Integrate new SMTP pool lib

This commit is contained in:
Kailash Nadh 2020-05-16 22:41:30 +05:30
parent e58b2fa669
commit 9d3ca357f6
7 changed files with 68 additions and 56 deletions

View file

@ -91,8 +91,7 @@ max_idle = 10
username = "xxxxx"
password = ""
# Optional. Inform listmonk on which email format to use
# {html|plain|both} default: both
# Format to send e-mails in: html|plain|both.
email_format = "both"
# Optional. Some SMTP servers require a FQDN in the hostname.
@ -100,12 +99,19 @@ max_idle = 10
# hostname should be used.
hello_hostname = ""
# Maximum time (milliseconds) to wait per e-mail push.
send_timeout = 5000
# Maximum concurrent connections to the SMTP server.
max_conns = 10
# Time to wait for new activity on a connection before closing
# it and removing it from the pool.
idle_timeout = "15s"
# Message send / wait timeout.
wait_timeout = "5s"
# The number of times a message should be retried if sending fails.
max_msg_retries = 2
[smtp.postal]
enabled = false
host = "my.smtp.server2"
@ -116,16 +122,27 @@ max_idle = 10
username = "xxxxx"
password = ""
# Optional. Inform listmonk on which email format to use
# {html|plain|both} default: both
# Format to send e-mails in: html|plain|both.
email_format = "both"
# Maximum time (milliseconds) to wait per e-mail push.
send_timeout = 5000
# Optional. Some SMTP servers require a FQDN in the hostname.
# By default, HELLOs go with "localhost". Set this if a custom
# hostname should be used.
hello_hostname = ""
# Maximum concurrent connections to the SMTP server.
max_conns = 10
# Time to wait for new activity on a connection before closing
# it and removing it from the pool.
idle_timeout = "15s"
# Message send / wait timeout.
wait_timeout = "5s"
# The number of times a message should be retried if sending fails.
max_msg_retries = 2
[upload]
# File storage backend. "filesystem" or "s3".

6
go.mod
View file

@ -1,13 +1,14 @@
module github.com/knadh/listmonk
go 1.13
require (
github.com/disintegration/imaging v1.6.2
github.com/gofrs/uuid v3.2.0+incompatible
github.com/jaytaylor/html2text v0.0.0-20200220170450-61d9dc4d7195
github.com/jmoiron/sqlx v1.2.0
github.com/jordan-wright/email v0.0.0-20200307200233-de844847de93
github.com/knadh/goyesql/v2 v2.1.1
github.com/knadh/koanf v0.8.1
github.com/knadh/smtppool v0.2.0
github.com/knadh/stuffbin v1.1.0
github.com/labstack/echo v3.3.10+incompatible
github.com/labstack/gommon v0.3.0 // indirect
@ -22,6 +23,3 @@ require (
jaytaylor.com/html2text v0.0.0-20200220170450-61d9dc4d7195
)
replace github.com/jordan-wright/email => github.com/knadh/email v0.0.0-20200206100304-6d2c7064c2e8
go 1.13

4
go.sum
View file

@ -29,6 +29,10 @@ github.com/knadh/goyesql/v2 v2.1.1 h1:Orp5ldaxPM4ozKHfu1m7p6iolJFXDGOpF3/jyOgO6l
github.com/knadh/goyesql/v2 v2.1.1/go.mod h1:pMzCA130/ZhEIoMmSmbEFXor3A2dxl5L+JllAc/l64s=
github.com/knadh/koanf v0.8.1 h1:4VLACWqrkWRQIup3ooq6lOnaSbOJSNO+YVXnJn/NPZ8=
github.com/knadh/koanf v0.8.1/go.mod h1:kVvmDbXnBtW49Czi4c1M+nnOWF0YSNZ8BaKvE/bCO1w=
github.com/knadh/smtppool v0.1.1 h1:pSi1Gc5TXOaN/Z/YiqfZbk/vd9dqzXzAfQiss0QSGQU=
github.com/knadh/smtppool v0.1.1/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA=
github.com/knadh/smtppool v0.2.0 h1:+llTWRljNIVg05MMu9TiefELTNwblexjsd1ALAPXZUs=
github.com/knadh/smtppool v0.2.0/go.mod h1:3DJHouXAgPDBz0kC50HukOsdapYSwIEfJGwuip46oCA=
github.com/knadh/stuffbin v1.0.0 h1:NQon6PTpLXies4bRFhS3VpLCf6y+jn6YVXU3i2wPQ+M=
github.com/knadh/stuffbin v1.0.0/go.mod h1:yVCFaWaKPubSNibBsTAJ939q2ABHudJQxRWZWV5yh+4=
github.com/knadh/stuffbin v1.1.0 h1:f5S5BHzZALjuJEgTIOMC9NidEnBJM7Ze6Lu1GHR/lwU=

15
init.go
View file

@ -7,11 +7,11 @@ import (
"os"
"path/filepath"
"strings"
"time"
"github.com/jmoiron/sqlx"
"github.com/knadh/goyesql/v2"
goyesqlx "github.com/knadh/goyesql/v2/sqlx"
"github.com/knadh/koanf"
"github.com/knadh/koanf/maps"
"github.com/knadh/listmonk/internal/manager"
"github.com/knadh/listmonk/internal/media"
@ -214,28 +214,27 @@ func initImporter(q *Queries, db *sqlx.DB, app *App) *subimporter.Importer {
})
}
// initMessengers initializes various messaging backends.
// initMessengers initializes various messenger backends.
func initMessengers(m *manager.Manager) messenger.Messenger {
// Load SMTP configurations for the default e-mail Messenger.
var (
mapKeys = ko.MapKeys("smtp")
srv = make([]messenger.Server, 0, len(mapKeys))
)
// Load the default SMTP messengers.
for _, name := range mapKeys {
if !ko.Bool(fmt.Sprintf("smtp.%s.enabled", name)) {
lo.Printf("skipped SMTP: %s", name)
continue
}
var s messenger.Server
if err := ko.Unmarshal("smtp."+name, &s); err != nil {
// Read the SMTP config.
s := messenger.Server{Name: name}
if err := ko.UnmarshalWithConf("smtp."+name, &s, koanf.UnmarshalConf{Tag: "json"}); err != nil {
lo.Fatalf("error loading SMTP: %v", err)
}
s.Name = name
s.SendTimeout *= time.Millisecond
srv = append(srv, s)
srv = append(srv, s)
lo.Printf("loaded SMTP: %s (%s@%s)", s.Name, s.Username, s.Host)
}

View file

@ -5,10 +5,9 @@ import (
"fmt"
"math/rand"
"net/smtp"
"time"
"github.com/jaytaylor/html2text"
"github.com/jordan-wright/email"
"github.com/knadh/smtppool"
)
const emName = "email"
@ -21,21 +20,21 @@ type loginAuth struct {
// Server represents an SMTP server's credentials.
type Server struct {
Name string
Host string `koanf:"host"`
Port int `koanf:"port"`
AuthProtocol string `koanf:"auth_protocol"`
Username string `koanf:"username"`
Password string `koanf:"password"`
EmailFormat string `koanf:"email_format"`
HelloHostname string `koanf:"hello_hostname"`
SendTimeout time.Duration `koanf:"send_timeout"`
MaxConns int `koanf:"max_conns"`
Name string
Username string `json:"username"`
Password string `json:"password"`
AuthProtocol string `json:"auth_protocol"`
EmailFormat string `json:"email_format"`
mailer *email.Pool
// Rest of the options are embedded directly from the smtppool lib.
// The JSON tag is for config unmarshal to work.
smtppool.Opt `json:",squash"`
pool *smtppool.Pool
}
type emailer struct {
// Emailer is the SMTP e-mail messenger.
type Emailer struct {
servers map[string]*Server
serverNames []string
numServers int
@ -43,8 +42,8 @@ type emailer struct {
// NewEmailer creates and returns an e-mail Messenger backend.
// It takes multiple SMTP configurations.
func NewEmailer(srv ...Server) (Messenger, error) {
e := &emailer{
func NewEmailer(srv ...Server) (*Emailer, error) {
e := &Emailer{
servers: make(map[string]*Server),
}
@ -62,18 +61,14 @@ func NewEmailer(srv ...Server) (Messenger, error) {
default:
return nil, fmt.Errorf("unknown SMTP auth type '%s'", s.AuthProtocol)
}
s.Opt.Auth = auth
pool, err := email.NewPool(fmt.Sprintf("%s:%d", s.Host, s.Port), s.MaxConns, auth)
pool, err := smtppool.New(s.Opt)
if err != nil {
return nil, err
}
// Optional SMTP HELLO hostname.
if server.HelloHostname != "" {
pool.SetHelloHostname(server.HelloHostname)
}
s.mailer = pool
s.pool = pool
e.servers[s.Name] = &s
e.serverNames = append(e.serverNames, s.Name)
}
@ -83,12 +78,12 @@ func NewEmailer(srv ...Server) (Messenger, error) {
}
// Name returns the Server's name.
func (e *emailer) Name() string {
func (e *Emailer) Name() string {
return emName
}
// Push pushes a message to the server.
func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byte, atts []*Attachment) error {
func (e *Emailer) Push(fromAddr string, toAddr []string, subject string, m []byte, atts []Attachment) error {
var key string
// If there are more than one SMTP servers, send to a random
@ -100,11 +95,11 @@ func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byt
}
// Are there attachments?
var files []*email.Attachment
var files []smtppool.Attachment
if atts != nil {
files = make([]*email.Attachment, 0, len(atts))
files = make([]smtppool.Attachment, 0, len(atts))
for _, f := range atts {
a := &email.Attachment{
a := smtppool.Attachment{
Filename: f.Name,
Header: f.Header,
Content: make([]byte, len(f.Content)),
@ -120,7 +115,7 @@ func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byt
}
srv := e.servers[key]
em := &email.Email{
em := smtppool.Email{
From: fromAddr,
To: toAddr,
Subject: subject,
@ -137,11 +132,11 @@ func (e *emailer) Push(fromAddr string, toAddr []string, subject string, m []byt
em.Text = []byte(mtext)
}
return srv.mailer.Send(em, srv.SendTimeout)
return srv.pool.Send(em)
}
// Flush flushes the message queue to the server.
func (e *emailer) Flush() error {
func (e *Emailer) Flush() error {
return nil
}

View file

@ -6,8 +6,7 @@ import "net/textproto"
// for instance, e-mail, SMS etc.
type Messenger interface {
Name() string
Push(fromAddr string, toAddr []string, subject string, message []byte, atts []*Attachment) error
Push(fromAddr string, toAddr []string, subject string, message []byte, atts []Attachment) error
Flush() error
}

View file

@ -360,8 +360,8 @@ func handleSelfExportSubscriberData(c echo.Context) error {
[]string{data.Email},
"Your profile data",
msg.Bytes(),
[]*messenger.Attachment{
&messenger.Attachment{
[]messenger.Attachment{
{
Name: fname,
Content: b,
Header: messenger.MakeAttachmentHeader(fname, "base64"),