How to stop http.ListenAndServe()
Regarding graceful shutdown (introduced in Go 1.8), a bit more concrete example:
package main
import (
"context"
"io"
"log"
"net/http"
"sync"
"time"
)
func startHttpServer(wg *sync.WaitGroup) *http.Server {
srv := &http.Server{Addr: ":8080"}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "hello world\n")
})
go func() {
defer wg.Done() // let main know we are done cleaning up
// always returns error. ErrServerClosed on graceful close
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
// unexpected error. port in use?
log.Fatalf("ListenAndServe(): %v", err)
}
}()
// returning reference so caller can call Shutdown()
return srv
}
func main() {
log.Printf("main: starting HTTP server")
httpServerExitDone := &sync.WaitGroup{}
httpServerExitDone.Add(1)
srv := startHttpServer(httpServerExitDone)
log.Printf("main: serving for 10 seconds")
time.Sleep(10 * time.Second)
log.Printf("main: stopping HTTP server")
// now close the server gracefully ("shutdown")
// timeout could be given with a proper context
// (in real world you shouldn't use TODO()).
if err := srv.Shutdown(context.TODO()); err != nil {
panic(err) // failure/timeout shutting down the server gracefully
}
// wait for goroutine started in startHttpServer() to stop
httpServerExitDone.Wait()
log.Printf("main: done. exiting")
}
As mentioned in yo.ian.g
's answer. Go 1.8 has included this functionality in the standard lib.
Minimal example for for Go 1.8+
:
server := &http.Server{Addr: ":8080", Handler: handler}
go func() {
if err := server.ListenAndServe(); err != nil {
// handle err
}
}()
// Setting up signal capturing
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
// Waiting for SIGINT (kill -2)
<-stop
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
// handle err
}
// Wait for ListenAndServe goroutine to close.
You can kill the server gracefully using kill -2 <pid>
Original Answer - Pre Go 1.8 :
Building on Uvelichitel's answer.
You can create your own version of ListenAndServe
which returns an io.Closer
and does not block.
func ListenAndServeWithClose(addr string, handler http.Handler) (io.Closer,error) {
var (
listener net.Listener
srvCloser io.Closer
err error
)
srv := &http.Server{Addr: addr, Handler: handler}
if addr == "" {
addr = ":http"
}
listener, err = net.Listen("tcp", addr)
if err != nil {
return nil, err
}
go func() {
err := srv.Serve(tcpKeepAliveListener{listener.(*net.TCPListener)})
if err != nil {
log.Println("HTTP Server Error - ", err)
}
}()
srvCloser = listener
return srvCloser, nil
}
Full code available here.
The HTTP Server will close with the error
accept tcp [::]:8080: use of closed network connection