http.Server.Shutdown() crashing?

357 views
Skip to first unread message

char...@gmail.com

unread,
Jul 9, 2017, 7:53:36 AM7/9/17
to golang-nuts
Hi,

Go code after calling srv.Shutdown() simply isn't called. There is no error message, no dump, nothing. This is the output after visiting http://localhost:8080/stop on my browser

2017/07/09 13:58:40 Server starting up...
2017/07/09 13:58:44 Server shutting down...
test - before shutdown

Notice that "test - after shutdown" doesn't show up. What am I doing wrong?

func main() {
    srv := &http.Server{Addr: ":8080", Handler: http.DefaultServeMux}

    http.Handle("/web/", http.FileServer(http.Dir("./")))
    http.HandleFunc("/stop", func(w http.ResponseWriter, r *http.Request) {
        log.Println("Server shutting down...")
        fmt.Println("test - before shutdown")
        err := srv.Shutdown(context.Background())
        fmt.Println("test - after shutdown")
        log.Println("Error: %v", err)
    })
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        log.Println("Got request")
        time.Sleep(6000 * time.Millisecond)
        fmt.Fprintf(w, "Hello @ %s", time.Now())
        log.Println("Finished request")
    })

    log.Println("Server starting up...")
    if err := srv.ListenAndServe(); err != http.ErrServerClosed {
        log.Fatalf("Server startup failed! Error: %v", err)
    }
}


Elias Naur

unread,
Jul 9, 2017, 8:13:49 AM7/9/17
to golang-nuts
Your main goroutine probably exits before the handler gets to write "after shutdown" to the console. Try adding a time.Sleep(1*time.Second) as the very last line in main().

 - elias

char...@gmail.com

unread,
Jul 9, 2017, 8:45:38 AM7/9/17
to golang-nuts
Just tried it but it doesn't work.

The problem I started from is that server.Shutdown didn't wait for my requests to complete. (To check this I put in the time.Sleep(6000 * time.Millisecond)). When I narrowed down the problem it showed me that code after the Shutdown call wasn't being executed. And now putting a time.Sleep() in the main didn't help either...

Matt Harden

unread,
Jul 9, 2017, 10:50:20 PM7/9/17
to char...@gmail.com, golang-nuts
Try a channel to wait for shutdown in main.

func main() {
    srv := &http.Server{Addr: ":8080", Handler: http.DefaultServeMux}
    done := make(chan struct{})

    http.Handle("/web/", http.FileServer(http.Dir("./")))
    http.HandleFunc("/stop", func(w http.ResponseWriter, r *http.Request) {
        log.Println("Server shutting down...")
        fmt.Println("test - before shutdown")
        err := srv.Shutdown(context.Background())
        fmt.Println("test - after shutdown")
        log.Println("Error: %v", err)
        close(done)
    })
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        log.Println("Got request")
        time.Sleep(6000 * time.Millisecond)
        fmt.Fprintf(w, "Hello @ %s", time.Now())
        log.Println("Finished request")
    })

    log.Println("Server starting up...")
    if err := srv.ListenAndServe(); err != http.ErrServerClosed {
        log.Fatalf("Server startup failed! Error: %v", err)
    }
    <-done
}


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

char...@gmail.com

unread,
Jul 9, 2017, 11:25:48 PM7/9/17
to golang-nuts, char...@gmail.com
Interesting - now I get the same messages:

2017/07/10 13:23:17 Server starting up...
2017/07/10 13:23:22 Server shutting down...
test - before shutdown

But the program doesn't stop! I need to Ctrl+C to stop it...

I don't get it - why doesn't server.Shutdown() just work?

mikioh...@gmail.com

unread,
Jul 10, 2017, 1:05:10 AM7/10/17
to golang-nuts
calling Shutdown method of http.Server in a registered HTTP handler probably may prevent the package internal helpers from marking the inflight connection idle. so you can write like the following:

http.HandleFunc("/stop", func(w http.ResponseWriter, r *http.Request) {

        fmt.Fprintf(w, "shutdown request accepted")

        go func() {

                log.Println("shutting down the server: "srv.Shutdown(context.Background())

                close(done)

        }()

})


char...@gmail.com

unread,
Jul 10, 2017, 1:10:40 AM7/10/17
to golang-nuts
Yes! That works :-) Thanks so much.

So apparently we cannot call net.http.Server.Shutdown() from a handler function. Maybe this should be added to the docs.

Matt Harden

unread,
Jul 13, 2017, 10:01:36 PM7/13/17
to char...@gmail.com, golang-nuts
Oh! In retrospect, it's obvious. The whole point of Shutdown() is to give running handlers a chance to finish, so if we call it from within a handler (and wait for it to finish there) then we have a deadlock.

--

amk...@gmail.com

unread,
Jul 14, 2017, 2:55:16 AM7/14/17
to golang-nuts, char...@gmail.com

char...@gmail.com

unread,
Jul 14, 2017, 2:56:57 AM7/14/17
to golang-nuts, char...@gmail.com
Well that makes sense! But shouldn't it have hung instead of exiting then?

Matt Harden

unread,
Jul 14, 2017, 9:33:37 PM7/14/17
to char...@gmail.com, golang-nuts
The reason it was exiting was because you didn't wait at the end of main() after http.ListenAndServe exited. ListenAndServe will exit as soon as the listening socket is closed, but there can still be connected sockets open at that time (including the one handling /stop). When main returns, the Go program exits, and all other goroutines cease to exist.

char...@gmail.com

unread,
Jul 14, 2017, 9:55:26 PM7/14/17
to golang-nuts, char...@gmail.com
Ah - that explains it. Even though the bug's been fixed, the behaviour's been bugging me! Thanks for the explanation :-)
Reply all
Reply to author
Forward
0 new messages