smtppool

repository·master·Indexed 41 days ago

https://github.com/knadh/smtppool

A Go library for high-throughput emailing that maintains a pool of reusable SMTP connections. It provides automatic connection management, including idle timeouts, retries, and connection limits, and includes a comprehensive Email struct for constructing RFC-compliant MIME messages with support for HTML, attachments, and custom headers.

Tokens
3.3K
Snippets
15
Records
16
Agent score
52%

What's inside smtppool

  1. Configure SSL/TLS connection types

    master

    The SSL field in Opt determines how the connection to the SMTP server is established using the SSLType enum:

    • SSLNone: A plain unencrypted connection (default).
    • SSLTLS: An SSL (TLS) connection established immediately without using the STARTTLS extension.
    • SSLSTARTTLS: A non-TLS connection that is upgraded to TLS using the STARTTLS extension.
    // Example: Using STARTTLS
    opt := smtppool.Opt{
        Host: "smtp.example.com",
        Port: 587,
        SSL:  smtppool.SSLSTARTTLS,
        TLSConfig: &tls.Config{ /* ... */ },
    }
  2. Send emails using smtppool

    master

    To send an email, define an smtppool.Email struct and pass it to the pool.Send() method. The Email struct supports standard fields like From, To, Bcc, Cc, Subject, Text, and HTML. You can also attach files using the AttachFile method.

    package main
    
    import (
    	"log"
    	"time"
    
    	"github.com/knadh/smtppool/v2"
    )
    
    func main() {
    	// Create a new pool.
    	pool, err := smtppool.New(smtppool.Opt{
    		Host:            "localhost",
    		Port:            1025,
    		MaxConns:        10,
    		IdleTimeout:     time.Second * 10,
    		PoolWaitTimeout: time.Second * 3,
    		SSL:             smtppool.SSLNone
    	})
    	if err != nil {
    		log.Fatalf("error creating pool: %v", err)
    	}
    
    	e := smtppool.Email{
    		From:    "John Doe <john@example.com>",
    		To:      []string{"doe@example.com"},
    
    		// Optional.
    		Bcc:     []string{"doebcc@example.com"},
    		Cc:      []string{"doecc@example.com"},
    
    		Subject: "Hello, World",
    		Text:    []byte("This is a test e-mail"),
    		HTML:    []byte("<strong>This is a test e-mail</strong>"),
    	}
    
    	// Add attachments.
    	if _, err := e.AttachFile("test.txt"); err != nil {
    		log.Fatalf("error attaching file: %v", err)
    	}
    
    	if err := pool.Send(e); err != nil {
    		log.Fatalf("error sending e-mail: %v", err)
    	}
    }
  3. Create an SMTP connection pool

    master

    Use smtppool.New to initialize a pool of reusable SMTP connections. This pool manages connection lifecycles, including idle timeouts and maximum connection limits, to support high-throughput emailing.

    Configuration is provided via the smtppool.Opt struct.

    import (
    	"time"
    	"github.com/knadh/smtppool/v2"
    )
    
    // ...
    
    pool, err := smtppool.New(smtppool.Opt{
    	Host:            "localhost",
    	Port:            1025,
    	MaxConns:        10,
    	IdleTimeout:     time.Second * 10,
    	PoolWaitTimeout: time.Second * 3,
    	SSL:             smtppool.SSLNone
    })
  4. Configure SMTP Pool options

    master

    The Opt struct defines the configuration for the SMTP pool. Use these fields to control connection behavior, security, and reliability.

    FieldTypeDescription
    HoststringThe SMTP server's hostname.
    PortintThe SMTP server port.
    HelloHostnamestringOptional hostname passed with the HELO command. Defaults to "localhost".
    MaxConnsintMaximum allowed concurrent SMTP connections.
    MaxMessageRetriesintNumber of retries for a failed message. Min is 1.
    MessageRetryDelaytime.DurationDuration to wait before retrying a failed message.
    IdleTimeouttime.DurationMax time to wait for activity on a connection before closing it.
    PoolWaitTimeouttime.DurationMax time to wait to obtain a connection or create a new one.
    SSLSSLTypeConnection security type (SSLNone, SSLTLS, or SSLSTARTTLS).
    Authsmtp.AuthThe smtp.Auth authentication scheme.
    TLSConfig*tls.ConfigOptional TLS configuration.
  5. Close the SMTP Pool

    master

    Call Close() on the *Pool to shut down the pool. This marks the pool as closed, prevents new connections from being borrowed, and signals the idle connection sweeper to stop. Any subsequent calls to Send() will return ErrPoolClosed.

    pool.Close()
  6. Initialize an SMTP Pool with New()

    master

    Use New(o Opt) to create a new *Pool instance. The pool manages a set of reusable SMTP connections to improve throughput.

    Important Defaults & Constraints:

    • MaxConns must be $\ge 1$.
    • If MaxMessageRetries is 0, it defaults to 1.
    • If PoolWaitTimeout is less than 1 second, it defaults to 2 seconds.
    • If IdleTimeout is $\ge 1$ second and MaxConns > 1, an internal connection sweeper goroutine is started automatically to clean up idle connections.
    import "github.com/knadh/smtppool"
    
    opt := smtppool.Opt{
        Host: "smtp.example.com",
        Port: 587,
        MaxConns: 10,
    }
    
    pool, err := smtppool.New(opt)
    if err != nil {
        panic(err)
    }
  7. Parse an email from an io.Reader

    master

    Use NewEmailFromReader(r io.Reader) to parse an existing email stream (in RFC 5322 format) into an Email struct. This function automatically decodes headers, parses MIME parts, and populates the Text, HTML, and Attachments fields.

    email, err := smtppool.NewEmailFromReader(reader)
    if err != nil {
    	// handle error
    }
    fmt.Println("Subject:", email.Subject)
  8. Attach files to an Email

    master

    You can add attachments to an Email instance using two methods:

    1. Attach(r io.Reader, filename string, c string): Attaches content from an io.Reader. If the content type c is empty, it defaults to application/octet-stream.
    2. AttachFile(filename string): Automatically opens a file from the local filesystem, determines its MIME type based on the extension, and attaches it.

    Both methods return the created Attachment object.

    // Attach from a reader
    email.Attach(bytes.NewReader(data), "document.pdf", "application/pdf")
    
    // Attach from a file
    email.AttachFile("/path/to/image.png")
  9. Send an email with Send()

    master

    The Send(e Email) method sends an email using an available connection from the pool.

    Behavior:

    • It automatically borrows a connection from the pool.
    • If sending fails due to a network-related error (e.g., TCP/DNS/timeout/broken pipe), it will retry the message on a new connection up to MaxMessageRetries times.
    • If a connection encounters a non-retriable error, it is closed and removed from the pool.
    • If successful, the connection is reset (RSET) and returned to the pool for reuse.
    // Assuming 'pool' is an initialized *smtppool.Pool
    // and 'email' is an implementation of the smtppool.Email interface
    err := pool.Send(email)
    if err != nil {
        // Handle error
    }
  10. Convert an Email to bytes for sending

    master

    The Bytes() method converts the Email struct into a complete, RFC-compliant MIME-encoded []byte slice. This includes all necessary headers, boundaries for multipart messages (alternative, mixed, or related), and base64 encoding for attachments. This byte slice is what you would typically pass to an SMTP client to send the message.

    data, err := email.Bytes()
    if err != nil {
    	// handle error
    }
    // 'data' is now ready to be sent via SMTP
  11. Email MIME Content-Type constants

    master

    The following constants define standard MIME content types used by the package:

    • ContentTypePlain: text/plain
    • ContentTypeHTML: text/html
    • ContentTypeOctetStream: application/octet-stream
    • ContentTypeMultipartAlt: multipart/alternative
    • ContentTypeMultipartMixed: multipart/mixed
    • ContentTypeMultipartRelated: multipart/related
    const (
    	ContentTypePlain            = "text/plain"
    	ContentTypeHTML             = "text/html"
    	ContentTypeOctetStream      = "application/octet-stream"
    	ContentTypeMultipartAlt     = "multipart/alternative"
    	ContentTypeMultipartMixed   = "multipart/mixed"
    	ContentTypeMultipartRelated = "multipart/related"
    )