Skip to content

Commit 17e503f

Browse files
committed
net/http: prevent Server reuse after a Shutdown
Fixes #20239 Change-Id: Icb021daad82e6905f536e4ef09ab219500b08167 Reviewed-on: https://go-review.googlesource.com/81778 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 4ba5527 commit 17e503f

File tree

2 files changed

+44
-8
lines changed

2 files changed

+44
-8
lines changed

src/net/http/serve_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5980,6 +5980,21 @@ func TestServerCloseListenerOnce(t *testing.T) {
59805980
}
59815981
}
59825982

5983+
// Issue 20239: don't block in Serve if Shutdown is called first.
5984+
func TestServerShutdownThenServe(t *testing.T) {
5985+
var srv Server
5986+
cl := &countCloseListener{Listener: nil}
5987+
srv.Shutdown(context.Background())
5988+
got := srv.Serve(cl)
5989+
if got != ErrServerClosed {
5990+
t.Errorf("Serve err = %v; want ErrServerClosed", got)
5991+
}
5992+
nclose := atomic.LoadInt32(&cl.closes)
5993+
if nclose != 1 {
5994+
t.Errorf("Close calls = %v; want 1", nclose)
5995+
}
5996+
}
5997+
59835998
// Issue 23351: document and test behavior of ServeMux with ports
59845999
func TestStripPortFromHost(t *testing.T) {
59856000
mux := NewServeMux()

src/net/http/server.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2541,6 +2541,7 @@ func (s *Server) closeDoneChanLocked() {
25412541
// Close returns any error returned from closing the Server's
25422542
// underlying Listener(s).
25432543
func (srv *Server) Close() error {
2544+
atomic.StoreInt32(&srv.inShutdown, 1)
25442545
srv.mu.Lock()
25452546
defer srv.mu.Unlock()
25462547
srv.closeDoneChanLocked()
@@ -2578,9 +2579,11 @@ var shutdownPollInterval = 500 * time.Millisecond
25782579
// separately notify such long-lived connections of shutdown and wait
25792580
// for them to close, if desired. See RegisterOnShutdown for a way to
25802581
// register shutdown notification functions.
2582+
//
2583+
// Once Shutdown has been called on a server, it may not be reused;
2584+
// future calls to methods such as Serve will return ErrServerClosed.
25812585
func (srv *Server) Shutdown(ctx context.Context) error {
2582-
atomic.AddInt32(&srv.inShutdown, 1)
2583-
defer atomic.AddInt32(&srv.inShutdown, -1)
2586+
atomic.StoreInt32(&srv.inShutdown, 1)
25842587

25852588
srv.mu.Lock()
25862589
lnerr := srv.closeListenersLocked()
@@ -2727,6 +2730,9 @@ func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
27272730
// If srv.Addr is blank, ":http" is used.
27282731
// ListenAndServe always returns a non-nil error.
27292732
func (srv *Server) ListenAndServe() error {
2733+
if srv.shuttingDown() {
2734+
return ErrServerClosed
2735+
}
27302736
addr := srv.Addr
27312737
if addr == "" {
27322738
addr = ":http"
@@ -2775,8 +2781,8 @@ var ErrServerClosed = errors.New("http: Server closed")
27752781
// srv.TLSConfig is non-nil and doesn't include the string "h2" in
27762782
// Config.NextProtos, HTTP/2 support is not enabled.
27772783
//
2778-
// Serve always returns a non-nil error. After Shutdown or Close, the
2779-
// returned error is ErrServerClosed.
2784+
// Serve always returns a non-nil error and closes l.
2785+
// After Shutdown or Close, the returned error is ErrServerClosed.
27802786
func (srv *Server) Serve(l net.Listener) error {
27812787
if fn := testHookServerServe; fn != nil {
27822788
fn(srv, l) // call hook with unwrapped listener
@@ -2785,15 +2791,19 @@ func (srv *Server) Serve(l net.Listener) error {
27852791
l = &onceCloseListener{Listener: l}
27862792
defer l.Close()
27872793

2788-
var tempDelay time.Duration // how long to sleep on accept failure
2789-
27902794
if err := srv.setupHTTP2_Serve(); err != nil {
27912795
return err
27922796
}
27932797

2794-
srv.trackListener(&l, true)
2798+
serveDone := make(chan struct{})
2799+
defer close(serveDone)
2800+
2801+
if !srv.trackListener(&l, true) {
2802+
return ErrServerClosed
2803+
}
27952804
defer srv.trackListener(&l, false)
27962805

2806+
var tempDelay time.Duration // how long to sleep on accept failure
27972807
baseCtx := context.Background() // base is always background, per Issue 16220
27982808
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
27992809
for {
@@ -2877,13 +2887,18 @@ func (srv *Server) ServeTLS(l net.Listener, certFile, keyFile string) error {
28772887
// trackListener via Serve and can track+defer untrack the same
28782888
// pointer to local variable there. We never need to compare a
28792889
// Listener from another caller.
2880-
func (s *Server) trackListener(ln *net.Listener, add bool) {
2890+
//
2891+
// It reports whether the server is still up (not Shutdown or Closed).
2892+
func (s *Server) trackListener(ln *net.Listener, add bool) bool {
28812893
s.mu.Lock()
28822894
defer s.mu.Unlock()
28832895
if s.listeners == nil {
28842896
s.listeners = make(map[*net.Listener]struct{})
28852897
}
28862898
if add {
2899+
if s.shuttingDown() {
2900+
return false
2901+
}
28872902
// If the *Server is being reused after a previous
28882903
// Close or Shutdown, reset its doneChan:
28892904
if len(s.listeners) == 0 && len(s.activeConn) == 0 {
@@ -2893,6 +2908,7 @@ func (s *Server) trackListener(ln *net.Listener, add bool) {
28932908
} else {
28942909
delete(s.listeners, ln)
28952910
}
2911+
return true
28962912
}
28972913

28982914
func (s *Server) trackConn(c *conn, add bool) {
@@ -2927,6 +2943,8 @@ func (s *Server) doKeepAlives() bool {
29272943
}
29282944

29292945
func (s *Server) shuttingDown() bool {
2946+
// TODO: replace inShutdown with the existing atomicBool type;
2947+
// see https://github.com/golang/go/issues/20239#issuecomment-381434582
29302948
return atomic.LoadInt32(&s.inShutdown) != 0
29312949
}
29322950

@@ -3055,6 +3073,9 @@ func ListenAndServeTLS(addr, certFile, keyFile string, handler Handler) error {
30553073
//
30563074
// ListenAndServeTLS always returns a non-nil error.
30573075
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
3076+
if srv.shuttingDown() {
3077+
return ErrServerClosed
3078+
}
30583079
addr := srv.Addr
30593080
if addr == "" {
30603081
addr = ":https"

0 commit comments

Comments
 (0)