impl connection recovery

This commit is contained in:
iceBear67
2026-08-15 17:31:35 +08:00
parent e63a34d53a
commit 7bd84af48d
33 changed files with 3858 additions and 200 deletions
+33
View File
@@ -26,6 +26,7 @@ type blackholeRelay struct {
addr string
backend string
gen atomic.Uint64
down atomic.Bool
mu sync.Mutex
held []net.Conn
}
@@ -59,6 +60,10 @@ func newBlackholeRelay(t *testing.T, backend string) *blackholeRelay {
func (r *blackholeRelay) handle(cli net.Conn) {
born := r.gen.Load()
if r.down.Load() {
_ = cli.Close()
return
}
up, err := net.Dial("tcp", r.backend)
if err != nil {
_ = cli.Close()
@@ -89,6 +94,34 @@ func (r *blackholeRelay) handle(cli net.Conn) {
// unaffected.
func (r *blackholeRelay) blackhole() { r.gen.Add(1) }
// dropAll hard-resets every currently-established pair: a real FIN/RST reaches
// both ends immediately, as when a middlebox is restarted or a route flaps,
// rather than the silent stranding blackhole models. Later connections are
// carried normally.
//
// stop takes the relay out of service: new connections are refused rather than
// carried, so the tunnel stays down until restore is called. Models an outage
// the client cannot immediately reconnect through.
func (r *blackholeRelay) stop() { r.down.Store(true) }
// restore puts the relay back in service. Flows stranded before it are still
// dead — only fresh connections are carried, which is what a client gets after
// a middlebox or upstream link comes back.
func (r *blackholeRelay) restore() { r.down.Store(false) }
// This is the fast path into stream resumption: the client learns the conn is
// gone at once instead of waiting out a heartbeat timeout.
func (r *blackholeRelay) dropAll() {
r.gen.Add(1)
r.mu.Lock()
held := r.held
r.held = nil
r.mu.Unlock()
for _, c := range held {
_ = c.Close()
}
}
// TestBlackholedPathRecovers is the end-to-end regression guard for the
// stability bug this hardening was written for: with the tunnel's path silently
// dropped, the client used to notice nothing at all. Its read loops parked