fix(imap): retry connection failures with bounded exponential backoff - #761
fix(imap): retry connection failures with bounded exponential backoff#761rodboev wants to merge 26 commits into
Conversation
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
…io#199) Rebuild the connection retry boundary above completed implicit TLS and keep authentication outside the establishment scheduler.
31b4a7e to
cd32371
Compare
roborev: Combined Review (
|
…#199) Track the STARTTLS response and TLS handshake boundary without parsing protocol or TLS payloads, preserving terminal protocol and certificate failures.
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
roborev: Combined Review (
|
|
looking |
Issue kenn-io#199 asks the IMAP client to retry connection-level failures (EOF, reset, timeout) and to leave auth and protocol errors alone. The retry path had grown a stricter policy on top of that: no retry once the server had sent any greeting byte, and for STARTTLS only after the TLS ClientHello was on the wire. Enforcing that took a byte-progress wrapper, a hand-written parser for tagged IMAP responses, TLS record sniffing, and a four-state phase machine with a race-repair branch, none of which the issue needs. A server that sends half a greeting and resets is exactly the transient loss users hit, and refusing to retry there only fails faster. The retry decision now reads: a protocol refusal (IMAP status response, TLS alert, rejected certificate) is terminal; otherwise retry when the returned error or the socket itself reported a transport loss. The socket wrapper survives only because the pinned IMAP client reports a clean close before the greeting as a plain string error with no cause attached. The transient errno table was comparing Windows error numbers on every platform, where 64 and 109 mean unrelated Linux errnos. The Windows values now live in a build-tagged file using the syscall constants. Generated with Claude Code Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
roborev: Combined Review (
|
CI runs a repository check that rejects a test with four or more direct assert or require package calls, asking for a local helper instead. The partial-greeting and BYE-greeting retry tests crossed that line, which failed the lint job on the pull request. Both tests now bind the helpers once at the top, matching the rest of the file. Generated with Claude Code Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
roborev: Combined Review (
|
The transient errno table is now split per platform, and on Windows the syscall package's EPIPE is a placeholder value that Winsock never produces, so it is deliberately absent from the Windows table. The classifier test still wrapped EPIPE and failed on the Windows CI job. The test now wraps an entry from the platform's own table, which is what the classifier consults. Generated with Claude Code Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
roborev: Combined Review (
|
|
Thanks for catching the scope creep here. Marked a couple other PRs draft while I make sure they are right-sized to the issue while maintaining correctness, and going deeper into distilling lessons from that process once they get there. |
IMAP connection establishment now retries transient TCP failures and empty-greeting transport failures in plaintext, implicit-TLS, or STARTTLS mode on a bounded 5s, 15s, 45s schedule. TLS negotiation remains with
crypto/tls, the pinned IMAP client owns greeting and STARTTLS protocol handling, and LOGIN or XOAUTH2 runs once after establishment succeeds.The retry boundary records inbound plaintext IMAP progress and, for STARTTLS, separates the completed protocol response from the following TLS handshake transport state, so encrypted TLS handshake records cannot be mistaken for a partial greeting. A fresh connection is opened only before any inbound server greeting byte, or after a completed STARTTLS response with transient TLS transport loss, each retry emits a warning, and context cancellation interrupts connection work or backoff. Established-session reconnects and command replay retain their existing behavior, and there are no usage or configuration changes.
Closes #199