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) {
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
// Serve always returns a non-nil error. After Shutdown or Close, the
// returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {
- defer l.Close()
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
if err := srv.setupHTTP2_Serve(); err != 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.
type globalOptionsHandler struct{}