From 0922f0104b31d86af0265e0c27b95bc62845bef2 Mon Sep 17 00:00:00 2001 From: YEVHENII SHCHERBINA Date: Tue, 21 Oct 2025 20:11:19 +0000 Subject: [PATCH 1/5] feat: add pprof server --- proxy/proxy.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/proxy/proxy.go b/proxy/proxy.go index 10c6e59..0a8e09a 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -3,6 +3,7 @@ package proxy import ( "bufio" "bytes" + "context" "crypto/tls" "errors" "fmt" @@ -10,10 +11,12 @@ import ( "log/slog" "net" "net/http" + _ "net/http/pprof" "net/url" "strconv" "strings" "sync/atomic" + "time" "github.com/coder/boundary/audit" "github.com/coder/boundary/rules" @@ -29,6 +32,7 @@ type Server struct { started atomic.Bool listener net.Listener + pprofServer *http.Server } // Config holds configuration for the proxy server @@ -58,6 +62,23 @@ func (p *Server) Start() error { } p.logger.Info("Starting HTTP proxy with TLS termination", "port", p.httpPort) + + // Start pprof server on a different port + go func() { + pprofMux := http.NewServeMux() + pprofMux.HandleFunc("/debug/pprof/", http.DefaultServeMux.ServeHTTP) + + p.pprofServer = &http.Server{ + Addr: ":6060", // pprof port + Handler: pprofMux, + } + + p.logger.Info("Starting pprof server", "port", 6060) + if err := p.pprofServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + p.logger.Error("pprof server error", "error", err) + } + }() + var err error p.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", p.httpPort)) if err != nil { @@ -105,6 +126,15 @@ func (p *Server) Stop() error { return err } + // Close pprof server + if p.pprofServer != nil { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := p.pprofServer.Shutdown(ctx); err != nil { + p.logger.Error("Failed to shutdown pprof server", "error", err) + } + } + return nil } From e50d4f1089e9c74618e144b9060c0fbcc36429bb Mon Sep 17 00:00:00 2001 From: YEVHENII SHCHERBINA Date: Tue, 21 Oct 2025 20:20:53 +0000 Subject: [PATCH 2/5] refactor: minor refactor --- proxy/proxy.go | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 0a8e09a..75150b3 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -31,7 +31,7 @@ type Server struct { httpPort int started atomic.Bool - listener net.Listener + listener net.Listener pprofServer *http.Server } @@ -62,23 +62,20 @@ func (p *Server) Start() error { } p.logger.Info("Starting HTTP proxy with TLS termination", "port", p.httpPort) - + // Start pprof server on a different port go func() { - pprofMux := http.NewServeMux() - pprofMux.HandleFunc("/debug/pprof/", http.DefaultServeMux.ServeHTTP) - p.pprofServer = &http.Server{ - Addr: ":6060", // pprof port - Handler: pprofMux, + Addr: ":6060", // pprof port + Handler: http.DefaultServeMux, } - + p.logger.Info("Starting pprof server", "port", 6060) - if err := p.pprofServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + if err := p.pprofServer.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { p.logger.Error("pprof server error", "error", err) } }() - + var err error p.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", p.httpPort)) if err != nil { From 5363459534920c7d7c8c09b6b8a2a11de964b490 Mon Sep 17 00:00:00 2001 From: YEVHENII SHCHERBINA Date: Wed, 22 Oct 2025 13:27:16 +0000 Subject: [PATCH 3/5] fix: race detection --- proxy/proxy.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index 75150b3..a653eca 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -63,14 +63,15 @@ func (p *Server) Start() error { p.logger.Info("Starting HTTP proxy with TLS termination", "port", p.httpPort) + p.pprofServer = &http.Server{ + Addr: ":6060", // pprof port + Handler: http.DefaultServeMux, + } + // Start pprof server on a different port go func() { - p.pprofServer = &http.Server{ - Addr: ":6060", // pprof port - Handler: http.DefaultServeMux, - } - p.logger.Info("Starting pprof server", "port", 6060) + if err := p.pprofServer.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { p.logger.Error("pprof server error", "error", err) } From 4668b136a67a118e2f2a0d9264fb04f6b07abb47 Mon Sep 17 00:00:00 2001 From: YEVHENII SHCHERBINA Date: Wed, 22 Oct 2025 15:30:49 +0000 Subject: [PATCH 4/5] feat: parametrize pprof server --- boundary.go | 14 +++++++++----- cli/cli.go | 29 +++++++++++++++++++++++------ proxy/proxy.go | 41 ++++++++++++++++++++++++----------------- 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/boundary.go b/boundary.go index c60aa61..f8c4dee 100644 --- a/boundary.go +++ b/boundary.go @@ -21,6 +21,8 @@ type Config struct { Logger *slog.Logger Jailer jail.Jailer ProxyPort int + PprofEnabled bool + PprofPort int } type Boundary struct { @@ -35,11 +37,13 @@ type Boundary struct { func New(ctx context.Context, config Config) (*Boundary, error) { // Create proxy server proxyServer := proxy.NewProxyServer(proxy.Config{ - HTTPPort: config.ProxyPort, - RuleEngine: config.RuleEngine, - Auditor: config.Auditor, - Logger: config.Logger, - TLSConfig: config.TLSConfig, + HTTPPort: config.ProxyPort, + RuleEngine: config.RuleEngine, + Auditor: config.Auditor, + Logger: config.Logger, + TLSConfig: config.TLSConfig, + PprofEnabled: config.PprofEnabled, + PprofPort: config.PprofPort, }) // Create cancellable context for boundary diff --git a/cli/cli.go b/cli/cli.go index bfd6894..e5c4a74 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -29,6 +29,8 @@ type Config struct { LogDir string Unprivileged bool ProxyPort int64 + PprofEnabled bool + PprofPort int64 } // NewCommand creates and returns the root serpent command @@ -94,6 +96,19 @@ func BaseCommand() *serpent.Command { Default: "8080", Value: serpent.Int64Of(&config.ProxyPort), }, + { + Flag: "pprof", + Env: "BOUNDARY_PPROF", + Description: "Enable pprof profiling server.", + Value: serpent.BoolOf(&config.PprofEnabled), + }, + { + Flag: "pprof-port", + Env: "BOUNDARY_PPROF_PORT", + Description: "Set port for pprof profiling server.", + Default: "6060", + Value: serpent.Int64Of(&config.PprofPort), + }, }, Handler: func(inv *serpent.Invocation) error { args := inv.Args @@ -203,12 +218,14 @@ func Run(ctx context.Context, config Config, args []string) error { // Create boundary instance boundaryInstance, err := boundary.New(ctx, boundary.Config{ - RuleEngine: ruleEngine, - Auditor: auditor, - TLSConfig: tlsConfig, - Logger: logger, - Jailer: jailer, - ProxyPort: int(config.ProxyPort), + RuleEngine: ruleEngine, + Auditor: auditor, + TLSConfig: tlsConfig, + Logger: logger, + Jailer: jailer, + ProxyPort: int(config.ProxyPort), + PprofEnabled: config.PprofEnabled, + PprofPort: int(config.PprofPort), }) if err != nil { return fmt.Errorf("failed to create boundary instance: %v", err) diff --git a/proxy/proxy.go b/proxy/proxy.go index a653eca..ae8339e 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -33,15 +33,19 @@ type Server struct { listener net.Listener pprofServer *http.Server + pprofEnabled bool + pprofPort int } // Config holds configuration for the proxy server type Config struct { - HTTPPort int - RuleEngine rules.Evaluator - Auditor audit.Auditor - Logger *slog.Logger - TLSConfig *tls.Config + HTTPPort int + RuleEngine rules.Evaluator + Auditor audit.Auditor + Logger *slog.Logger + TLSConfig *tls.Config + PprofEnabled bool + PprofPort int } // NewProxyServer creates a new proxy server instance @@ -52,6 +56,8 @@ func NewProxyServer(config Config) *Server { logger: config.Logger, tlsConfig: config.TLSConfig, httpPort: config.HTTPPort, + pprofEnabled: config.PprofEnabled, + pprofPort: config.PprofPort, } } @@ -63,19 +69,20 @@ func (p *Server) Start() error { p.logger.Info("Starting HTTP proxy with TLS termination", "port", p.httpPort) - p.pprofServer = &http.Server{ - Addr: ":6060", // pprof port - Handler: http.DefaultServeMux, - } - - // Start pprof server on a different port - go func() { - p.logger.Info("Starting pprof server", "port", 6060) - - if err := p.pprofServer.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { - p.logger.Error("pprof server error", "error", err) + // Start pprof server if enabled + if p.pprofEnabled { + p.pprofServer = &http.Server{ + Addr: fmt.Sprintf(":%d", p.pprofPort), + Handler: http.DefaultServeMux, } - }() + + go func() { + p.logger.Info("Starting pprof server", "port", p.pprofPort) + if err := p.pprofServer.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { + p.logger.Error("pprof server error", "error", err) + } + }() + } var err error p.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", p.httpPort)) From b05df7cd8121db971ade7bfa462ffb821e2ac812 Mon Sep 17 00:00:00 2001 From: YEVHENII SHCHERBINA Date: Wed, 22 Oct 2025 17:29:07 +0000 Subject: [PATCH 5/5] refactor: minor refactoring --- proxy/proxy.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index ae8339e..709f3a9 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -31,8 +31,8 @@ type Server struct { httpPort int started atomic.Bool - listener net.Listener - pprofServer *http.Server + listener net.Listener + pprofServer *http.Server pprofEnabled bool pprofPort int } @@ -51,11 +51,11 @@ type Config struct { // NewProxyServer creates a new proxy server instance func NewProxyServer(config Config) *Server { return &Server{ - ruleEngine: config.RuleEngine, - auditor: config.Auditor, - logger: config.Logger, - tlsConfig: config.TLSConfig, - httpPort: config.HTTPPort, + ruleEngine: config.RuleEngine, + auditor: config.Auditor, + logger: config.Logger, + tlsConfig: config.TLSConfig, + httpPort: config.HTTPPort, pprofEnabled: config.PprofEnabled, pprofPort: config.PprofPort, } @@ -76,12 +76,19 @@ func (p *Server) Start() error { Handler: http.DefaultServeMux, } + ln, err := net.Listen("tcp", fmt.Sprintf(":%d", p.pprofPort)) + if err != nil { + p.logger.Error("failed to listen on port for pprof server", "port", p.pprofPort, "error", err) + return fmt.Errorf("failed to listen on port %v for pprof server: %v", p.pprofPort, err) + } + go func() { - p.logger.Info("Starting pprof server", "port", p.pprofPort) - if err := p.pprofServer.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) { + p.logger.Info("Serving pprof on existing listener", "port", p.pprofPort) + if err := p.pprofServer.Serve(ln); err != nil && errors.Is(err, http.ErrServerClosed) { p.logger.Error("pprof server error", "error", err) } }() + } var err error