gomail

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 30, 2024 License: MIT Imports: 18 Imported by: 1

README

gomail

This is a Go package for sending emails. It provides functionality to send emails using the SMTP protocol with advanced features like connection pooling, rate limiting, and TLS support.

Installation

To use the package, you need to have Go installed and Go modules enabled in your project. Then you can add the package to your project by running the following command:

go get -u github.com/mstgnz/gomail

Features

  • SMTP protocol support
  • Connection pooling for performance optimization
  • HTML template support with custom functions
  • File attachments (regular and streaming)
  • Asynchronous email sending
  • Rate limiting
  • TLS support (STARTTLS and Direct TLS)
  • CC and BCC recipients
  • Email preview
  • Configurable timeouts and keep-alive
  • Template caching
  • Comprehensive error handling

Benchmarks

Performance benchmarks on Apple M1:

BenchmarkMailSend-8                    13111    83133 ns/op    14435 B/op    184 allocs/op
BenchmarkMailSendWithAttachments-8     13074    87134 ns/op    19552 B/op    234 allocs/op
BenchmarkMailSendAsync-8               12862    92055 ns/op    14660 B/op    187 allocs/op
  • MailSend: Basic email sending without attachments
  • MailSendWithAttachments: Email sending with file attachments
  • MailSendAsync: Asynchronous email sending

The benchmarks show that:

  • Basic email sending takes ~83μs per operation
  • Adding attachments increases memory allocation by ~35% but only adds ~4μs to operation time
  • Async sending has minimal overhead (~9μs) compared to synchronous sending

All operations maintain efficient memory usage with relatively low allocations.

Basic Usage

package main

import (
    "github.com/mstgnz/gomail"
)

func main() {
    // Create a Mail struct and set the necessary fields
    mail := &Mail{
        From:    "[email protected]",
        Name:    "Sender Name",
        Host:    "smtp.example.com",
        Port:    "587",
        User:    "username",
        Pass:    "password",
    }

    // Send a simple email
    err := mail.SetSubject("Test Email").
        SetContent("This is a test email.").
        SetTo("[email protected]").
        Send()
    if err != nil {
        // Handle error
    }
}

Advanced Features

Connection Pooling
mail := &Mail{
    From:     "[email protected]",
    Name:     "Sender Name",
    Host:     "smtp.example.com",
    Port:     "587",
    User:     "username",
    Pass:     "password",
}

// Set connection pool size (default: 10)
mail.SetPoolSize(20)
HTML Templates with Custom Functions
// Configure template engine
engine := &TemplateEngine{
    BaseDir:    "templates",
    DefaultExt: ".html",
    FuncMap: template.FuncMap{
        "upper": strings.ToUpper,
        "formatDate": func(t time.Time) string {
            return t.Format("2006-01-02")
        },
    },
}
mail.SetTemplateEngine(engine)

// Render template with data
data := map[string]any{
    "Name": "John",
    "Date": time.Now(),
}
err := mail.RenderTemplate("welcome", data)

Example template (templates/welcome.html):

<html>
<body>
    <h1>Welcome {{.Name}}!</h1>
    <p>Today is {{formatDate .Date}}</p>
    <p>Your name in uppercase: {{upper .Name}}</p>
</body>
</html>
TLS Configuration
// STARTTLS configuration
mail.SetTLSConfig(&TLSConfig{
    StartTLS:           true,
    InsecureSkipVerify: false,
    ServerName:         "smtp.example.com",
})

// Direct TLS configuration
mail.SetTLSConfig(&TLSConfig{
    StartTLS:           false,
    InsecureSkipVerify: false,
    ServerName:         "smtp.example.com",
})
Rate Limiting
// Limit to 10 emails per second
mail.SetRateLimit(&RateLimit{
    Enabled:   true,
    PerSecond: 10,
})

// Send multiple emails with rate limiting
for i := 0; i < 100; i++ {
    err := mail.SetSubject(fmt.Sprintf("Email %d", i)).
        SetContent("Rate limited email").
        SetTo("[email protected]").
        Send()
    if err != nil {
        log.Printf("Failed to send email %d: %v", i, err)
    }
}
Asynchronous Email Sending
// Send email asynchronously
result := mail.SetSubject("Test Email").
    SetContent("This is a test email.").
    SetTo("[email protected]").
    SendAsync()

