48 lines
1.7 KiB
Go
48 lines
1.7 KiB
Go
package imapx
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/emersion/go-imap/v2/imapclient"
|
|
)
|
|
|
|
// 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 *imapclient.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
|
|
}
|
|
}
|
|
}
|
|
}
|