Skip to content

Commit b5f0aff

Browse files
committed
net/http: conditionally configure HTTP/2 in Server.Serve(Listener)
Don't configure HTTP/2 in http.Server.Serve(net.Listener) if the Server's TLSConfig is set and doesn't include the "h2" NextProto value. This avoids mutating a *tls.Config already in use if previously passed to tls.NewListener. Also document this. (it's come up a few times now) Fixes #15908 Change-Id: I283eed82fdb29a791f80d801aadd9f75db244de0 Reviewed-on: https://go-review.googlesource.com/24508 Reviewed-by: Andrew Gerrand <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 996ed3b commit b5f0aff

File tree

2 files changed

+68
-3
lines changed

2 files changed

+68
-3
lines changed

src/net/http/serve_test.go

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1107,11 +1107,44 @@ func TestTLSServer(t *testing.T) {
11071107
})
11081108
}
11091109

1110-
func TestAutomaticHTTP2_Serve(t *testing.T) {
1110+
// Issue 15908
1111+
func TestAutomaticHTTP2_Serve_NoTLSConfig(t *testing.T) {
1112+
testAutomaticHTTP2_Serve(t, nil, true)
1113+
}
1114+
1115+
func TestAutomaticHTTP2_Serve_NonH2TLSConfig(t *testing.T) {
1116+
testAutomaticHTTP2_Serve(t, &tls.Config{}, false)
1117+
}
1118+
1119+
func TestAutomaticHTTP2_Serve_H2TLSConfig(t *testing.T) {
1120+
testAutomaticHTTP2_Serve(t, &tls.Config{NextProtos: []string{"h2"}}, true)
1121+
}
1122+
1123+
func testAutomaticHTTP2_Serve(t *testing.T, tlsConf *tls.Config, wantH2 bool) {
11111124
defer afterTest(t)
11121125
ln := newLocalListener(t)
11131126
ln.Close() // immediately (not a defer!)
11141127
var s Server
1128+
s.TLSConfig = tlsConf
1129+
if err := s.Serve(ln); err == nil {
1130+
t.Fatal("expected an error")
1131+
}
1132+
gotH2 := s.TLSNextProto["h2"] != nil
1133+
if gotH2 != wantH2 {
1134+
t.Errorf("http2 configured = %v; want %v", gotH2, wantH2)
1135+
}
1136+
}
1137+
1138+
func TestAutomaticHTTP2_Serve_WithTLSConfig(t *testing.T) {
1139+
defer afterTest(t)
1140+
ln := newLocalListener(t)
1141+
ln.Close() // immediately (not a defer!)
1142+
var s Server
1143+
// Set the TLSConfig. In reality, this would be the
1144+
// *tls.Config given to tls.NewListener.
1145+
s.TLSConfig = &tls.Config{
1146+
NextProtos: []string{"h2"},
1147+
}
11151148
if err := s.Serve(ln); err == nil {
11161149
t.Fatal("expected an error")
11171150
}

src/net/http/server.go

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2222,19 +2222,51 @@ func (srv *Server) ListenAndServe() error {
22222222

22232223
var testHookServerServe func(*Server, net.Listener) // used if non-nil
22242224

2225+
// shouldDoServeHTTP2 reports whether Server.Serve should configure
2226+
// automatic HTTP/2. (which sets up the srv.TLSNextProto map)
2227+
func (srv *Server) shouldConfigureHTTP2ForServe() bool {
2228+
if srv.TLSConfig == nil {
2229+
// Compatibility with Go 1.6:
2230+
// If there's no TLSConfig, it's possible that the user just
2231+
// didn't set it on the http.Server, but did pass it to
2232+
// tls.NewListener and passed that listener to Serve.
2233+
// So we should configure HTTP/2 (to set up srv.TLSNextProto)
2234+
// in case the listener returns an "h2" *tls.Conn.
2235+
return true
2236+
}
2237+
// The user specified a TLSConfig on their http.Server.
2238+
// In this, case, only configure HTTP/2 if their tls.Config
2239+
// explicitly mentions "h2". Otherwise http2.ConfigureServer
2240+
// would modify the tls.Config to add it, but they probably already
2241+
// passed this tls.Config to tls.NewListener. And if they did,
2242+
// it's too late anyway to fix it. It would only be potentially racy.
2243+
// See Issue 15908.
2244+
return strSliceContains(srv.TLSConfig.NextProtos, http2NextProtoTLS)
2245+
}
2246+
22252247
// Serve accepts incoming connections on the Listener l, creating a
22262248
// new service goroutine for each. The service goroutines read requests and
22272249
// then call srv.Handler to reply to them.
2250+
//
2251+
// For HTTP/2 support, srv.TLSConfig should be initialized to the
2252+
// provided listener's TLS Config before calling Serve. If
2253+
// srv.TLSConfig is non-nil and doesn't include the string "h2" in
2254+
// Config.NextProtos, HTTP/2 support is not enabled.
2255+
//
22282256
// Serve always returns a non-nil error.
22292257
func (srv *Server) Serve(l net.Listener) error {
22302258
defer l.Close()
22312259
if fn := testHookServerServe; fn != nil {
22322260
fn(srv, l)
22332261
}
22342262
var tempDelay time.Duration // how long to sleep on accept failure
2235-
if err := srv.setupHTTP2(); err != nil {
2236-
return err
2263+
2264+
if srv.shouldConfigureHTTP2ForServe() {
2265+
if err := srv.setupHTTP2(); err != nil {
2266+
return err
2267+
}
22372268
}
2269+
22382270
// TODO: allow changing base context? can't imagine concrete
22392271
// use cases yet.
22402272
baseCtx := context.Background()

0 commit comments

Comments
 (0)