// Check the result
if err := <-result; err != nil {
    log.Printf("Failed to send email: %v", err)
}
Large File Attachments (Streaming)
// Stream a large file
file, err := os.Open("large-file.zip")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

fileInfo, err := file.Stat()
if err != nil {
    log.Fatal(err)
}

attachments := []AttachmentReader{
    {
        Name:   "large-file.zip",
        Reader: file,
        Size:   fileInfo.Size(),
    },
}

err = mail.SetSubject("Email with Large Attachment").
    SetContent("Please find the attached file.").
    SetTo("[email protected]").
    SetStreamAttachment(attachments).
    Send()
Email Preview
// Preview email before sending
preview, err := mail.PreviewEmail()
if err != nil {
    log.Printf("Preview error: %v", err)
    return
}
fmt.Println("Email Preview:")
fmt.Println(preview)
Error Handling
// Basic error handling
err := mail.Send()
if err != nil {
    switch {
    case strings.Contains(err.Error(), "connection refused"):
        log.Printf("SMTP server is not accessible: %v", err)
    case strings.Contains(err.Error(), "invalid auth"):
        log.Printf("Authentication failed: %v", err)
    case strings.Contains(err.Error(), "invalid recipient"):
        log.Printf("Invalid recipient address: %v", err)
    default:
        log.Printf("Failed to send email: %v", err)
    }
}

// Async error handling with timeout
result := mail.SendAsync()
select {
case err := <-result:
    if err != nil {
        log.Printf("Failed to send email: %v", err)
    }
case <-time.After(30 * time.Second):
    log.Printf("Email sending timed out")
}
Template Usage Examples
// 1. Basic Template
engine := &TemplateEngine{
    BaseDir:    "templates",
    DefaultExt: ".html",
}
mail.SetTemplateEngine(engine)

// Basic template usage
data := map[string]any{
    "Name": "John",
    "Products": []string{"Product 1", "Product 2"},
    "Total": 99.99,
}
err := mail.RenderTemplate("order-confirmation", data)

// 2. Custom Template Functions
engine := &TemplateEngine{
    BaseDir:    "templates",
    DefaultExt: ".html",
    FuncMap: template.FuncMap{
        "upper": strings.ToUpper,
        "formatDate": func(t time.Time) string {
            return t.Format("2006-01-02")
        },
        "formatPrice": func(price float64) string {
            return fmt.Sprintf("$%.2f", price)
        },
        "safeHTML": func(s string) template.HTML {
            return template.HTML(s)
        },
    },
}
mail.SetTemplateEngine(engine)

// 3. Template with Layouts
// templates/layouts/base.html
/*
<!DOCTYPE html>
<html>
<head>
    <title>{{.Title}}</title>
</head>
<body>
    <header>{{template "header" .}}</header>
    <main>{{template "content" .}}</main>
    <footer>{{template "footer" .}}</footer>
</body>
</html>
*/

// templates/welcome.html
/*
{{define "header"}}
    <h1>Welcome {{.Name}}</h1>
{{end}}

{{define "content"}}
    <p>Today is {{formatDate .Date}}</p>
    <p>Your total: {{formatPrice .Total}}</p>
{{end}}

{{define "footer"}}
    <p>Contact us: [email protected]</p>
{{end}}
*/
TLS Configuration Examples
// 1. STARTTLS with Gmail
mail.SetTLSConfig(&TLSConfig{
    StartTLS:           true,
    InsecureSkipVerify: false,
    ServerName:         "smtp.gmail.com",
})

// 2. Direct TLS with custom certificates
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
if err != nil {
    log.Fatal(err)
}

mail.SetTLSConfig(&TLSConfig{
    StartTLS:           false,
    InsecureSkipVerify: false,
    ServerName:         "smtp.example.com",
    Certificates:       []tls.Certificate{cert},
})

// 3. TLS with custom verification
mail.SetTLSConfig(&TLSConfig{
    StartTLS:           true,
    InsecureSkipVerify: false,
    ServerName:         "smtp.example.com",
    MinVersion:         tls.VersionTLS12,
    MaxVersion:         tls.VersionTLS13,
})

