http2: fix off-by-one error in client check for max concurrent streams
Fixes golang/go#48358.
Change-Id: Ib4eb93702b32ae7d03cad17ca0b997d5e6a58ad7
Reviewed-on: https://go-review.googlesource.com/c/net/+/349490
Trust: Damien Neil <dneil@google.com>
Run-TryBot: Damien Neil <dneil@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/http2/transport.go b/http2/transport.go
index 7c2c013..74c76da 100644
--- a/http2/transport.go
+++ b/http2/transport.go
@@ -800,7 +800,7 @@
// writing it.
maxConcurrentOkay = true
} else {
- maxConcurrentOkay = int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams)
+ maxConcurrentOkay = int64(len(cc.streams)+1) <= int64(cc.maxConcurrentStreams)
}
st.canTakeNewRequest = cc.goAway == nil && !cc.closed && !cc.closing && maxConcurrentOkay &&
@@ -1207,7 +1207,7 @@
return errClientConnUnusable
}
cc.lastIdle = time.Time{}
- if int64(len(cc.streams))+1 <= int64(cc.maxConcurrentStreams) {
+ if int64(len(cc.streams)) < int64(cc.maxConcurrentStreams) {
if waitingForConn != nil {
close(waitingForConn)
}
diff --git a/http2/transport_test.go b/http2/transport_test.go
index efc2695..97735fe 100644
--- a/http2/transport_test.go
+++ b/http2/transport_test.go
@@ -3824,6 +3824,51 @@
ct.run()
}
+func TestTransportRequestsLowServerLimit(t *testing.T) {
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
+ }, optOnlyServer, func(s *Server) {
+ s.MaxConcurrentStreams = 1
+ })
+ defer st.Close()
+
+ var (
+ connCountMu sync.Mutex
+ connCount int
+ )
+ tr := &Transport{
+ TLSClientConfig: tlsConfigInsecure,
+ DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
+ connCountMu.Lock()
+ defer connCountMu.Unlock()
+ connCount++
+ return tls.Dial(network, addr, cfg)
+ },
+ }
+ defer tr.CloseIdleConnections()
+
+ const reqCount = 3
+ for i := 0; i < reqCount; i++ {
+ req, err := http.NewRequest("GET", st.ts.URL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res, err := tr.RoundTrip(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got, want := res.StatusCode, 200; got != want {
+ t.Errorf("StatusCode = %v; want %v", got, want)
+ }
+ if res != nil && res.Body != nil {
+ res.Body.Close()
+ }
+ }
+
+ if connCount != 1 {
+ t.Errorf("created %v connections for %v requests, want 1", connCount, reqCount)
+ }
+}
+
// tests Transport.StrictMaxConcurrentStreams
func TestTransportRequestsStallAtServerLimit(t *testing.T) {
const maxConcurrent = 2