Skip to content

Commit 91b910b

Browse files
committed
net/http: update bundled http2
If a user starts two HTTP requests when no http2 connection is available, both end up creating new TCP connections, since the server's protocol (h1 or h2) isn't yet known. Once it turns out that the server supports h2, one of the connections is useless. Previously we kept upgrading both TLS connections to h2 (SETTINGS frame exchange, etc). Now the unnecessary connections are closed instead, before the h2 preface/SETTINGS. Updates x/net/http2 to git rev a8e212f3d for https://golang.org/cl/18675 This CL contains the tests for https://golang.org/cl/18675 Semi-related change noticed while writing the tests: now that we have TLSNextProto in Go 1.6, which consults the TLS ConnectionState.NegotiatedProtocol, we have to gurantee that the TLS handshake has been done before we look at the ConnectionState. So add that check after the DialTLS hook. (we never documented that users have to call Handshake, so do it for them, now that it matters) Updates #13957 Change-Id: I9a70e9d1282fe937ea654d9b1269c984c4e366c0 Reviewed-on: https://go-review.googlesource.com/18676 Reviewed-by: Andrew Gerrand <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 1556c31 commit 91b910b

File tree

3 files changed

+179
-14
lines changed

3 files changed

+179
-14
lines changed

src/net/http/clientserver_test.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"io"
1515
"io/ioutil"
1616
"log"
17+
"net"
1718
. "net/http"
1819
"net/http/httptest"
1920
"net/url"
@@ -22,7 +23,9 @@ import (
2223
"sort"
2324
"strings"
2425
"sync"
26+
"sync/atomic"
2527
"testing"
28+
"time"
2629
)
2730

2831
type clientServerTest struct {
@@ -861,3 +864,93 @@ func testStarRequest(t *testing.T, method string, h2 bool) {
861864
t.Errorf("RequestURI = %q; want *", req.RequestURI)
862865
}
863866
}
867+
868+
// Issue 13957
869+
func TestTransportDiscardsUnneededConns(t *testing.T) {
870+
defer afterTest(t)
871+
cst := newClientServerTest(t, h2Mode, HandlerFunc(func(w ResponseWriter, r *Request) {
872+
fmt.Fprintf(w, "Hello, %v", r.RemoteAddr)
873+
}))
874+
defer cst.close()
875+
876+
var numOpen, numClose int32 // atomic
877+
878+
tlsConfig := &tls.Config{InsecureSkipVerify: true}
879+
tr := &Transport{
880+
TLSClientConfig: tlsConfig,
881+
DialTLS: func(_, addr string) (net.Conn, error) {
882+
time.Sleep(10 * time.Millisecond)
883+
rc, err := net.Dial("tcp", addr)
884+
if err != nil {
885+
return nil, err
886+
}
887+
atomic.AddInt32(&numOpen, 1)
888+
c := noteCloseConn{rc, func() { atomic.AddInt32(&numClose, 1) }}
889+
return tls.Client(c, tlsConfig), nil
890+
},
891+
}
892+
if err := ExportHttp2ConfigureTransport(tr); err != nil {
893+
t.Fatal(err)
894+
}
895+
defer tr.CloseIdleConnections()
896+
897+
c := &Client{Transport: tr}
898+
899+
const N = 10
900+
gotBody := make(chan string, N)
901+
var wg sync.WaitGroup
902+
for i := 0; i < N; i++ {
903+
wg.Add(1)
904+
go func() {
905+
defer wg.Done()
906+
resp, err := c.Get(cst.ts.URL)
907+
if err != nil {
908+
t.Errorf("Get: %v", err)
909+
return
910+
}
911+
defer resp.Body.Close()
912+
slurp, err := ioutil.ReadAll(resp.Body)
913+
if err != nil {
914+
t.Error(err)
915+
}
916+
gotBody <- string(slurp)
917+
}()
918+
}
919+
wg.Wait()
920+
close(gotBody)
921+
922+
var last string
923+
for got := range gotBody {
924+
if last == "" {
925+
last = got
926+
continue
927+
}
928+
if got != last {
929+
t.Errorf("Response body changed: %q -> %q", last, got)
930+
}
931+
}
932+
933+
var open, close int32
934+
for i := 0; i < 150; i++ {
935+
open, close = atomic.LoadInt32(&numOpen), atomic.LoadInt32(&numClose)
936+
if open < 1 {
937+
t.Fatalf("open = %d; want at least", open)
938+
}
939+
if close == open-1 {
940+
// Success
941+
return
942+
}
943+
time.Sleep(10 * time.Millisecond)
944+
}
945+
t.Errorf("%d connections opened, %d closed; want %d to close", open, close, open-1)
946+
}
947+
948+
type noteCloseConn struct {
949+
net.Conn
950+
closeFunc func()
951+
}
952+
953+
func (x noteCloseConn) Close() error {
954+
x.closeFunc()
955+
return x.Conn.Close()
956+
}

src/net/http/h2_bundle.go

Lines changed: 80 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/net/http/transport.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,12 @@ func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
713713
return nil, errors.New("net/http: Transport.DialTLS returned (nil, nil)")
714714
}
715715
if tc, ok := pconn.conn.(*tls.Conn); ok {
716+
// Handshake here, in case DialTLS didn't. TLSNextProto below
717+
// depends on it for knowing the connection state.
718+
if err := tc.Handshake(); err != nil {
719+
go pconn.conn.Close()
720+
return nil, err
721+
}
716722
cs := tc.ConnectionState()
717723
pconn.tlsState = &cs
718724
}

0 commit comments

Comments
 (0)