Files
imap-copier/internal/imapx/keepalive.go
T
vasyansk 6bf3a6c4ca Add body read timeout recovery to IMAP client
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
2026-07-21 05:29:35 +07:00

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
}
}
}
}