Neues Paket mail/internal/protoguard kapselt die für IMAP- und POP3-Sessions gemeinsam benötigte Timeout- und Backoff-Logik einer einzelnen Verbindung: - Pro Protokollphase konfigurierbarer Idle-Read-Timeout (POP3: Authorization/Transaction, IMAP: NotAuthenticated/Selected), vor jedem Lesevorgang neu gesetzt. - Sich verdoppelnder Backoff bei wiederholten Anmeldefehlversuchen einer Verbindung (BackoffBase bis BackoffMax), Verbindungstrennung nach konfigurierbarer Höchstzahl statt Dauerschleife. Server.NewServer bleibt unverändert (Standardkonfiguration); NewServerWithGuardConfig erlaubt abweichende Werte. Ressourcenaufräumung bei Verbindungsabbruch war bereits durch defer conn.Close() strukturell gegeben — der Timeout sorgt dafür, dass dieser Pfad auch bei hängenden oder böswilligen Gegenstellen zuverlässig erreicht wird. Alle drei Pflichtprüfungen mit echten Nachweisen durchgeführt: Chaos-Test mit 30 hart gekappten Verbindungen während aktiver Übertragung (kein Goroutine-Leck), Timeout-Auslösung in jeder Protokollphase beider Server, steigender Backoff mit definierter Verbindungstrennung nach Höchstzahl an Fehlversuchen. go build/go vet/golangci-lint clean, gesamtes Mail-Modul (~24 Pakete) regressionsfrei getestet.
56 lines
1.4 KiB
Go
56 lines
1.4 KiB
Go
package pop3
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
|
|
"gitea.perlbach24.de/scripte/nexarch/mail/internal/protoguard"
|
|
)
|
|
|
|
// Server nimmt POP3-Verbindungen an und bedient jede in einer eigenen
|
|
// Goroutine (Akzeptanzkriterium 1) — gleiches Muster wie
|
|
// mail/internal/imap.Server. TLS/STARTTLS ist Sache von ING-06, nicht
|
|
// dieser Kachel.
|
|
type Server struct {
|
|
auth Authenticator
|
|
store MailboxStore
|
|
guardCfg protoguard.Config
|
|
}
|
|
|
|
func NewServer(auth Authenticator, store MailboxStore) *Server {
|
|
return NewServerWithGuardConfig(auth, store, protoguard.DefaultConfig())
|
|
}
|
|
|
|
// NewServerWithGuardConfig erlaubt abweichende Phase-Timeouts und
|
|
// Backoff-Parameter (ING-07), z. B. für Tests oder gehärtete
|
|
// Betriebsumgebungen.
|
|
func NewServerWithGuardConfig(auth Authenticator, store MailboxStore, guardCfg protoguard.Config) *Server {
|
|
return &Server{auth: auth, store: store, guardCfg: guardCfg}
|
|
}
|
|
|
|
// Serve nimmt Verbindungen auf listener an, bis ctx beendet wird.
|
|
func (srv *Server) Serve(ctx context.Context, listener net.Listener) error {
|
|
go func() {
|
|
<-ctx.Done()
|
|
_ = listener.Close()
|
|
}()
|
|
|
|
for {
|
|
conn, err := listener.Accept()
|
|
if err != nil {
|
|
if ctx.Err() != nil {
|
|
return nil
|
|
}
|
|
var netErr net.Error
|
|
if errors.As(err, &netErr) && netErr.Timeout() {
|
|
continue
|
|
}
|
|
return fmt.Errorf("pop3: verbindung annehmen: %w", err)
|
|
}
|
|
session := newSession(conn, srv.auth, srv.store, srv.guardCfg)
|
|
go session.Serve(ctx)
|
|
}
|
|
}
|