net/http: ensure ConnState for StateNew fires before Server.Serve returns

The addition of Server.ConnState provides all the necessary
hooks to stop a Server gracefully, but StateNew previously
could fire concurrently with Serve exiting (as it does when
its net.Listener is closed). This previously meant one
couldn't use a WaitGroup incremented in the StateNew hook
along with calling Wait after Serve. Now you can.

Update #4674

LGTM=bradfitz
R=bradfitz
CC=golang-codereviews
https://golang.org/cl/70410044
This commit is contained in:
Richard Crowley 2014-03-01 20:32:42 -08:00 committed by Brad Fitzpatrick
parent 9f0bba45f4
commit 7124ee59d1
2 changed files with 22 additions and 1 deletions

View File

@ -2372,6 +2372,27 @@ func TestServerKeepAlivesEnabled(t *testing.T) {
}
}
func TestServerConnStateNew(t *testing.T) {
sawNew := false // if the test is buggy, we'll race on this variable.
srv := &Server{
ConnState: func(c net.Conn, state ConnState) {
if state == StateNew {
sawNew = true // testing that this write isn't racy
}
},
Handler: HandlerFunc(func(w ResponseWriter, r *Request) {}), // irrelevant
}
srv.Serve(&oneConnListener{
conn: &rwTestConn{
Reader: strings.NewReader("GET / HTTP/1.1\r\nHost: foo\r\n\r\n"),
Writer: ioutil.Discard,
},
})
if !sawNew { // testing that this read isn't racy
t.Error("StateNew not seen")
}
}
func BenchmarkClientServer(b *testing.B) {
b.ReportAllocs()
b.StopTimer()

View File

@ -1090,7 +1090,6 @@ func (c *conn) setState(nc net.Conn, state ConnState) {
// Serve a new connection.
func (c *conn) serve() {
origConn := c.rwc // copy it before it's set nil on Close or Hijack
c.setState(origConn, StateNew)
defer func() {
if err := recover(); err != nil {
const size = 64 << 10
@ -1722,6 +1721,7 @@ func (srv *Server) Serve(l net.Listener) error {
if err != nil {
continue
}
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve()
}
}