Add client reconnect, Happy Eyeballs, and clean disconnect handling - #32
Merged
Merged
Conversation
Reliability and UX overhaul of the client/server lifecycle. Previously the client exited on the first disconnect, blocked v6-first DNS against v4-only servers for the full QUIC handshake timeout, and the server silently leaked reverse-tunnel listeners when clients went away. Now: * Client reconnects automatically with exponential backoff (200 ms → 300 s by default, configurable via --max-retry-count and --max-retry-interval, mirroring chisel). The same loop covers initial connect failures so clients started before the server is up keep retrying until it appears. * Client races every resolved address with RFC 8305 Happy Eyeballs v2: parse_server_addr collects all candidates and reorders them by alternating address families, the client maintains one quinn endpoint per family lazily, and connect attempts are staggered by the spec-recommended 250 ms Connection Attempt Delay. Fixes the common "macOS localhost resolves to ::1 first, server is on 0.0.0.0" failure mode that previously blocked for ~30 s. * Server installs a ^C handler that gracefully closes the QUIC endpoint with reason "server received ^C", so connected clients log the close immediately and proceed into the reconnect loop instead of waiting out the idle timeout. * Server's per-connection accept loop decodes every ConnectionError variant into a human-readable INFO log; previously even a clean client shutdown produced zero server-side output (the message was at debug!). * Server scopes per-tunnel work to a tokio::task::JoinSet whose shutdown().await fires the moment the connection ends, releasing reverse-tunnel listener ports immediately. New regression test in tests/disconnect_cleanup.rs asserts the listener is rebindable within ~600 ms of a client connection.close(). * Client briefly waits for quinn to flush the CONNECTION_CLOSE frame before tearing down the endpoint on shutdown, so the server reliably sees the close instead of racing wait_idle. Public API changes: * ClientConfig gains a `reconnect: ReconnectConfig` field. * ServerEndpoint.addr (SocketAddr) is now addrs (Vec<SocketAddr>) plus a primary() accessor. * create_client_endpoint takes the resolved server SocketAddr so it can pick the matching bind family. Tests: * tests/reconnect.rs covers initial-connect-before-server-up and server-restart-mid-session scenarios. * tests/disconnect_cleanup.rs is the JoinSet-abort regression. * Three unit tests in main.rs for the address-family interleaving. Made-with: Cursor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reliability and UX overhaul of the client/server lifecycle. Previously the client exited on the first disconnect, blocked v6-first DNS against v4-only servers for the full QUIC handshake timeout, and the server silently leaked reverse-tunnel listeners when clients went away. Now:
Public API changes:
reconnect: ReconnectConfigfield.Tests: