diff --git a/cmd/stackwhere/web.go b/cmd/stackwhere/web.go index e2cb2d5..adeb0c1 100644 --- a/cmd/stackwhere/web.go +++ b/cmd/stackwhere/web.go @@ -63,6 +63,14 @@ func (wc *webCmd) runE(cmd *cobra.Command, args []string) error { return err } + listener, err := net.Listen("tcp", *wc.flagAddr) + if err != nil { + return fmt.Errorf("web server failed: %w", err) + } + defer func() { + _ = listener.Close() + }() + if _, err := fmt.Fprintf(cmd.OutOrStdout(), "Serving stackwhere web UI on %s\n", startupURL(*wc.flagAddr)); err != nil { return err } @@ -72,7 +80,7 @@ func (wc *webCmd) runE(cmd *cobra.Command, args []string) error { Handler: app.handler(), } - if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { + if err := server.Serve(listener); err != nil && err != http.ErrServerClosed { return fmt.Errorf("web server failed: %w", err) } return nil diff --git a/cmd/stackwhere/web_test.go b/cmd/stackwhere/web_test.go index f1c2aec..5bd5fff 100644 --- a/cmd/stackwhere/web_test.go +++ b/cmd/stackwhere/web_test.go @@ -2,6 +2,7 @@ package main import ( "bytes" + "net" "net/http/httptest" "os" "path/filepath" @@ -55,6 +56,29 @@ func TestWebCommandRequiresCollectionArg(t *testing.T) { } } +func TestWebCommandDoesNotAnnounceFailedBind(t *testing.T) { + listener, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("failed to reserve test address: %v", err) + } + defer func() { + _ = listener.Close() + }() + + cmd := root() + cmd.SetArgs([]string{"web", "../../testdata/basic.o", "--addr", listener.Addr().String()}) + + var stdout bytes.Buffer + cmd.SetOut(&stdout) + + if err := cmd.Execute(); err == nil { + t.Fatal("expected web command to fail when address is already in use") + } + if got := stdout.String(); strings.Contains(got, "Serving stackwhere web UI") { + t.Fatalf("web command announced a server that failed to start: %q", got) + } +} + func TestWebHandlerServesLandingPage(t *testing.T) { app, err := newWebApp("../../testdata/basic.o", nil) if err != nil {