Remove idle connection timeout handling
Remove error handling for closed connections Add progress watchdog to detect stalled accounts Improve error modal styling and pagination
This commit is contained in:
+15
-17
@@ -18,51 +18,49 @@ type Endpoint struct {
|
||||
|
||||
func (e Endpoint) addr() string { return fmt.Sprintf("%s:%d", e.Host, e.Port) }
|
||||
|
||||
// dialTimeout bounds establishing the TCP connection (matches go-imap's own
|
||||
// default). The subsequent idleReadTimeout governs reads once connected.
|
||||
// dialTimeout bounds establishing the TCP connection.
|
||||
const dialTimeout = 30 * time.Second
|
||||
|
||||
// dialOnce establishes one connection and returns a ready *Client whose reads
|
||||
// are guarded by idleReadTimeout. Unlike imapclient.Dial*, the underlying TCP
|
||||
// conn is wrapped in idleConn so a server that stops responding mid-command
|
||||
// unblocks the read instead of hanging forever. ctx bounds the TCP dial.
|
||||
// dialOnce establishes one connection and returns a ready *Client. ctx bounds
|
||||
// the TCP dial. We deliberately do NOT impose a socket-level read deadline:
|
||||
// a blanket read deadline can't tell an idle connection (e.g. dst sitting idle
|
||||
// during a long src scan) from one stuck mid-response, and would wrongly close
|
||||
// idle connections. Stall detection is done at the orchestrator level via a
|
||||
// progress watchdog; go-imap's own per-command timeouts bound active commands.
|
||||
func dialOnce(ctx context.Context, ep Endpoint) (*imapclient.Client, error) {
|
||||
d := &net.Dialer{Timeout: dialTimeout}
|
||||
raw, err := d.DialContext(ctx, "tcp", ep.addr())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn := &idleConn{Conn: raw, timeout: idleReadTimeout}
|
||||
|
||||
switch ep.TLSMode {
|
||||
case "ssl":
|
||||
// NextProtos mirrors imapclient.DialTLS's ALPN advertisement.
|
||||
tlsConn := tls.Client(conn, &tls.Config{ServerName: ep.Host, NextProtos: []string{"imap"}})
|
||||
tlsConn := tls.Client(raw, &tls.Config{ServerName: ep.Host, NextProtos: []string{"imap"}})
|
||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||
_ = conn.Close()
|
||||
_ = raw.Close()
|
||||
return nil, err
|
||||
}
|
||||
c := imapclient.New(tlsConn, nil)
|
||||
return waitGreeting(c)
|
||||
return waitGreeting(imapclient.New(tlsConn, nil))
|
||||
case "starttls":
|
||||
opts := &imapclient.Options{TLSConfig: &tls.Config{ServerName: ep.Host}}
|
||||
c, err := imapclient.NewStartTLS(conn, opts)
|
||||
c, err := imapclient.NewStartTLS(raw, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
case "plain":
|
||||
c := imapclient.New(conn, nil)
|
||||
return waitGreeting(c)
|
||||
return waitGreeting(imapclient.New(raw, nil))
|
||||
default:
|
||||
_ = conn.Close()
|
||||
_ = raw.Close()
|
||||
return nil, fmt.Errorf("unknown tls_mode %q", ep.TLSMode)
|
||||
}
|
||||
}
|
||||
|
||||
// waitGreeting blocks for the server's initial greeting so a mute server is
|
||||
// caught at connect time (bounded by idleReadTimeout) rather than at the first
|
||||
// command. NewStartTLS already awaits the greeting during its STARTTLS upgrade.
|
||||
// caught at connect time rather than at the first command. NewStartTLS already
|
||||
// awaits the greeting during its STARTTLS upgrade.
|
||||
func waitGreeting(c *imapclient.Client) (*imapclient.Client, error) {
|
||||
if err := c.WaitGreeting(); err != nil {
|
||||
_ = c.Close()
|
||||
|
||||
Reference in New Issue
Block a user