Introduce Client wrapper with socket deadline support Add reconnection logic for body read timeouts Implement test cases for underflow scenarios Update orchestrator to handle reconnections
46 lines
1.6 KiB
Go
46 lines
1.6 KiB
Go
package imapx
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
// KeepaliveInterval is how often an otherwise-idle IMAP connection is pinged
|
|
// with NOOP so the server does not drop it.
|
|
//
|
|
// The destination connection sits completely idle for the entire duration of
|
|
// the source-side metadata scan (Pass 1 of CopyFolder), which on a large
|
|
// mailbox runs for many minutes across all folders. With no traffic, the
|
|
// server closes the idle connection; go-imap's reader then tears the client
|
|
// down, and every subsequent APPEND fails with "use of closed network
|
|
// connection" — aborting each folder and copying nothing. A periodic NOOP
|
|
// keeps the connection warm. 60s is well under the idle timeout of any common
|
|
// IMAP server.
|
|
const KeepaliveInterval = 60 * time.Second
|
|
|
|
// Keepalive pings c with a NOOP every interval until ctx is cancelled, keeping
|
|
// an idle connection from being dropped by the server. It is meant to run in
|
|
// its own goroutine.
|
|
//
|
|
// It is safe to run concurrently with other commands on c: go-imap serializes
|
|
// command submission and supports multiple in-flight commands over a single
|
|
// connection (RFC 9051 §5.5 pipelining). A NOOP issued while another command is
|
|
// in flight simply queues behind it and completes when the server responds.
|
|
//
|
|
// Keepalive returns when ctx is done or when a NOOP fails — a failed NOOP means
|
|
// the connection is already gone, so there is nothing left to keep alive.
|
|
func Keepalive(ctx context.Context, c *Client, interval time.Duration) {
|
|
t := time.NewTicker(interval)
|
|
defer t.Stop()
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-t.C:
|
|
if err := c.Noop().Wait(); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|