// 4. Development/Testing TLS (insecure)
mail.SetTLSConfig(&TLSConfig{
    StartTLS:           true,
    InsecureSkipVerify: true, // Only for development!
    ServerName:         "localhost",
})
Rate Limiting Examples
// 1. Basic Rate Limiting
mail.SetRateLimit(&RateLimit{
    Enabled:   true,
    PerSecond: 10, // 10 emails per second
})

// 2. Burst Sending with Rate Limiting
emails := []struct {
    to      string
    subject string
    content string
}{
    {"[email protected]", "Subject 1", "Content 1"},
    {"[email protected]", "Subject 2", "Content 2"},
    // ... more emails
}

// Configure rate limiting
mail.SetRateLimit(&RateLimit{
    Enabled:   true,
    PerSecond: 5, // 5 emails per second
})

// Send emails with progress tracking
for i, email := range emails {
    err := mail.SetSubject(email.subject).
        SetContent(email.content).
        SetTo(email.to).
        Send()
    
    if err != nil {
        log.Printf("Failed to send email %d: %v", i+1, err)
        continue
    }
    
    log.Printf("Progress: %d/%d emails sent", i+1, len(emails))
}

// 3. Async Sending with Rate Limiting
results := make(chan error, len(emails))
for _, email := range emails {
    go func(e struct {
        to      string
        subject string
        content string
    }) {
        results <- mail.SetSubject(e.subject).
            SetContent(e.content).
            SetTo(e.to).
            Send()
    }(email)
}

// Collect results
for i := 0; i < len(emails); i++ {
    if err := <-results; err != nil {
        log.Printf("Email error: %v", err)
    }
}
Error Handling Examples
// 1. Comprehensive Error Handling
err := mail.Send()
if err != nil {
    switch {
    case strings.Contains(err.Error(), "connection refused"):
        log.Printf("SMTP server is not accessible: %v", err)
        // Retry with backup server
        mail.SetHost("backup-smtp.example.com")
        err = mail.Send()
        
    case strings.Contains(err.Error(), "invalid auth"):
        log.Printf("Authentication failed: %v", err)
        // Refresh credentials and retry
        mail.SetUser("new-user").SetPass("new-pass")
        err = mail.Send()
        
    case strings.Contains(err.Error(), "invalid recipient"):
        log.Printf("Invalid recipient address: %v", err)
        // Log invalid address for cleanup
        logInvalidAddress(mail.To[0])
        
    case strings.Contains(err.Error(), "timeout"):
        log.Printf("Connection timeout: %v", err)
        // Retry with increased timeout
        mail.SetTimeout(30 * time.Second)
        err = mail.Send()
        
    default:
        log.Printf("Unexpected error: %v", err)
    }
}

// 2. Retry Logic
maxRetries := 3
retryDelay := time.Second

for i := 0; i < maxRetries; i++ {
    err := mail.Send()
    if err == nil {
        break
    }
    
    log.Printf("Attempt %d failed: %v", i+1, err)
    if i < maxRetries-1 {
        time.Sleep(retryDelay * time.Duration(i+1))
    }
}

// 3. Async Error Handling with Context
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

result := mail.SendAsync()
select {
case err := <-result:
    if err != nil {
        log.Printf("Failed to send email: %v", err)
    }
case <-ctx.Done():
    log.Printf("Email sending timed out or cancelled")
case <-time.After(5 * time.Second):
    log.Printf("Email sending took too long")
}

// 4. Batch Error Handling
type EmailResult struct {
    To    string
    Error error
}

results := make(chan EmailResult, len(recipients))
for _, to := range recipients {
    go func(recipient string) {
        err := mail.SetTo(recipient).Send()
        results <- EmailResult{To: recipient, Error: err}
    }(to)
}

// Process results
successCount := 0
failureCount := 0
for i := 0; i < len(recipients); i++ {
    result := <-results
    if result.Error != nil {
        failureCount++
        log.Printf("Failed to send to %s: %v", result.To, result.Error)
    } else {
        successCount++
    }
}

log.Printf("Sending complete: %d successful, %d failed", successCount, failureCount)

Contributing

Contributions are welcome! For any feedback, bug reports, or contributions, please submit an issue or pull request to the GitHub repository.

License

This package is licensed under the MIT License. See the LICENSE file for more information.

Documentation

Overview

Package gomail provides a simple and efficient way to send emails using SMTP protocol. It supports various features including HTML templates, attachments, connection pooling, rate limiting, TLS configuration, and asynchronous sending.

