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 }