Skip to content

Commit 1a058cd

Browse files
fraenkelbradfitz
authored andcommitted
net/http: only decrement connection count if we removed a connection
The connection count must only be decremented if the persistent connection was also removed. Fixes #34941 Change-Id: I5070717d5d9effec78016005fa4910593500c8cf Reviewed-on: https://go-review.googlesource.com/c/go/+/202087 Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent e7ce862 commit 1a058cd

File tree

2 files changed

+66
-5
lines changed

2 files changed

+66
-5
lines changed

src/net/http/transport.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,9 @@ func (t *Transport) roundTrip(req *Request) (*Response, error) {
545545

546546
_, isH2DialError := pconn.alt.(http2erringRoundTripper)
547547
if http2isNoCachedConnError(err) || isH2DialError {
548-
t.removeIdleConn(pconn)
549-
t.decConnsPerHost(pconn.cacheKey)
548+
if t.removeIdleConn(pconn) {
549+
t.decConnsPerHost(pconn.cacheKey)
550+
}
550551
}
551552
if !pconn.shouldRetryRequest(req, err) {
552553
// Issue 16465: return underlying net.Conn.Read error from peek,
@@ -958,26 +959,28 @@ func (t *Transport) queueForIdleConn(w *wantConn) (delivered bool) {
958959
}
959960

960961
// removeIdleConn marks pconn as dead.
961-
func (t *Transport) removeIdleConn(pconn *persistConn) {
962+
func (t *Transport) removeIdleConn(pconn *persistConn) bool {
962963
t.idleMu.Lock()
963964
defer t.idleMu.Unlock()
964-
t.removeIdleConnLocked(pconn)
965+
return t.removeIdleConnLocked(pconn)
965966
}
966967

967968
// t.idleMu must be held.
968-
func (t *Transport) removeIdleConnLocked(pconn *persistConn) {
969+
func (t *Transport) removeIdleConnLocked(pconn *persistConn) bool {
969970
if pconn.idleTimer != nil {
970971
pconn.idleTimer.Stop()
971972
}
972973
t.idleLRU.remove(pconn)
973974
key := pconn.cacheKey
974975
pconns := t.idleConn[key]
976+
var removed bool
975977
switch len(pconns) {
976978
case 0:
977979
// Nothing
978980
case 1:
979981
if pconns[0] == pconn {
980982
delete(t.idleConn, key)
983+
removed = true
981984
}
982985
default:
983986
for i, v := range pconns {
@@ -988,9 +991,11 @@ func (t *Transport) removeIdleConnLocked(pconn *persistConn) {
988991
// conns at the end.
989992
copy(pconns[i:], pconns[i+1:])
990993
t.idleConn[key] = pconns[:len(pconns)-1]
994+
removed = true
991995
break
992996
}
993997
}
998+
return removed
994999
}
9951000

9961001
func (t *Transport) setReqCanceler(r *Request, fn func(error)) {

src/net/http/transport_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5893,3 +5893,59 @@ func TestDontCacheBrokenHTTP2Conn(t *testing.T) {
58935893
t.Errorf("GotConn calls = %v; want %v", got, want)
58945894
}
58955895
}
5896+
5897+
// Issue 34941
5898+
// When the client has too many concurrent requests on a single connection,
5899+
// http.http2noCachedConnError is reported on multiple requests. There should
5900+
// only be one decrement regardless of the number of failures.
5901+
func TestTransportDecrementConnWhenIdleConnRemoved(t *testing.T) {
5902+
defer afterTest(t)
5903+
5904+
h := HandlerFunc(func(w ResponseWriter, r *Request) {
5905+
_, err := w.Write([]byte("foo"))
5906+
if err != nil {
5907+
t.Fatalf("Write: %v", err)
5908+
}
5909+
})
5910+
5911+
ts := httptest.NewUnstartedServer(h)
5912+
ts.EnableHTTP2 = true
5913+
ts.StartTLS()
5914+
defer ts.Close()
5915+
5916+
c := ts.Client()
5917+
tr := c.Transport.(*Transport)
5918+
tr.MaxConnsPerHost = 1
5919+
if err := ExportHttp2ConfigureTransport(tr); err != nil {
5920+
t.Fatalf("ExportHttp2ConfigureTransport: %v", err)
5921+
}
5922+
5923+
errCh := make(chan error, 300)
5924+
doReq := func() {
5925+
resp, err := c.Get(ts.URL)
5926+
if err != nil {
5927+
errCh <- fmt.Errorf("request failed: %v", err)
5928+
return
5929+
}
5930+
defer resp.Body.Close()
5931+
_, err = ioutil.ReadAll(resp.Body)
5932+
if err != nil {
5933+
errCh <- fmt.Errorf("read body failed: %v", err)
5934+
}
5935+
}
5936+
5937+
var wg sync.WaitGroup
5938+
for i := 0; i < 300; i++ {
5939+
wg.Add(1)
5940+
go func() {
5941+
defer wg.Done()
5942+
doReq()
5943+
}()
5944+
}
5945+
wg.Wait()
5946+
close(errCh)
5947+
5948+
for err := range errCh {
5949+
t.Errorf("error occurred: %v", err)
5950+
}
5951+
}

0 commit comments

Comments
 (0)