Skip to content

Commit 098a87f

Browse files
tibbesgopherbot
authored andcommitted
net/http: add missing call to decConnsPerHost
A recent change to Transport.dialConnFor introduced an early return that skipped dialing. This path did not call decConnsPerHost, which can cause subsequent HTTP calls to hang if Transport.MaxConnsPerHost is set. Fixes: #65705 Change-Id: I157591114b02a3a66488d3ead7f1e6dbd374a41c Reviewed-on: https://go-review.googlesource.com/c/go/+/564036 Reviewed-by: Damien Neil <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Than McIntosh <[email protected]>
1 parent d42cd45 commit 098a87f

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

src/net/http/transport.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1478,6 +1478,7 @@ func (t *Transport) dialConnFor(w *wantConn) {
14781478
defer w.afterDial()
14791479
ctx := w.getCtxForDial()
14801480
if ctx == nil {
1481+
t.decConnsPerHost(w.key)
14811482
return
14821483
}
14831484

src/net/http/transport_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,56 @@ func testTransportMaxConnsPerHost(t *testing.T, mode testMode) {
730730
}
731731
}
732732

733+
func TestTransportMaxConnsPerHostDialCancellation(t *testing.T) {
734+
run(t, testTransportMaxConnsPerHostDialCancellation,
735+
testNotParallel, // because test uses SetPendingDialHooks
736+
[]testMode{http1Mode, https1Mode, http2Mode},
737+
)
738+
}
739+
740+
func testTransportMaxConnsPerHostDialCancellation(t *testing.T, mode testMode) {
741+
CondSkipHTTP2(t)
742+
743+
h := HandlerFunc(func(w ResponseWriter, r *Request) {
744+
_, err := w.Write([]byte("foo"))
745+
if err != nil {
746+
t.Fatalf("Write: %v", err)
747+
}
748+
})
749+
750+
cst := newClientServerTest(t, mode, h)
751+
defer cst.close()
752+
ts := cst.ts
753+
c := ts.Client()
754+
tr := c.Transport.(*Transport)
755+
tr.MaxConnsPerHost = 1
756+
757+
// This request is cancelled when dial is queued, which preempts dialing.
758+
ctx, cancel := context.WithCancel(context.Background())
759+
defer cancel()
760+
SetPendingDialHooks(cancel, nil)
761+
defer SetPendingDialHooks(nil, nil)
762+
763+
req, _ := NewRequestWithContext(ctx, "GET", ts.URL, nil)
764+
_, err := c.Do(req)
765+
if !errors.Is(err, context.Canceled) {
766+
t.Errorf("expected error %v, got %v", context.Canceled, err)
767+
}
768+
769+
// This request should succeed.
770+
SetPendingDialHooks(nil, nil)
771+
req, _ = NewRequest("GET", ts.URL, nil)
772+
resp, err := c.Do(req)
773+
if err != nil {
774+
t.Fatalf("request failed: %v", err)
775+
}
776+
defer resp.Body.Close()
777+
_, err = io.ReadAll(resp.Body)
778+
if err != nil {
779+
t.Fatalf("read body failed: %v", err)
780+
}
781+
}
782+
733783
func TestTransportRemovesDeadIdleConnections(t *testing.T) {
734784
run(t, testTransportRemovesDeadIdleConnections, []testMode{http1Mode})
735785
}

0 commit comments

Comments
 (0)