Basic Usage:

mail := &gomail.Mail{
	From:    "[email protected]",
	Name:    "Sender Name",
	Host:    "smtp.example.com",
	Port:    "587",
	User:    "username",
	Pass:    "password",
}

err := mail.SetSubject("Test Email").
	SetContent("This is a test email.").
	SetTo("[email protected]").
	Send()

Features:

  • Connection pooling for better performance
  • HTML template support with caching
  • File attachments (regular and streaming)
  • Asynchronous sending
  • TLS support (STARTTLS and Direct TLS)
  • CC and BCC recipients
  • Rate limiting
  • Email preview
  • Configurable timeouts and keep-alive
  • Custom template functions

Connection Pooling:

The package implements connection pooling to reuse SMTP connections:

mail.SetPoolSize(5) // Set pool size to 5 connections

HTML Templates:

Send emails using HTML templates with support for custom functions:

engine := &TemplateEngine{
	BaseDir:     "templates",
	DefaultExt:  ".html",
	FuncMap:     template.FuncMap{
		"upper": strings.ToUpper,
	},
}
mail.SetTemplateEngine(engine)
mail.RenderTemplate("welcome", data)

Rate Limiting:

Control email sending rate:

mail.SetRateLimit(&RateLimit{
	Enabled:   true,
	PerSecond: 2, // 2 emails per second
})

TLS Configuration:

Configure TLS settings:

mail.SetTLSConfig(&TLSConfig{
	StartTLS:           true,
	InsecureSkipVerify: false,
	ServerName:         "smtp.example.com",
})

Asynchronous Sending:

Send emails asynchronously:

result := mail.SetSubject("Async Email").
	SetContent("Content").
	SetTo("[email protected]").
	SendAsync()

if err := <-result; err != nil {
	log.Printf("Failed to send: %v", err)
}

Attachments:

Add file attachments:

attachments := map[string][]byte{
	"file.txt": []byte("content"),
}
mail.SetAttachment(attachments)

For large files, use streaming attachments:

file, _ := os.Open("large-file.zip")
defer file.Close()

attachments := []AttachmentReader{
	{
		Name:   "large-file.zip",
		Reader: file,
		Size:   fileInfo.Size(),
	},
}
mail.SetStreamAttachment(attachments)

Email Preview:

Preview email content before sending:

preview, err := mail.PreviewEmail()
if err != nil {
	log.Printf("Preview error: %v", err)
}
fmt.Println(preview)

Index

Constants

View Source
const (
	DefaultTimeout   = 30 * time.Second
	DefaultKeepAlive = 60 * time.Second
	DefaultPoolSize  = 10
)

Default configuration

Variables

This section is empty.

Functions

func SimpleRenderTemplate added in v1.2.0

func SimpleRenderTemplate(filePath string, data map[string]any) (string, error)

SimpleRenderTemplate renders a simple HTML template with dynamic data

Types

type Attachment added in v1.2.0

type Attachment struct {
	Name        string
	ContentType string
	Data        []byte
	Inline      bool
}

Attachment represents an email attachment with metadata

type AttachmentReader added in v1.2.0

type AttachmentReader struct {
	Name   string
	Reader io.Reader
	Size   int64
}

AttachmentReader represents a streaming attachment

type ContentType added in v1.2.0

type ContentType string

ContentType represents email content type

const (
	TextPlain    ContentType = "text/plain"
	TextHTML     ContentType = "text/html"
	TextMarkdown ContentType = "text/markdown"
)

type Mail

type Mail struct {
	From        string
	Name        string
	Host        string
	Port        string
	User        string
	Pass        string `json:"-"` // Password will be omitted from JSON
	Subject     string
	Content     string
	To          []string
	Cc          []string
	Bcc         []string
	Attachments map[string][]byte
	Timeout     time.Duration
	KeepAlive   time.Duration

	ContentType    ContentType
	TemplateEngine *TemplateEngine
	// contains filtered or unexported fields
}

Mail represents an email message with all its configuration

func (*Mail) PreviewEmail added in v1.2.0

func (m *Mail) PreviewEmail() (string, error)

PreviewEmail returns a preview of the email content

func (*Mail) RenderTemplate added in v1.2.0

func (m *Mail) RenderTemplate(name string, data any) error

