diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-27 17:11:58 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-27 17:11:58 +0200 |
| commit | 25ecf971f695e80ab41d08ab204dcc5bf1214147 (patch) | |
| tree | cdffb8af4423934f0f2203788b0132db752234bc /internal/flamegraph/liveserver.go | |
| parent | 5f3d3c71650c23b66b347e748263c9759d3be2b5 (diff) | |
flamegraph: add live HTTP server entrypoint
Diffstat (limited to 'internal/flamegraph/liveserver.go')
| -rw-r--r-- | internal/flamegraph/liveserver.go | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/internal/flamegraph/liveserver.go b/internal/flamegraph/liveserver.go index d7a48cf..6b964a1 100644 --- a/internal/flamegraph/liveserver.go +++ b/internal/flamegraph/liveserver.go @@ -1,11 +1,62 @@ package flamegraph import ( + "context" "fmt" "net/http" "time" ) +// ServeLive starts the live flamegraph HTTP server and blocks until ctx is canceled. +func ServeLive(ctx context.Context, lt *LiveTrie, interval time.Duration) error { + mux := http.NewServeMux() + mux.HandleFunc("/", handleLivePage()) + mux.HandleFunc("/events", handleSSE(lt, interval)) + srv := &http.Server{Handler: mux} + + listener, err := listenRandomPort() + if err != nil { + return err + } + defer listener.Close() + + hostname, port := serverHostPort(listener) + fmt.Printf("Live flamegraph available at http://%s:%d/\n", hostname, port) + + errCh := make(chan error, 1) + go func() { + errCh <- srv.Serve(listener) + }() + + select { + case <-ctx.Done(): + case serveErr := <-errCh: + if serveErr != nil && serveErr != http.ErrServerClosed { + return serveErr + } + return nil + } + + shutdownCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + if err := srv.Shutdown(shutdownCtx); err != nil { + return fmt.Errorf("shutdown live web server: %w", err) + } + + serveErr := <-errCh + if serveErr != nil && serveErr != http.ErrServerClosed { + return serveErr + } + return nil +} + +func handleLivePage() http.HandlerFunc { + return func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + _, _ = w.Write([]byte(liveHTML)) + } +} + func handleSSE(lt *LiveTrie, interval time.Duration) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { flusher, ok := w.(http.Flusher) |
