aboutsummaryrefslogtreecommitdiff
path: root/src/net/http/transport_test.go
diff options
context:
space:
mode:
authorMichael Fraenkel <michael.fraenkel@gmail.com>2018-10-06 13:32:19 -0400
committerBrad Fitzpatrick <bradfitz@golang.org>2019-04-30 23:25:55 +0000
commit43b9fcf6fe9feb0ec67a7ddb01e5b542542b47c8 (patch)
tree2279d8cbf82e67312514657b0381edbcf7922a3b /src/net/http/transport_test.go
parentc706d422899e82c71578e30715684b7a991f4490 (diff)
downloadgo-43b9fcf6fe9feb0ec67a7ddb01e5b542542b47c8.tar.gz
go-43b9fcf6fe9feb0ec67a7ddb01e5b542542b47c8.zip
net/http: make Transport.MaxConnsPerHost work for HTTP/2
Treat HTTP/2 connections as an ongoing persistent connection. When we are told there is no cached connections, cleanup the associated connection and host connection count. Fixes #27753 Change-Id: I6b7bd915fc7819617cb5d3b35e46e225c75eda29 Reviewed-on: https://go-review.googlesource.com/c/go/+/140357 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/net/http/transport_test.go')
-rw-r--r--src/net/http/transport_test.go101
1 files changed, 101 insertions, 0 deletions
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index 857f0d5928..44a935960e 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -588,6 +588,107 @@ func TestTransportMaxConnsPerHostIncludeDialInProgress(t *testing.T) {
<-reqComplete
}
+func TestTransportMaxConnsPerHost(t *testing.T) {
+ defer afterTest(t)
+ if runtime.GOOS == "js" {
+ t.Skipf("skipping test on js/wasm")
+ }
+ h := HandlerFunc(func(w ResponseWriter, r *Request) {
+ _, err := w.Write([]byte("foo"))
+ if err != nil {
+ t.Fatalf("Write: %v", err)
+ }
+ })
+
+ testMaxConns := func(scheme string, ts *httptest.Server) {
+ defer ts.Close()
+
+ c := ts.Client()
+ tr := c.Transport.(*Transport)
+ tr.MaxConnsPerHost = 1
+ if err := ExportHttp2ConfigureTransport(tr); err != nil {
+ t.Fatalf("ExportHttp2ConfigureTransport: %v", err)
+ }
+
+ connCh := make(chan net.Conn, 1)
+ var dialCnt, gotConnCnt, tlsHandshakeCnt int32
+ tr.Dial = func(network, addr string) (net.Conn, error) {
+ atomic.AddInt32(&dialCnt, 1)
+ c, err := net.Dial(network, addr)
+ connCh <- c
+ return c, err
+ }
+
+ doReq := func() {
+ trace := &httptrace.ClientTrace{
+ GotConn: func(connInfo httptrace.GotConnInfo) {
+ if !connInfo.Reused {
+ atomic.AddInt32(&gotConnCnt, 1)
+ }
+ },
+ TLSHandshakeStart: func() {
+ atomic.AddInt32(&tlsHandshakeCnt, 1)
+ },
+ }
+ req, _ := NewRequest("GET", ts.URL, nil)
+ req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
+
+ resp, err := c.Do(req)
+ if err != nil {
+ t.Fatalf("request failed: %v", err)
+ }
+ defer resp.Body.Close()
+ _, err = ioutil.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatalf("read body failed: %v", err)
+ }
+ }
+
+ wg := sync.WaitGroup{}
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ doReq()
+ }()
+ }
+ wg.Wait()
+
+ expected := int32(tr.MaxConnsPerHost)
+ if dialCnt != expected {
+ t.Errorf("Too many dials (%s): %d", scheme, dialCnt)
+ }
+ if gotConnCnt != expected {
+ t.Errorf("Too many get connections (%s): %d", scheme, gotConnCnt)
+ }
+ if ts.TLS != nil && tlsHandshakeCnt != expected {
+ t.Errorf("Too many tls handshakes (%s): %d", scheme, tlsHandshakeCnt)
+ }
+
+ (<-connCh).Close()
+
+ doReq()
+ expected++
+ if dialCnt != expected {
+ t.Errorf("Too many dials (%s): %d", scheme, dialCnt)
+ }
+ if gotConnCnt != expected {
+ t.Errorf("Too many get connections (%s): %d", scheme, gotConnCnt)
+ }
+ if ts.TLS != nil && tlsHandshakeCnt != expected {
+ t.Errorf("Too many tls handshakes (%s): %d", scheme, tlsHandshakeCnt)
+ }
+ }
+
+ testMaxConns("http", httptest.NewServer(h))
+ testMaxConns("https", httptest.NewTLSServer(h))
+
+ ts := httptest.NewUnstartedServer(h)
+ ts.TLS = &tls.Config{NextProtos: []string{"h2"}}
+ ts.StartTLS()
+ testMaxConns("http2", ts)
+}
+
func TestTransportRemovesDeadIdleConnections(t *testing.T) {
setParallel(t)
defer afterTest(t)