Skip to content

Commit 3ec1911

Browse files
fraenkelbradfitz
authored andcommitted
http2: track reused connections
nextStreamID was used as a means to determine if the connection was being reused. Multiple requests can see a new connection because the nextStreamID is updated after a ClientTrace reports it is being reused. Updates golang/go#31982 Change-Id: Iaa4b62b217f015423cddb99fd86de75a352f8320 Reviewed-on: https://go-review.googlesource.com/c/net/+/176720 Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent a4d6f7f commit 3ec1911

File tree

2 files changed

+32
-4
lines changed

2 files changed

+32
-4
lines changed

http2/transport.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"strconv"
2929
"strings"
3030
"sync"
31+
"sync/atomic"
3132
"time"
3233

3334
"golang.org/x/net/http/httpguts"
@@ -199,6 +200,7 @@ type ClientConn struct {
199200
t *Transport
200201
tconn net.Conn // usually *tls.Conn, except specialized impls
201202
tlsState *tls.ConnectionState // nil only for specialized impls
203+
reused uint32 // whether conn is being reused; atomic
202204
singleUse bool // whether being used for a single http.Request
203205

204206
// readLoop goroutine fields:
@@ -440,7 +442,8 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
440442
t.vlogf("http2: Transport failed to get client conn for %s: %v", addr, err)
441443
return nil, err
442444
}
443-
traceGotConn(req, cc)
445+
reused := !atomic.CompareAndSwapUint32(&cc.reused, 0, 1)
446+
traceGotConn(req, cc, reused)
444447
res, gotErrAfterReqBodyWrite, err := cc.roundTrip(req)
445448
if err != nil && retry <= 6 {
446449
if req, err = shouldRetryRequest(req, err, gotErrAfterReqBodyWrite); err == nil {
@@ -2559,15 +2562,15 @@ func traceGetConn(req *http.Request, hostPort string) {
25592562
trace.GetConn(hostPort)
25602563
}
25612564

2562-
func traceGotConn(req *http.Request, cc *ClientConn) {
2565+
func traceGotConn(req *http.Request, cc *ClientConn, reused bool) {
25632566
trace := httptrace.ContextClientTrace(req.Context())
25642567
if trace == nil || trace.GotConn == nil {
25652568
return
25662569
}
25672570
ci := httptrace.GotConnInfo{Conn: cc.tconn}
2571+
ci.Reused = reused
25682572
cc.mu.Lock()
2569-
ci.Reused = cc.nextStreamID > 1
2570-
ci.WasIdle = len(cc.streams) == 0 && ci.Reused
2573+
ci.WasIdle = len(cc.streams) == 0 && reused
25712574
if ci.WasIdle && !cc.lastActive.IsZero() {
25722575
ci.IdleTime = time.Now().Sub(cc.lastActive)
25732576
}

http2/transport_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"net"
2020
"net/http"
2121
"net/http/httptest"
22+
"net/http/httptrace"
2223
"net/textproto"
2324
"net/url"
2425
"os"
@@ -98,6 +99,15 @@ func TestTransportH2c(t *testing.T) {
9899
if err != nil {
99100
t.Fatal(err)
100101
}
102+
var gotConnCnt int32
103+
trace := &httptrace.ClientTrace{
104+
GotConn: func(connInfo httptrace.GotConnInfo) {
105+
if !connInfo.Reused {
106+
atomic.AddInt32(&gotConnCnt, 1)
107+
}
108+
},
109+
}
110+
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
101111
tr := &Transport{
102112
AllowHTTP: true,
103113
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
@@ -118,6 +128,9 @@ func TestTransportH2c(t *testing.T) {
118128
if got, want := string(body), "Hello, /foobar, http: true"; got != want {
119129
t.Fatalf("response got %v, want %v", got, want)
120130
}
131+
if got, want := gotConnCnt, int32(1); got != want {
132+
t.Errorf("Too many got connections: %d", gotConnCnt)
133+
}
121134
}
122135

123136
func TestTransport(t *testing.T) {
@@ -244,6 +257,14 @@ func TestTransportGroupsPendingDials(t *testing.T) {
244257
mu sync.Mutex
245258
dials = map[string]int{}
246259
)
260+
var gotConnCnt int32
261+
trace := &httptrace.ClientTrace{
262+
GotConn: func(connInfo httptrace.GotConnInfo) {
263+
if !connInfo.Reused {
264+
atomic.AddInt32(&gotConnCnt, 1)
265+
}
266+
},
267+
}
247268
var wg sync.WaitGroup
248269
for i := 0; i < 10; i++ {
249270
wg.Add(1)
@@ -254,6 +275,7 @@ func TestTransportGroupsPendingDials(t *testing.T) {
254275
t.Error(err)
255276
return
256277
}
278+
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
257279
res, err := tr.RoundTrip(req)
258280
if err != nil {
259281
t.Error(err)
@@ -298,6 +320,9 @@ func TestTransportGroupsPendingDials(t *testing.T) {
298320
}); err != nil {
299321
t.Errorf("State of pool after CloseIdleConnections: %v", err)
300322
}
323+
if got, want := gotConnCnt, int32(1); got != want {
324+
t.Errorf("Too many got connections: %d", gotConnCnt)
325+
}
301326
}
302327

303328
func retry(tries int, delay time.Duration, fn func() error) error {

0 commit comments

Comments
 (0)