49 lines
1.4 KiB
Go
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
|
||
|
|
}
|