Fix Connection timeout
This commit is contained in:
@@ -263,20 +263,31 @@ func (l *requestLane) single(mode byte, sid wire.SessionID, seq uint64, payload
|
||||
var lastErr error
|
||||
for attempt := 0; attempt < 2; attempt++ {
|
||||
if err := l.ensureLocked(); err != nil {
|
||||
// A dial timeout is a dead path for this logical tunnel. Do not
|
||||
// spend another timeout replaying the same request.
|
||||
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 := wire.WriteRequestProfileEncoding(l.pc.conn, mode, sid, seq, payload, l.headerMask, l.coverProfile.Clear); err != nil {
|
||||
lastErr = err
|
||||
l.transportFailureLocked(reused)
|
||||
if isTransportTimeout(err) {
|
||||
return 0, nil, err
|
||||
}
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
status, body, err := wire.ReadResponseProfile(l.pc.conn, l.headerMask)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
l.transportFailureLocked(reused)
|
||||
if isTransportTimeout(err) {
|
||||
return 0, nil, err
|
||||
}
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
l.pc.requests++
|
||||
@@ -307,14 +318,20 @@ func (l *requestLane) download(sid wire.SessionID, startOffset, ackOffset uint64
|
||||
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 := wire.WriteRequestProfileEncoding(l.pc.conn, wire.ModeDownload, sid, startOffset, payload, l.headerMask, l.coverProfile.Clear); err != nil {
|
||||
lastErr = err
|
||||
l.transportFailureLocked(reused)
|
||||
if isTransportTimeout(err) {
|
||||
return nil, 0, err
|
||||
}
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -324,8 +341,11 @@ func (l *requestLane) download(sid wire.SessionID, startOffset, ackOffset uint64
|
||||
for i := 0; i < count; i++ {
|
||||
status, body, err := wire.ReadResponseProfile(l.pc.conn, l.headerMask)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
l.transportFailureLocked(reused)
|
||||
if isTransportTimeout(err) {
|
||||
return nil, 0, err
|
||||
}
|
||||
lastErr = err
|
||||
goto retry
|
||||
}
|
||||
lastStatus = status
|
||||
@@ -585,6 +605,7 @@ type chunkConn struct {
|
||||
maxPipeline int
|
||||
|
||||
closeOnce sync.Once
|
||||
dead atomic.Bool
|
||||
}
|
||||
|
||||
// appendChunkParts keeps the single-response fast path zero-copy. For a batch,
|
||||
@@ -755,6 +776,10 @@ func (c *chunkConn) fillReadBuffer() error {
|
||||
minFailures = 0
|
||||
}
|
||||
if err != nil {
|
||||
if isTransportTimeout(err) {
|
||||
c.dead.Store(true)
|
||||
return err
|
||||
}
|
||||
if c.pipeline > c.minPipeline {
|
||||
old := c.pipeline
|
||||
c.pipeline /= 2
|
||||
@@ -829,6 +854,10 @@ func (c *chunkConn) Write(p []byte) (int, error) {
|
||||
n := minInt(size, len(p))
|
||||
status, body, err := c.uploadLane.single(wire.ModeUpload, c.sid, c.upOffset, p[:n])
|
||||
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 {
|
||||
minFailures++
|
||||
@@ -858,9 +887,13 @@ func (c *chunkConn) Write(p []byte) (int, error) {
|
||||
|
||||
func (c *chunkConn) Close() error {
|
||||
c.closeOnce.Do(func() {
|
||||
// Reuse the upload lane rather than dialling a connection just to say
|
||||
// goodbye; that was a second wasted dial per flow.
|
||||
_, _, _ = c.uploadLane.single(wire.ModeClose, c.sid, 0, nil)
|
||||
// A timed-out tunnel is already dead. Do not redial merely to send a
|
||||
// graceful CLOSE for a session that the caller is abandoning.
|
||||
if !c.dead.Load() {
|
||||
// Reuse the upload lane rather than dialling a connection just to say
|
||||
// goodbye; that was a second wasted dial per flow.
|
||||
_, _, _ = c.uploadLane.single(wire.ModeClose, c.sid, 0, nil)
|
||||
}
|
||||
c.uploadLane.Close()
|
||||
c.downloadLane.Close()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user