Skip to content

Commit 1a63f11

Browse files
tomberganbradfitz
authored andcommitted
net/http: Add Server.RegisterOnShutdown
This will be used to allow http2 servers to register a shutdown function so that net/http.Server.Shutdown will work when the http2 server is configured via a manual call to http2.ConfigureServer. Currently, Shutdown only works when the http2 server is configured automatically by the net/http package. Updates #20302 Updates #18471 Change-Id: Ifc2b5f3126126a106b49ea4a7e999279852b9cc9 Reviewed-on: https://go-review.googlesource.com/44003 Run-TryBot: Tom Bergan <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 6374a66 commit 1a63f11

File tree

2 files changed

+26
-2
lines changed

2 files changed

+26
-2
lines changed

src/net/http/serve_test.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -5232,15 +5232,19 @@ func testServerShutdown(t *testing.T, h2 bool) {
52325232
defer afterTest(t)
52335233
var doShutdown func() // set later
52345234
var shutdownRes = make(chan error, 1)
5235-
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
5235+
var gotOnShutdown = make(chan struct{}, 1)
5236+
handler := HandlerFunc(func(w ResponseWriter, r *Request) {
52365237
go doShutdown()
52375238
// Shutdown is graceful, so it should not interrupt
52385239
// this in-flight response. Add a tiny sleep here to
52395240
// increase the odds of a failure if shutdown has
52405241
// bugs.
52415242
time.Sleep(20 * time.Millisecond)
52425243
io.WriteString(w, r.RemoteAddr)
5243-
}))
5244+
})
5245+
cst := newClientServerTest(t, h2, handler, func(srv *httptest.Server) {
5246+
srv.Config.RegisterOnShutdown(func() { gotOnShutdown <- struct{}{} })
5247+
})
52445248
defer cst.close()
52455249

52465250
doShutdown = func() {
@@ -5251,6 +5255,11 @@ func testServerShutdown(t *testing.T, h2 bool) {
52515255
if err := <-shutdownRes; err != nil {
52525256
t.Fatalf("Shutdown: %v", err)
52535257
}
5258+
select {
5259+
case <-gotOnShutdown:
5260+
case <-time.After(5 * time.Second):
5261+
t.Errorf("onShutdown callback not called, RegisterOnShutdown broken?")
5262+
}
52545263

52555264
res, err := cst.c.Get(cst.ts.URL)
52565265
if err == nil {

src/net/http/server.go

+15
Original file line numberDiff line numberDiff line change
@@ -2395,6 +2395,7 @@ type Server struct {
23952395
listeners map[net.Listener]struct{}
23962396
activeConn map[*conn]struct{}
23972397
doneChan chan struct{}
2398+
onShutdown []func()
23982399
}
23992400

24002401
func (s *Server) getDoneChan() <-chan struct{} {
@@ -2475,6 +2476,9 @@ func (srv *Server) Shutdown(ctx context.Context) error {
24752476
srv.mu.Lock()
24762477
lnerr := srv.closeListenersLocked()
24772478
srv.closeDoneChanLocked()
2479+
for _, f := range srv.onShutdown {
2480+
go f()
2481+
}
24782482
srv.mu.Unlock()
24792483

24802484
ticker := time.NewTicker(shutdownPollInterval)
@@ -2491,6 +2495,17 @@ func (srv *Server) Shutdown(ctx context.Context) error {
24912495
}
24922496
}
24932497

2498+
// RegisterOnShutdown registers a function to call on Shutdown.
2499+
// This can be used to gracefully shutdown connections that have
2500+
// undergone NPN/ALPN protocol upgrade or that have been hijacked.
2501+
// This function should start protocol-specific graceful shutdown,
2502+
// but should not wait for shutdown to complete.
2503+
func (srv *Server) RegisterOnShutdown(f func()) {
2504+
srv.mu.Lock()
2505+
srv.onShutdown = append(srv.onShutdown, f)
2506+
srv.mu.Unlock()
2507+
}
2508+
24942509
// closeIdleConns closes all idle connections and reports whether the
24952510
// server is quiescent.
24962511
func (s *Server) closeIdleConns() bool {

0 commit comments

Comments
 (0)