// Package httputil contains additional functionality for working with http. package httputil import ( "context" "errors" "net" "net/http" "time" ) // ServeWithGracefulStop serves the HTTP listener until ctx.Done(), and then gracefully stops and waits for gracefulTimeout // before definitively stopping. func ServeWithGracefulStop(ctx context.Context, handler http.Handler, li net.Listener, gracefulTimeout time.Duration) error { // create a context that will be used for the http requests // it will only be cancelled when baseCancel is called but will // preserve the values from ctx baseCtx, baseCancel := context.WithCancelCause(context.WithoutCancel(ctx)) srv := http.Server{ Handler: handler, BaseContext: func(_ net.Listener) context.Context { return baseCtx }, } go func() { <-ctx.Done() // create a context that will cancel after the graceful timeout timeoutCtx, clearTimeout := context.WithTimeout(context.Background(), gracefulTimeout) defer clearTimeout() // shut the http server down _ = srv.Shutdown(timeoutCtx) // cancel the base context used for http requests baseCancel(ctx.Err()) }() err := srv.Serve(li) if errors.Is(err, http.ErrServerClosed) { err = nil } return err }