Add support for running profiling when debug build-tags provided (#491)

* wrap root HTTP handler in debug.WithPprof(), rearrange router.Start() to support this
* remove unused code
* set debug buildtag in build script when $DEBUG set
* update go-debug version with fixed handler
* use clone of router.srv for LE cert manager, reset server timeouts in debug
* add kim's other libraries to README
This commit is contained in:
kim
2022-04-28 13:32:53 +01:00
committed by GitHub
parent 420e2fb22b
commit 69011d4901
15 changed files with 248 additions and 37 deletions

View File

@ -24,6 +24,7 @@ import (
"net/http"
"time"
"codeberg.org/gruf/go-debug"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"github.com/spf13/viper"
@ -32,7 +33,7 @@ import (
"golang.org/x/crypto/acme/autocert"
)
var (
const (
readTimeout = 60 * time.Second
writeTimeout = 30 * time.Second
idleTimeout = 30 * time.Second
@ -69,38 +70,69 @@ func (r *router) AttachStaticFS(relativePath string, fs http.FileSystem) {
// Start starts the router nicely. It will serve two handlers if letsencrypt is enabled, and only the web/API handler if letsencrypt is not enabled.
func (r *router) Start() {
keys := config.Keys
leEnabled := viper.GetBool(keys.LetsEncryptEnabled)
var (
keys = config.Keys
if leEnabled {
bindAddress := viper.GetString(keys.BindAddress)
lePort := viper.GetInt(keys.LetsEncryptPort)
// listen is the server start function, by
// default pointing to regular HTTP listener,
// but updated to TLS if LetsEncrypt is enabled.
listen = r.srv.ListenAndServe
)
// serve the http handler on the selected letsencrypt port, for receiving letsencrypt requests and solving their devious riddles
if viper.GetBool(keys.LetsEncryptEnabled) {
// LetsEncrypt support is enabled
// Prepare an HTTPS-redirect handler for LetsEncrypt fallback
redirect := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
target := "https://" + r.Host + r.URL.Path
if len(r.URL.RawQuery) > 0 {
target += "?" + r.URL.RawQuery
}
http.Redirect(rw, r, target, http.StatusTemporaryRedirect)
})
// Clone HTTP server but with autocert handler
srv := r.srv
srv.Handler = r.certManager.HTTPHandler(redirect)
// Start the LetsEncrypt autocert manager HTTP server.
go func() {
listen := fmt.Sprintf("%s:%d", bindAddress, lePort)
logrus.Infof("letsencrypt listening on %s", listen)
if err := http.ListenAndServe(listen, r.certManager.HTTPHandler(http.HandlerFunc(httpsRedirect))); err != nil && err != http.ErrServerClosed {
addr := fmt.Sprintf("%s:%d",
viper.GetString(keys.BindAddress),
viper.GetInt(keys.LetsEncryptPort),
)
logrus.Infof("letsencrypt listening on %s", addr)
if err := srv.ListenAndServe(); err != nil &&
err != http.ErrServerClosed {
logrus.Fatalf("letsencrypt: listen: %s", err)
}
}()
// and serve the actual TLS handler
go func() {
logrus.Infof("listening on %s", r.srv.Addr)
if err := r.srv.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
logrus.Fatalf("listen: %s", err)
}
}()
} else {
// no tls required
go func() {
logrus.Infof("listening on %s", r.srv.Addr)
if err := r.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logrus.Fatalf("listen: %s", err)
}
}()
// TLS is enabled, update the listen function
listen = func() error { return r.srv.ListenAndServeTLS("", "") }
}
// Pass the server handler through a debug pprof middleware handler.
// For standard production builds this will be a no-op, but when the
// "debug" or "debugenv" build-tag is set pprof stats will be served
// at the standard "/debug/pprof" URL.
r.srv.Handler = debug.WithPprof(r.srv.Handler)
if debug.DEBUG() {
// Profiling requires timeouts longer than 30s, so reset these.
logrus.Warn("resetting http.Server{} timeout to support profiling")
r.srv.ReadTimeout = 0
r.srv.WriteTimeout = 0
}
// Start the main listener.
go func() {
logrus.Infof("listening on %s", r.srv.Addr)
if err := listen(); err != nil && err != http.ErrServerClosed {
logrus.Fatalf("listen: %s", err)
}
}()
}
// Stop shuts down the router nicely
@ -193,13 +225,3 @@ func New(ctx context.Context, db db.DB) (Router, error) {
certManager: m,
}, nil
}
func httpsRedirect(w http.ResponseWriter, req *http.Request) {
target := "https://" + req.Host + req.URL.Path
if len(req.URL.RawQuery) > 0 {
target += "?" + req.URL.RawQuery
}
http.Redirect(w, req, target, http.StatusTemporaryRedirect)
}