Skip to content

Commit ef20fe5

Browse files
committed
http2: make Transport.IdleConnTimeout consider wall (not monotonic) time
This is the http2 version of CL 204797. Updates golang/go#29308 (fixes once bundled into std) Change-Id: I7cd97d38c941e9a8a62808e23b6533c72760f003 Reviewed-on: https://go-review.googlesource.com/c/net/+/208798 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Bryan C. Mills <[email protected]>
1 parent ffdde10 commit ef20fe5

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

http2/transport.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ type ClientConn struct {
227227
br *bufio.Reader
228228
fr *Framer
229229
lastActive time.Time
230+
lastIdle time.Time // time last idle
230231
// Settings from peer: (also guarded by mu)
231232
maxFrameSize uint32
232233
maxConcurrentStreams uint32
@@ -736,7 +737,8 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) {
736737
}
737738

738739
st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay &&
739-
int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32
740+
int64(cc.nextStreamID)+2*int64(cc.pendingRequests) < math.MaxInt32 &&
741+
!cc.tooIdleLocked()
740742
st.freshConn = cc.nextStreamID == 1 && st.canTakeNewRequest
741743
return
742744
}
@@ -746,6 +748,16 @@ func (cc *ClientConn) canTakeNewRequestLocked() bool {
746748
return st.canTakeNewRequest
747749
}
748750

751+
// tooIdleLocked reports whether this connection has been been sitting idle
752+
// for too much wall time.
753+
func (cc *ClientConn) tooIdleLocked() bool {
754+
// The Round(0) strips the monontonic clock reading so the
755+
// times are compared based on their wall time. We don't want
756+
// to reuse a connection that's been sitting idle during
757+
// VM/laptop suspend if monotonic time was also frozen.
758+
return cc.idleTimeout != 0 && !cc.lastIdle.IsZero() && time.Since(cc.lastIdle.Round(0)) > cc.idleTimeout
759+
}
760+
749761
// onIdleTimeout is called from a time.AfterFunc goroutine. It will
750762
// only be called when we're idle, but because we're coming from a new
751763
// goroutine, there could be a new request coming in at the same time,
@@ -1150,6 +1162,7 @@ func (cc *ClientConn) awaitOpenSlotForRequest(req *http.Request) error {
11501162
}
11511163
return errClientConnUnusable
11521164
}
1165+
cc.lastIdle = time.Time{}
11531166
if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) {
11541167
if waitingForConn != nil {
11551168
close(waitingForConn)
@@ -1638,6 +1651,7 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
16381651
delete(cc.streams, id)
16391652
if len(cc.streams) == 0 && cc.idleTimer != nil {
16401653
cc.idleTimer.Reset(cc.idleTimeout)
1654+
cc.lastIdle = time.Now()
16411655
}
16421656
close(cs.done)
16431657
// Wake up checkResetOrDone via clientStream.awaitFlowControl and

http2/transport_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -4483,3 +4483,41 @@ func testTransportBodyLargerThanSpecifiedContentLength(t *testing.T, body *chunk
44834483
t.Fatalf("expected %v, got %v", errReqBodyTooLong, err)
44844484
}
44854485
}
4486+
4487+
func TestClientConnTooIdle(t *testing.T) {
4488+
tests := []struct {
4489+
cc func() *ClientConn
4490+
want bool
4491+
}{
4492+
{
4493+
func() *ClientConn {
4494+
return &ClientConn{idleTimeout: 5 * time.Second, lastIdle: time.Now().Add(-10 * time.Second)}
4495+
},
4496+
true,
4497+
},
4498+
{
4499+
func() *ClientConn {
4500+
return &ClientConn{idleTimeout: 5 * time.Second, lastIdle: time.Time{}}
4501+
},
4502+
false,
4503+
},
4504+
{
4505+
func() *ClientConn {
4506+
return &ClientConn{idleTimeout: 60 * time.Second, lastIdle: time.Now().Add(-10 * time.Second)}
4507+
},
4508+
false,
4509+
},
4510+
{
4511+
func() *ClientConn {
4512+
return &ClientConn{idleTimeout: 0, lastIdle: time.Now().Add(-10 * time.Second)}
4513+
},
4514+
false,
4515+
},
4516+
}
4517+
for i, tt := range tests {
4518+
got := tt.cc().tooIdleLocked()
4519+
if got != tt.want {
4520+
t.Errorf("%d. got %v; want %v", i, got, tt.want)
4521+
}
4522+
}
4523+
}

0 commit comments

Comments
 (0)