package app import ( "context" "errors" "net" "net/http" "time" ) type ListenerFactory func(network, address string) (net.Listener, error) type Server struct { server *http.Server listen ListenerFactory } func NewServer(listenAddr string, handler http.Handler, listenerFactory ListenerFactory) *Server { if handler == nil { handler = NewAPIHandler("", ActionSet{}) } server := &Server{ server: &http.Server{ Addr: listenAddr, Handler: handler, }, listen: net.Listen, } if listenerFactory != nil { server.listen = listenerFactory } return server } func (s *Server) Addr() string { return s.server.Addr } func (s *Server) Run(ctx context.Context) error { listener, err := s.listen("tcp", s.server.Addr) if err != nil { return err } return s.Serve(ctx, listener) } func (s *Server) Serve(ctx context.Context, listener net.Listener) error { errCh := make(chan error, 1) go func() { err := s.server.Serve(listener) if errors.Is(err, http.ErrServerClosed) { err = nil } errCh <- err }() select { case <-ctx.Done(): shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := s.server.Shutdown(shutdownCtx); err != nil { return err } return <-errCh case err := <-errCh: return err } }