Commit f70d1e76 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

net/http: ensure that Listener.Close is called only once in Server.Serve

Fixes #24803

Change-Id: I8b1e7c5a74018a0c333f8c38a7ec5f5827ab1606
Reviewed-on: https://go-review.googlesource.com/106715Reviewed-by: default avatarEmmanuel Odeke <emm.odeke@gmail.com>
parent 8998c55e
...@@ -5886,6 +5886,44 @@ func TestServerListenNotComparableListener(t *testing.T) { ...@@ -5886,6 +5886,44 @@ func TestServerListenNotComparableListener(t *testing.T) {
s.Serve(make(eofListenerNotComparable, 1)) // used to panic s.Serve(make(eofListenerNotComparable, 1)) // used to panic
} }
// countCloseListener is a Listener wrapper that counts the number of Close calls.
type countCloseListener struct {
net.Listener
closes int32 // atomic
}
func (p *countCloseListener) Close() error {
atomic.AddInt32(&p.closes, 1)
return nil
}
// Issue 24803: don't call Listener.Close on Server.Shutdown.
func TestServerCloseListenerOnce(t *testing.T) {
setParallel(t)
defer afterTest(t)
ln := newLocalListener(t)
defer ln.Close()
cl := &countCloseListener{Listener: ln}
server := &Server{}
sdone := make(chan bool, 1)
go func() {
server.Serve(cl)
sdone <- true
}()
time.Sleep(10 * time.Millisecond)
server.Shutdown(context.Background())
ln.Close()
<-sdone
nclose := atomic.LoadInt32(&cl.closes)
if nclose != 1 {
t.Errorf("Close calls = %v; want 1", nclose)
}
}
func BenchmarkResponseStatusLine(b *testing.B) { func BenchmarkResponseStatusLine(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
......
...@@ -2770,10 +2770,13 @@ var ErrServerClosed = errors.New("http: Server closed") ...@@ -2770,10 +2770,13 @@ var ErrServerClosed = errors.New("http: Server closed")
// Serve always returns a non-nil error. After Shutdown or Close, the // Serve always returns a non-nil error. After Shutdown or Close, the
// returned error is ErrServerClosed. // returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error { func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
if fn := testHookServerServe; fn != nil { if fn := testHookServerServe; fn != nil {
fn(srv, l) fn(srv, l) // call hook with unwrapped listener
} }
l = &onceCloseListener{Listener: l}
defer l.Close()
var tempDelay time.Duration // how long to sleep on accept failure var tempDelay time.Duration // how long to sleep on accept failure
if err := srv.setupHTTP2_Serve(); err != nil { if err := srv.setupHTTP2_Serve(); err != nil {
...@@ -3249,6 +3252,21 @@ func (ln tcpKeepAliveListener) Accept() (net.Conn, error) { ...@@ -3249,6 +3252,21 @@ func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
return tc, nil return tc, nil
} }
// onceCloseListener wraps a net.Listener, protecting it from
// multiple Close calls.
type onceCloseListener struct {
net.Listener
once sync.Once
closeErr error
}
func (oc *onceCloseListener) Close() error {
oc.once.Do(oc.close)
return oc.closeErr
}
func (oc *onceCloseListener) close() { oc.closeErr = oc.Listener.Close() }
// globalOptionsHandler responds to "OPTIONS *" requests. // globalOptionsHandler responds to "OPTIONS *" requests.
type globalOptionsHandler struct{} type globalOptionsHandler struct{}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment