Skip to content

Commit a333c53

Browse files
committed
http2: add Transport support for IdleConnTimeout
Tests will be in the go repo's net/http package when this package is re-bundled into std. Updates golang/go#16808 (fixes after bundle into std) Change-Id: Iad31dc120bc008b1e9679bf7b2b988aac9c893c8 Reviewed-on: https://go-review.googlesource.com/30075 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 8058fc7 commit a333c53

File tree

3 files changed

+37
-0
lines changed

3 files changed

+37
-0
lines changed

http2/go17.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ type clientTrace httptrace.ClientTrace
3939

4040
func reqContext(r *http.Request) context.Context { return r.Context() }
4141

42+
func (t *Transport) idleConnTimeout() time.Duration {
43+
if t.t1 != nil {
44+
return t.t1.IdleConnTimeout
45+
}
46+
return 0
47+
}
48+
4249
func setResponseUncompressed(res *http.Response) { res.Uncompressed = true }
4350

4451
func traceGotConn(req *http.Request, cc *ClientConn) {

http2/not_go17.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"crypto/tls"
1111
"net"
1212
"net/http"
13+
"time"
1314
)
1415

1516
type contextContext interface {
@@ -82,3 +83,5 @@ func cloneTLSConfig(c *tls.Config) *tls.Config {
8283
func (cc *ClientConn) Ping(ctx contextContext) error {
8384
return cc.ping(ctx)
8485
}
86+
87+
func (t *Transport) idleConnTimeout() time.Duration { return 0 }

http2/transport.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ type ClientConn struct {
151151
readerDone chan struct{} // closed on error
152152
readerErr error // set before readerDone is closed
153153

154+
idleTimeout time.Duration // or 0 for never
155+
idleTimer *time.Timer
156+
154157
mu sync.Mutex // guards following
155158
cond *sync.Cond // hold mu; broadcast on flow/closed changes
156159
flow flow // our conn-level flow control quota (cs.flow is per stream)
@@ -435,6 +438,10 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
435438
wantSettingsAck: true,
436439
pings: make(map[[8]byte]chan struct{}),
437440
}
441+
if d := t.idleConnTimeout(); d != 0 {
442+
cc.idleTimeout = d
443+
cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout)
444+
}
438445
if VerboseLogs {
439446
t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr())
440447
}
@@ -511,6 +518,16 @@ func (cc *ClientConn) canTakeNewRequestLocked() bool {
511518
cc.nextStreamID < math.MaxInt32
512519
}
513520

521+
// onIdleTimeout is called from a time.AfterFunc goroutine. It will
522+
// only be called when we're idle, but because we're coming from a new
523+
// goroutine, there could be a new request coming in at the same time,
524+
// so this simply calls the synchronized closeIfIdle to shut down this
525+
// connection. The timer could just call closeIfIdle, but this is more
526+
// clear.
527+
func (cc *ClientConn) onIdleTimeout() {
528+
cc.closeIfIdle()
529+
}
530+
514531
func (cc *ClientConn) closeIfIdle() {
515532
cc.mu.Lock()
516533
if len(cc.streams) > 0 {
@@ -652,6 +669,9 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
652669
if err := checkConnHeaders(req); err != nil {
653670
return nil, err
654671
}
672+
if cc.idleTimer != nil {
673+
cc.idleTimer.Stop()
674+
}
655675

656676
trailers, err := commaSeparatedTrailers(req)
657677
if err != nil {
@@ -1176,6 +1196,9 @@ func (cc *ClientConn) streamByID(id uint32, andRemove bool) *clientStream {
11761196
if andRemove && cs != nil && !cc.closed {
11771197
cc.lastActive = time.Now()
11781198
delete(cc.streams, id)
1199+
if len(cc.streams) == 0 && cc.idleTimer != nil {
1200+
cc.idleTimer.Reset(cc.idleTimeout)
1201+
}
11791202
close(cs.done)
11801203
cc.cond.Broadcast() // wake up checkResetOrDone via clientStream.awaitFlowControl
11811204
}
@@ -1232,6 +1255,10 @@ func (rl *clientConnReadLoop) cleanup() {
12321255
defer cc.t.connPool().MarkDead(cc)
12331256
defer close(cc.readerDone)
12341257

1258+
if cc.idleTimer != nil {
1259+
cc.idleTimer.Stop()
1260+
}
1261+
12351262
// Close any response bodies if the server closes prematurely.
12361263
// TODO: also do this if we've written the headers but not
12371264
// gotten a response yet.

0 commit comments

Comments
 (0)