Skip to content

Commit c80e0d3

Browse files
committed
net/http: fix data race with concurrent use of Server.Serve
Fixes #16505 Change-Id: I0afabcc8b1be3a5dbee59946b0c44d4c00a28d71 Reviewed-on: https://go-review.googlesource.com/25280 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Chris Broadfoot <[email protected]>
1 parent 4a15508 commit c80e0d3

File tree

2 files changed

+40
-9
lines changed

2 files changed

+40
-9
lines changed

src/net/http/serve_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4716,3 +4716,14 @@ func BenchmarkCloseNotifier(b *testing.B) {
47164716
}
47174717
b.StopTimer()
47184718
}
4719+
4720+
// Verify this doesn't race (Issue 16505)
4721+
func TestConcurrentServerServe(t *testing.T) {
4722+
for i := 0; i < 100; i++ {
4723+
ln1 := &oneConnListener{conn: nil}
4724+
ln2 := &oneConnListener{conn: nil}
4725+
srv := Server{}
4726+
go func() { srv.Serve(ln1) }()
4727+
go func() { srv.Serve(ln2) }()
4728+
}
4729+
}

src/net/http/server.go

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2129,8 +2129,8 @@ type Server struct {
21292129
ErrorLog *log.Logger
21302130

21312131
disableKeepAlives int32 // accessed atomically.
2132-
nextProtoOnce sync.Once // guards initialization of TLSNextProto in Serve
2133-
nextProtoErr error
2132+
nextProtoOnce sync.Once // guards setupHTTP2_* init
2133+
nextProtoErr error // result of http2.ConfigureServer if used
21342134
}
21352135

21362136
// A ConnState represents the state of a client connection to a server.
@@ -2260,10 +2260,8 @@ func (srv *Server) Serve(l net.Listener) error {
22602260
}
22612261
var tempDelay time.Duration // how long to sleep on accept failure
22622262

2263-
if srv.shouldConfigureHTTP2ForServe() {
2264-
if err := srv.setupHTTP2(); err != nil {
2265-
return err
2266-
}
2263+
if err := srv.setupHTTP2_Serve(); err != nil {
2264+
return err
22672265
}
22682266

22692267
// TODO: allow changing base context? can't imagine concrete
@@ -2408,7 +2406,7 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
24082406

24092407
// Setup HTTP/2 before srv.Serve, to initialize srv.TLSConfig
24102408
// before we clone it and create the TLS Listener.
2411-
if err := srv.setupHTTP2(); err != nil {
2409+
if err := srv.setupHTTP2_ListenAndServeTLS(); err != nil {
24122410
return err
24132411
}
24142412

@@ -2436,14 +2434,36 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
24362434
return srv.Serve(tlsListener)
24372435
}
24382436

2439-
func (srv *Server) setupHTTP2() error {
2437+
// setupHTTP2_ListenAndServeTLS conditionally configures HTTP/2 on
2438+
// srv and returns whether there was an error setting it up. If it is
2439+
// not configured for policy reasons, nil is returned.
2440+
func (srv *Server) setupHTTP2_ListenAndServeTLS() error {
24402441
srv.nextProtoOnce.Do(srv.onceSetNextProtoDefaults)
24412442
return srv.nextProtoErr
24422443
}
24432444

2445+
// setupHTTP2_Serve is called from (*Server).Serve and conditionally
2446+
// configures HTTP/2 on srv using a more conservative policy than
2447+
// setupHTTP2_ListenAndServeTLS because Serve may be called
2448+
// concurrently.
2449+
//
2450+
// The tests named TestTransportAutomaticHTTP2* and
2451+
// TestConcurrentServerServe in server_test.go demonstrate some
2452+
// of the supported use cases and motivations.
2453+
func (srv *Server) setupHTTP2_Serve() error {
2454+
srv.nextProtoOnce.Do(srv.onceSetNextProtoDefaults_Serve)
2455+
return srv.nextProtoErr
2456+
}
2457+
2458+
func (srv *Server) onceSetNextProtoDefaults_Serve() {
2459+
if srv.shouldConfigureHTTP2ForServe() {
2460+
srv.onceSetNextProtoDefaults()
2461+
}
2462+
}
2463+
24442464
// onceSetNextProtoDefaults configures HTTP/2, if the user hasn't
24452465
// configured otherwise. (by setting srv.TLSNextProto non-nil)
2446-
// It must only be called via srv.nextProtoOnce (use srv.setupHTTP2).
2466+
// It must only be called via srv.nextProtoOnce (use srv.setupHTTP2_*).
24472467
func (srv *Server) onceSetNextProtoDefaults() {
24482468
if strings.Contains(os.Getenv("GODEBUG"), "http2server=0") {
24492469
return

0 commit comments

Comments
 (0)