RenderTemplate renders a template with the given data

func (*Mail) Send added in v1.2.0

func (m *Mail) Send() error

Send initiates the email sending process

func (*Mail) SendAsync added in v1.2.0

func (m *Mail) SendAsync() chan error

SendAsync sends the email asynchronously and returns a channel for the result

func (*Mail) SendHtml added in v1.2.0

func (m *Mail) SendHtml(filePath string, data map[string]any) error

SendFile loads an HTML file and renders it with dynamic data

func (*Mail) SetAttachment

func (m *Mail) SetAttachment(attachments map[string][]byte) *Mail

SetAttachment sets the email attachments

func (*Mail) SetBcc

func (m *Mail) SetBcc(bcc ...string) *Mail

SetBcc sets the email BCC recipients

func (*Mail) SetCc

func (m *Mail) SetCc(cc ...string) *Mail

SetCc sets the email CC recipients

func (*Mail) SetContent

func (m *Mail) SetContent(content string) *Mail

SetContent sets the email content

func (*Mail) SetContentType added in v1.2.0

func (m *Mail) SetContentType(contentType ContentType) *Mail

SetContentType sets the content type of the email

func (*Mail) SetFrom

func (m *Mail) SetFrom(from string) *Mail

SetFrom sets the sender's email address

func (*Mail) SetHost

func (m *Mail) SetHost(host string) *Mail

SetHost sets the SMTP server host

func (*Mail) SetKeepAlive

func (m *Mail) SetKeepAlive(keepAlive time.Duration) *Mail

SetKeepAlive sets the keep-alive duration

func (*Mail) SetName

func (m *Mail) SetName(name string) *Mail

SetName sets the sender's name

func (*Mail) SetPass

func (m *Mail) SetPass(pass string) *Mail

SetPass sets the SMTP server password

func (*Mail) SetPoolSize added in v1.2.0

func (m *Mail) SetPoolSize(size int) *Mail

SetPoolSize sets the connection pool size

func (*Mail) SetPort

func (m *Mail) SetPort(port string) *Mail

SetPort sets the SMTP server port

func (*Mail) SetRateLimit added in v1.2.0

func (m *Mail) SetRateLimit(limit *RateLimit) *Mail

SetRateLimit configures rate limiting

func (*Mail) SetStreamAttachment added in v1.2.0

func (m *Mail) SetStreamAttachment(attachments []AttachmentReader) *Mail

SetStreamAttachment sets streaming attachments for the email

func (*Mail) SetSubject

func (m *Mail) SetSubject(subject string) *Mail

SetSubject sets the email subject

func (*Mail) SetTLSConfig added in v1.2.0

func (m *Mail) SetTLSConfig(config *TLSConfig) *Mail

SetTLSConfig sets the TLS configuration

func (*Mail) SetTemplateEngine added in v1.2.0

func (m *Mail) SetTemplateEngine(engine *TemplateEngine) *Mail

SetTemplateEngine configures the template engine

func (*Mail) SetTimeout

func (m *Mail) SetTimeout(timeout time.Duration) *Mail

SetTimeout sets the timeout duration

func (*Mail) SetTo

func (m *Mail) SetTo(to ...string) *Mail

SetTo sets the email recipients

func (*Mail) SetUser

func (m *Mail) SetUser(user string) *Mail

SetUser sets the SMTP server username

type Pool added in v1.2.0

type Pool struct {
	// contains filtered or unexported fields
}

Pool structure

func NewPool added in v1.2.0

func NewPool(config *Mail, size int) (*Pool, error)

NewPool creates a new connection pool

func (*Pool) Close added in v1.2.0

func (p *Pool) Close()

Close the pool and all its connections

type RateLimit added in v1.2.0

type RateLimit struct {
	Enabled   bool
	PerSecond int
}

RateLimit represents rate limiting configuration

type TLSConfig added in v1.2.0

type TLSConfig struct {
	StartTLS           bool
	InsecureSkipVerify bool
	ServerName         string
	Certificates       []tls.Certificate
}

TLSConfig represents TLS configuration options

type TemplateEngine added in v1.2.0

type TemplateEngine struct {
	BaseDir    string
	DefaultExt string
	FuncMap    template.FuncMap
}

TemplateEngine represents template engine configuration

Directories

Path Synopsis
examples
basic command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL