Files
mailgoserver/internal/imapserver/idletimeout.go
T

49 lines
1.4 KiB
Go

package imapserver
import (
"net"
"time"
)
// IdleTimeoutListener wraps a listener so every accepted connection gets a rolling
// deadline extended by d on each read/write. Unlike SMTP (go-smtp's Server.ReadTimeout/
// WriteTimeout), go-imap/v2's imapserver.Options has no timeout knob at all, so an idle
// or slow-drip connection can otherwise hold a goroutine (and a file descriptor) open
// indefinitely. 30 minutes (the caller's chosen d) matches RFC 2177's guidance that an
// IDLE-capable client re-issue IDLE at least that often, so a real IDLE session renews
// its own deadline in time and is never cut off by this.
func IdleTimeoutListener(inner net.Listener, d time.Duration) net.Listener {
return &idleTimeoutListener{Listener: inner, d: d}
}
type idleTimeoutListener struct {
net.Listener
d time.Duration
}
func (l *idleTimeoutListener) Accept() (net.Conn, error) {
conn, err := l.Listener.Accept()
if err != nil {
return nil, err
}
conn.SetDeadline(time.Now().Add(l.d))
return &idleTimeoutConn{Conn: conn, d: l.d}, nil
}
type idleTimeoutConn struct {
net.Conn
d time.Duration
}
func (c *idleTimeoutConn) Read(b []byte) (int, error) {
n, err := c.Conn.Read(b)
c.Conn.SetDeadline(time.Now().Add(c.d))
return n, err
}
func (c *idleTimeoutConn) Write(b []byte) (int, error) {
n, err := c.Conn.Write(b)
c.Conn.SetDeadline(time.Now().Add(c.d))
return n, err
}