Fix Connection timeout

This commit is contained in:
2026-08-16 19:18:03 -03:00
parent c8e3011f21
commit 2ff2cdf0c5
8 changed files with 235 additions and 14 deletions
+40 -5
View File
@@ -6,6 +6,7 @@ import (
"io"
"net"
"sync"
"sync/atomic"
"time"
"dragontcp/internal/cover"
@@ -163,20 +164,29 @@ func (l *bpLane) single(mode byte, sid wire.SessionID, seq uint64, payload []byt
var lastErr error
for attempt := 0; attempt < 2; attempt++ {
if err := l.ensureLocked(); err != nil {
if isTransportTimeout(err) {
return 0, nil, err
}
lastErr = err
continue
}
reused := l.pc.requests > 0
_ = l.pc.conn.SetDeadline(time.Now().Add(timeout))
if err := writeBPRequest(l.pc.conn, mode, sid, seq, payload, downloadHint, l.coverProfile.HeaderMask, l.coverProfile.Clear); err != nil {
lastErr = err
l.transportFailureLocked(reused)
if isTransportTimeout(err) {
return 0, nil, err
}
lastErr = err
continue
}
status, body, err := readBPResponse(l.pc.conn, sid, mode, seq, l.coverProfile.HeaderMask, l.coverProfile.Clear)
if err != nil {
lastErr = err
l.transportFailureLocked(reused)
if isTransportTimeout(err) {
return 0, nil, err
}
lastErr = err
continue
}
l.pc.requests++
@@ -222,14 +232,20 @@ func (l *bpLane) download(sid wire.SessionID, offset uint64, maxChunk, count int
var lastErr error
for attempt := 0; attempt < 2; attempt++ {
if err := l.ensureLocked(); err != nil {
if isTransportTimeout(err) {
return nil, 0, err
}
lastErr = err
continue
}
reused := l.pc.requests > 0
_ = l.pc.conn.SetDeadline(time.Now().Add(timeout))
if err := writeBPRequest(l.pc.conn, mode, sid, offset, payload, hint, l.coverProfile.HeaderMask, l.coverProfile.Clear); err != nil {
lastErr = err
l.transportFailureLocked(reused)
if isTransportTimeout(err) {
return nil, 0, err
}
lastErr = err
continue
}
@@ -238,8 +254,11 @@ func (l *bpLane) download(sid wire.SessionID, offset uint64, maxChunk, count int
for i := 0; i < responses; i++ {
status, body, err := readBPResponse(l.pc.conn, sid, mode, offset, l.coverProfile.HeaderMask, l.coverProfile.Clear)
if err != nil {
lastErr = err
l.transportFailureLocked(reused)
if isTransportTimeout(err) {
return nil, 0, err
}
lastErr = err
goto retry
}
lastStatus = status
@@ -292,6 +311,7 @@ type bpConn struct {
pipeline int
closeOnce sync.Once
dead atomic.Bool
}
func openBPTunnel(serverAddr, token, targetHost string, targetPort int, opts chunkClientOptions) (net.Conn, error) {
@@ -385,6 +405,9 @@ func (c *bpConn) fillReadBuffer() error {
if c.consumedOffset > c.lastAck {
status, body, err := c.downloadLane.single(bpModeACK, c.sid, c.consumedOffset, nil, 0)
if err != nil {
if isTransportTimeout(err) {
c.dead.Store(true)
}
return err
}
if status == wire.StatusError {
@@ -402,6 +425,10 @@ func (c *bpConn) fillReadBuffer() error {
}
data, status, err := c.downloadLane.download(c.sid, c.downloadOffset, chunk, count)
if err != nil {
if isTransportTimeout(err) {
c.dead.Store(true)
return err
}
old, next := c.downSizer.FailureReason(chunk, err)
if old == next && next == c.opts.minSize {
return err
@@ -462,6 +489,10 @@ func (c *bpConn) Write(p []byte) (int, error) {
n := minInt(size, len(p))
status, body, err := c.uploadLane.single(bpModeUpload, c.sid, c.upOffset+2, p[:n], 0)
if err != nil {
if isTransportTimeout(err) {
c.dead.Store(true)
return total, err
}
old, next := c.upSizer.FailureReason(size, err)
if old == next && next == c.opts.minSize {
return total, err
@@ -485,7 +516,11 @@ func (c *bpConn) Write(p []byte) (int, error) {
func (c *bpConn) Close() error {
c.closeOnce.Do(func() {
_, _, _ = c.downloadLane.single(bpModeACK, c.sid, c.consumedOffset, bpCloseMagic[:], 0)
// A timeout is a hard tunnel boundary. Do not create a fresh physical
// connection only to close a session that has already been abandoned.
if !c.dead.Load() {
_, _, _ = c.downloadLane.single(bpModeACK, c.sid, c.consumedOffset, bpCloseMagic[:], 0)
}
c.uploadLane.Close()
c.downloadLane.Close()
})